diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..808e0628 --- /dev/null +++ b/404.html @@ -0,0 +1,775 @@ + + + + + + + + + + + + + + + + + + + MLPerf Inference Unofficial Results (only meant for testing workflow) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/CONTRIBUTING/index.html b/CONTRIBUTING/index.html new file mode 100644 index 00000000..f13d3721 --- /dev/null +++ b/CONTRIBUTING/index.html @@ -0,0 +1,821 @@ + + + + + + + + + + + + + + + + + + + CONTRIBUTING - MLPerf Inference Unofficial Results (only meant for testing workflow) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + Skip to content + + +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

CONTRIBUTING

+ +

Contributing

+

The best way to contribute to the MLCommons is to get involved with one of our many project communities. You can find more information about getting involved with MLCommons here.

+

Generally we encourage people to become MLCommons members if they wish to contribute to MLCommons projects, but outside pull requests are very welcome too.

+

Regardless of whether you are a member, your organization (or you as an individual contributor) needs to sign the MLCommons Contributor License Agreement (CLA). Please submit your GitHub username to the MLCommons Subscription form to start that process.

+

MLCommons project work is tracked with issue trackers and pull requests. Modify the project in your own fork and issue a pull request once you want other developers to take a look at what you have done and discuss the proposed changes. Ensure that cla-bot and other checks pass for your pull requests.

+ + + + + + + + + + + + + +
+
+ + + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/LICENSE/index.html b/LICENSE/index.html new file mode 100644 index 00000000..2fe30bd4 --- /dev/null +++ b/LICENSE/index.html @@ -0,0 +1,965 @@ + + + + + + + + + + + + + + + + + + + LICENSE - MLPerf Inference Unofficial Results (only meant for testing workflow) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + + + + + + +
+ +
+ + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + +

LICENSE

+ +
                             Apache License
+                       Version 2.0, January 2004
+                    http://www.apache.org/licenses/
+
+

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

+
    +
  1. +

    Definitions.

    +

    "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document.

    +

    "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License.

    +

    "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity.

    +

    "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License.

    +

    "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files.

    +

    "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types.

    +

    "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below).

    +

    "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof.

    +

    "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution."

    +

    "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work.

    +
  2. +
  3. +

    Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form.

    +
  4. +
  5. +

    Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed.

    +
  6. +
  7. +

    Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions:

    +

    (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and

    +

    (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and

    +

    (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and

    +

    (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License.

    +

    You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License.

    +
  8. +
  9. +

    Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions.

    +
  10. +
  11. +

    Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file.

    +
  12. +
  13. +

    Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License.

    +
  14. +
  15. +

    Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages.

    +
  16. +
  17. +

    Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability.

    +
  18. +
+

END OF TERMS AND CONDITIONS

+ + + + + + + + + + + + + +
+
+ + + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + + \ No newline at end of file diff --git a/add_results_summary.py b/add_results_summary.py new file mode 100644 index 00000000..693770e7 --- /dev/null +++ b/add_results_summary.py @@ -0,0 +1,315 @@ +import json +import os +import time +import sys +sys.path.insert(0, os.path.join("inference", "tools", "submission")) +import submission_checker as checker # noqa + +with open('summary_results.json') as f: + data = json.load(f) +#print(models_all) +#print(platforms) + +def get_header(): + css = """ + /* General table styles */ +table { + width: 100%; + border-collapse: collapse; + margin: 20px 0; + border: none; +} + +table.titlebarcontainer,.titlebar { + text-align: center !important; +} +td.headerbar { + text-align: center; + font-size: x-large; +} +th, td { + padding: 8px; + text-align: left; + vertical-align: top; +} + +/* Styling for the first table to make it stand out */ +table:first-of-type { + background-color: #e8e8e8; /* Light grey background */ + color: #333; /* Darker text for contrast */ + font-weight: bold; /* Makes text bold */ +} + +/* Styling inner tables to have borders */ +table table { + border: 1px solid #ccc; +} + +/* More specific styling for headers and rows of inner tables */ +table table th, table table td { + border: 1px solid #ccc; +} + +th { + background-color: #f4f4f4; +} + +tr:nth-child(even) { + background-color: #f9f9f9; +} + +tr:nth-child(odd) { + background-color: #ffffff; +} + +/* Adjust column widths if necessary */ +th:nth-child(1), td:nth-child(1) { width: 20%; } +th:nth-child(2), td:nth-child(2) { width: 30%; } + """ + html_header = f""" + + + +""" + return html_header + +def get_header_table(system_json): + submitter = system_json.get('submitter') + system_name = system_json.get('system_name') + html = f""" +
+
+ +
+

MLPerf Inference v4.1

+

Copyright 2019-2024 MLCommons

+
+
+ + + + +
+

{submitter}

+

{system_name}

+
+ + + + + + + + + + + + + + + +
MLPerf Inference Category:DatacenterMLPerf Inference Division:Closed
Submitted by:{submitter}Availability:Available as of Aug 2024
+ """ + return html + + +def get_system_json(path): + #import requests + # Send a GET request to the URL + #response = requests.get(url) + + with open(path, "r") as f: + data = json.load(f) + # Check if the request was successful + #if response.status_code == 200: + # # Parse the JSON content + # data = response.json() + + return data + +def get_accelerator_details_table(system_json): + table = '

Accelerator Details

' + for key,value in system_json.items(): + if not key.startswith("accelerator"): + continue + table += f"""""" + + table += "
{key}{value}
" + return table + +def get_cpu_details_table(system_json): + table = '

Processor and Memory Details

' + hardware_fields = [ "processor", "cpu", "memory" ] + for key,value in system_json.items(): + if any (a in key for a in hardware_fields) and "accelerator" not in key: + table += f"""""" + + table += "
{key}{value}
" + return table +def get_network_details_table(system_json): + table = '

Network and Interconnect Details

' + hardware_fields = [ "network", "nics" ] + for key,value in system_json.items(): + if any (a in key for a in hardware_fields) and "accelerator" not in key: + table += f"""""" + + table += "
{key}{value}
" + return table +def get_hardware_details_table(system_json): + table = '

Other Hardware Details

' + hardware_fields = [ "hardware", "disk", "cooling", "power", "hw_" ] + for key,value in system_json.items(): + if any (a in key for a in hardware_fields) and "accelerator" not in key: + table += f"""""" + + table += "
{key}{value}
" + return table + +def get_software_details_table(system_json): + table = '

Software Details

' + software_fields = [ "software", "framework", "firmware", "sw_", "operating_" ] + for key,value in system_json.items(): + if any (a in key for a in software_fields): + table += f"""""" + + table += "
{key}{value}
" + return table + +# Initialize a dictionary to organize the data by 'Details' +tables = {} + +# Populate the dictionary with data +for entry in data: + details = entry['Details'] + if details not in tables: + tables[details] = {} + categories = [ "edge", "datacenter" ] + for category in categories: + if category not in entry['Suite']: + continue + if category not in tables[details]: + tables[details][category] = {} + if entry['Category'] not in tables[details][category]: + tables[details][category][entry['Category']] = {} + + if entry['Model'] not in tables[details][category][entry['Category']]: + tables[details][category][entry['Category']] [entry['Model']] = {} + if entry['Scenario'] not in tables[details][category][entry['Category']][entry['Model']]: + tables[details][category][entry['Category']][entry['Model']][entry['Scenario']] = entry + +# Now you can format each group in 'tables' as a markdown table +for details, entries in tables.items(): + out = f"## {details}" + + models_edge = [ "gptj-99", "gptj-99.9", "bert-99", "bert-99.9", "stable-diffusion-xl", "retinanet", "resnet", "3d-unet-99", "3d-unet-99.9" ] + if "datacenter" in entries: + models = [ "llama2-70b-99", "llama2-70b-99.9", "gptj-99", "gptj-99.9", "bert-99", "bert-99.9", "stable-diffusion-xl", "dlrm-v2-99", "dlrm-v2-99.9", "retinanet", "resnet", "3d-unet-99", "3d-unet-99.9" ] + + html_table_head = f""" +

Results Table

+ + + + + + + + + + + + + + """ + for category in entries: + for division, data in entries[category].items(): + html_table = html_table_head + hardware_details = '' + for model in models: + if model in data: + html_table += f"""""" + if "closed" in division: + version = data[model]["Offline"]["version"] + acc_target = checker.MODEL_CONFIG[version]["accuracy-target"][model] + i = 0 + acc_targets = [] + key = None + for item in acc_target: + if i%2 == 0: + key = item + else: + acc_targets.append( (key, item)) + i+=1 + else: + acc_targets = [] + acc_targets_list = [] + for item in acc_targets: + acc_targets_list.append(f"""{item[0]}: {round(item[1], 4)}""") + acc_targets_string = ", ".join(acc_targets_list) + html_table += f"""""" + if "Server" in data[model]: + html_table += f"""""" + else: + html_table += "" + if "Offline" in data[model]: + details_split = details.split("/") + details_split[9] = "systems" + system = os.path.sep.join(details_split[7:11]) + #details_split[0] = "https://raw.githubusercontent.com" + #system = details.replace("github.com", "raw.githubusercontent.com").replace("tree/", "refs/heads/").replace("results/", "systems/") + system_json_path = f"""{system}.json""" + system_json = get_system_json(system_json_path) + header_table = get_header_table(system_json) + accelerator_details = get_accelerator_details_table(system_json) + cpu_details = get_cpu_details_table(system_json) + hardware_details = get_hardware_details_table(system_json) + software_details = get_software_details_table(system_json) + network_details = get_network_details_table(system_json) + html_table += f"""""" + else: + html_table += "" + else: + pass + #html_table += "" + #html_table += "" + #html_table += "" + html_table += "
ModelAccuracy TargetServerOffline
MetricPerformanceMetricPerformance
{model}{acc_targets_string}{data[model]["Server"]["Performance_Units"]} {data[model]["Server"]["Performance_Result"]}{data[model]["Offline"]['Performance_Units']} {data[model]["Offline"]["Performance_Result"]}
" + sut_name = os.path.basename(details) + tmp_path = os.path.dirname(details) + tmp_path = os.path.dirname(tmp_path) + submitter = os.path.basename(tmp_path) + out_path = os.path.join(division, submitter, "results", sut_name, "summary", "README.md") + os.makedirs(os.path.dirname(out_path), exist_ok=True) + + html_table = f""" +{header_table} + + + + +
{accelerator_details} {cpu_details}
{hardware_details} {network_details}
{software_details}
+{html_table} +""" + with open(out_path, "w") as f: + f.write(html_table) + html_out_path = os.path.join(division, submitter, "results", sut_name, "summary", "summary.html") + html_header = get_header() + html = f""" + +{html_header} + +{html_table} + + + """ + with open(html_out_path, "w") as f: + f.write(html) + print(html_table) + #sys.exit() + + + diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.88dd0f4e.min.js b/assets/javascripts/bundle.88dd0f4e.min.js new file mode 100644 index 00000000..fb8f3109 --- /dev/null +++ b/assets/javascripts/bundle.88dd0f4e.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Wi=Object.create;var gr=Object.defineProperty;var Di=Object.getOwnPropertyDescriptor;var Vi=Object.getOwnPropertyNames,Vt=Object.getOwnPropertySymbols,Ni=Object.getPrototypeOf,yr=Object.prototype.hasOwnProperty,ao=Object.prototype.propertyIsEnumerable;var io=(e,t,r)=>t in e?gr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,$=(e,t)=>{for(var r in t||(t={}))yr.call(t,r)&&io(e,r,t[r]);if(Vt)for(var r of Vt(t))ao.call(t,r)&&io(e,r,t[r]);return e};var so=(e,t)=>{var r={};for(var o in e)yr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Vt)for(var o of Vt(e))t.indexOf(o)<0&&ao.call(e,o)&&(r[o]=e[o]);return r};var xr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var zi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Vi(t))!yr.call(e,n)&&n!==r&&gr(e,n,{get:()=>t[n],enumerable:!(o=Di(t,n))||o.enumerable});return e};var Mt=(e,t,r)=>(r=e!=null?Wi(Ni(e)):{},zi(t||!e||!e.__esModule?gr(r,"default",{value:e,enumerable:!0}):r,e));var co=(e,t,r)=>new Promise((o,n)=>{var i=p=>{try{s(r.next(p))}catch(c){n(c)}},a=p=>{try{s(r.throw(p))}catch(c){n(c)}},s=p=>p.done?o(p.value):Promise.resolve(p.value).then(i,a);s((r=r.apply(e,t)).next())});var lo=xr((Er,po)=>{(function(e,t){typeof Er=="object"&&typeof po!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(Er,function(){"use strict";function e(r){var o=!0,n=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function p(k){var ft=k.type,qe=k.tagName;return!!(qe==="INPUT"&&a[ft]&&!k.readOnly||qe==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function c(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(s(r.activeElement)&&c(r.activeElement),o=!0)}function u(k){o=!1}function d(k){s(k.target)&&(o||p(k.target))&&c(k.target)}function y(k){s(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function L(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",J),document.addEventListener("mousedown",J),document.addEventListener("mouseup",J),document.addEventListener("pointermove",J),document.addEventListener("pointerdown",J),document.addEventListener("pointerup",J),document.addEventListener("touchmove",J),document.addEventListener("touchstart",J),document.addEventListener("touchend",J)}function te(){document.removeEventListener("mousemove",J),document.removeEventListener("mousedown",J),document.removeEventListener("mouseup",J),document.removeEventListener("pointermove",J),document.removeEventListener("pointerdown",J),document.removeEventListener("pointerup",J),document.removeEventListener("touchmove",J),document.removeEventListener("touchstart",J),document.removeEventListener("touchend",J)}function J(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,te())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",L,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",y,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var qr=xr((hy,On)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var $a=/["'&<>]/;On.exports=Pa;function Pa(e){var t=""+e,r=$a.exec(t);if(!r)return t;var o,n="",i=0,a=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof It=="object"&&typeof Yr=="object"?Yr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof It=="object"?It.ClipboardJS=r():t.ClipboardJS=r()})(It,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Ui}});var a=i(279),s=i.n(a),p=i(370),c=i.n(p),l=i(817),f=i.n(l);function u(V){try{return document.execCommand(V)}catch(A){return!1}}var d=function(A){var M=f()(A);return u("cut"),M},y=d;function L(V){var A=document.documentElement.getAttribute("dir")==="rtl",M=document.createElement("textarea");M.style.fontSize="12pt",M.style.border="0",M.style.padding="0",M.style.margin="0",M.style.position="absolute",M.style[A?"right":"left"]="-9999px";var F=window.pageYOffset||document.documentElement.scrollTop;return M.style.top="".concat(F,"px"),M.setAttribute("readonly",""),M.value=V,M}var X=function(A,M){var F=L(A);M.container.appendChild(F);var D=f()(F);return u("copy"),F.remove(),D},te=function(A){var M=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},F="";return typeof A=="string"?F=X(A,M):A instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(A==null?void 0:A.type)?F=X(A.value,M):(F=f()(A),u("copy")),F},J=te;function k(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(M){return typeof M}:k=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},k(V)}var ft=function(){var A=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},M=A.action,F=M===void 0?"copy":M,D=A.container,Y=A.target,$e=A.text;if(F!=="copy"&&F!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Y!==void 0)if(Y&&k(Y)==="object"&&Y.nodeType===1){if(F==="copy"&&Y.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(F==="cut"&&(Y.hasAttribute("readonly")||Y.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if($e)return J($e,{container:D});if(Y)return F==="cut"?y(Y):J(Y,{container:D})},qe=ft;function Fe(V){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Fe=function(M){return typeof M}:Fe=function(M){return M&&typeof Symbol=="function"&&M.constructor===Symbol&&M!==Symbol.prototype?"symbol":typeof M},Fe(V)}function ki(V,A){if(!(V instanceof A))throw new TypeError("Cannot call a class as a function")}function no(V,A){for(var M=0;M0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof D.action=="function"?D.action:this.defaultAction,this.target=typeof D.target=="function"?D.target:this.defaultTarget,this.text=typeof D.text=="function"?D.text:this.defaultText,this.container=Fe(D.container)==="object"?D.container:document.body}},{key:"listenClick",value:function(D){var Y=this;this.listener=c()(D,"click",function($e){return Y.onClick($e)})}},{key:"onClick",value:function(D){var Y=D.delegateTarget||D.currentTarget,$e=this.action(Y)||"copy",Dt=qe({action:$e,container:this.container,target:this.target(Y),text:this.text(Y)});this.emit(Dt?"success":"error",{action:$e,text:Dt,trigger:Y,clearSelection:function(){Y&&Y.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(D){return vr("action",D)}},{key:"defaultTarget",value:function(D){var Y=vr("target",D);if(Y)return document.querySelector(Y)}},{key:"defaultText",value:function(D){return vr("text",D)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(D){var Y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return J(D,Y)}},{key:"cut",value:function(D){return y(D)}},{key:"isSupported",value:function(){var D=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Y=typeof D=="string"?[D]:D,$e=!!document.queryCommandSupported;return Y.forEach(function(Dt){$e=$e&&!!document.queryCommandSupported(Dt)}),$e}}]),M}(s()),Ui=Fi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,p){for(;s&&s.nodeType!==n;){if(typeof s.matches=="function"&&s.matches(p))return s;s=s.parentNode}}o.exports=a},438:function(o,n,i){var a=i(828);function s(l,f,u,d,y){var L=c.apply(this,arguments);return l.addEventListener(u,L,y),{destroy:function(){l.removeEventListener(u,L,y)}}}function p(l,f,u,d,y){return typeof l.addEventListener=="function"?s.apply(null,arguments):typeof u=="function"?s.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(L){return s(L,f,u,d,y)}))}function c(l,f,u,d){return function(y){y.delegateTarget=a(y.target,f),y.delegateTarget&&d.call(l,y)}}o.exports=p},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(o,n,i){var a=i(879),s=i(438);function p(u,d,y){if(!u&&!d&&!y)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(y))throw new TypeError("Third argument must be a Function");if(a.node(u))return c(u,d,y);if(a.nodeList(u))return l(u,d,y);if(a.string(u))return f(u,d,y);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(u,d,y){return u.addEventListener(d,y),{destroy:function(){u.removeEventListener(d,y)}}}function l(u,d,y){return Array.prototype.forEach.call(u,function(L){L.addEventListener(d,y)}),{destroy:function(){Array.prototype.forEach.call(u,function(L){L.removeEventListener(d,y)})}}}function f(u,d,y){return s(document.body,u,d,y)}o.exports=p},817:function(o){function n(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var p=window.getSelection(),c=document.createRange();c.selectNodeContents(i),p.removeAllRanges(),p.addRange(c),a=p.toString()}return a}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,a,s){var p=this.e||(this.e={});return(p[i]||(p[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var p=this;function c(){p.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),p=0,c=s.length;for(p;p0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function N(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],a;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(s){a={error:s}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(a)throw a.error}}return i}function q(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||p(d,L)})},y&&(n[d]=y(n[d])))}function p(d,y){try{c(o[d](y))}catch(L){u(i[0][3],L)}}function c(d){d.value instanceof nt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){p("next",d)}function f(d){p("throw",d)}function u(d,y){d(y),i.shift(),i.length&&p(i[0][0],i[0][1])}}function uo(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof he=="function"?he(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(a){return new Promise(function(s,p){a=e[i](a),n(s,p,a.done,a.value)})}}function n(i,a,s,p){Promise.resolve(p).then(function(c){i({value:c,done:s})},a)}}function H(e){return typeof e=="function"}function ut(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var zt=ut(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Qe(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ue=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=he(a),p=s.next();!p.done;p=s.next()){var c=p.value;c.remove(this)}}catch(L){t={error:L}}finally{try{p&&!p.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var l=this.initialTeardown;if(H(l))try{l()}catch(L){i=L instanceof zt?L.errors:[L]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=he(f),d=u.next();!d.done;d=u.next()){var y=d.value;try{ho(y)}catch(L){i=i!=null?i:[],L instanceof zt?i=q(q([],N(i)),N(L.errors)):i.push(L)}}}catch(L){o={error:L}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new zt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ho(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Qe(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Qe(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Tr=Ue.EMPTY;function qt(e){return e instanceof Ue||e&&"closed"in e&&H(e.remove)&&H(e.add)&&H(e.unsubscribe)}function ho(e){H(e)?e():e.unsubscribe()}var Pe={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var dt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,a=n.isStopped,s=n.observers;return i||a?Tr:(this.currentObservers=null,s.push(r),new Ue(function(){o.currentObservers=null,Qe(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,a=o.isStopped;n?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new j;return r.source=this,r},t.create=function(r,o){return new To(r,o)},t}(j);var To=function(e){oe(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:Tr},t}(g);var _r=function(e){oe(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t}(g);var At={now:function(){return(At.delegate||Date).now()},delegate:void 0};var Ct=function(e){oe(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=At);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,a=o._infiniteTimeWindow,s=o._timestampProvider,p=o._windowTime;n||(i.push(r),!a&&i.push(s.now()+p)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,a=n._buffer,s=a.slice(),p=0;p0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t}(gt);var Lo=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t}(yt);var kr=new Lo(Oo);var Mo=function(e){oe(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=vt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var a=r.actions;o!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==o&&(vt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(gt);var _o=function(e){oe(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(yt);var me=new _o(Mo);var S=new j(function(e){return e.complete()});function Yt(e){return e&&H(e.schedule)}function Hr(e){return e[e.length-1]}function Xe(e){return H(Hr(e))?e.pop():void 0}function ke(e){return Yt(Hr(e))?e.pop():void 0}function Bt(e,t){return typeof Hr(e)=="number"?e.pop():t}var xt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Gt(e){return H(e==null?void 0:e.then)}function Jt(e){return H(e[bt])}function Xt(e){return Symbol.asyncIterator&&H(e==null?void 0:e[Symbol.asyncIterator])}function Zt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Zi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var er=Zi();function tr(e){return H(e==null?void 0:e[er])}function rr(e){return fo(this,arguments,function(){var r,o,n,i;return Nt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,nt(r.read())];case 3:return o=a.sent(),n=o.value,i=o.done,i?[4,nt(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,nt(n)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function or(e){return H(e==null?void 0:e.getReader)}function U(e){if(e instanceof j)return e;if(e!=null){if(Jt(e))return ea(e);if(xt(e))return ta(e);if(Gt(e))return ra(e);if(Xt(e))return Ao(e);if(tr(e))return oa(e);if(or(e))return na(e)}throw Zt(e)}function ea(e){return new j(function(t){var r=e[bt]();if(H(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function ta(e){return new j(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?b(function(n,i){return e(n,i,o)}):le,Te(1),r?De(t):Qo(function(){return new ir}))}}function jr(e){return e<=0?function(){return S}:E(function(t,r){var o=[];t.subscribe(T(r,function(n){o.push(n),e=2,!0))}function pe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new g}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,p=s===void 0?!0:s;return function(c){var l,f,u,d=0,y=!1,L=!1,X=function(){f==null||f.unsubscribe(),f=void 0},te=function(){X(),l=u=void 0,y=L=!1},J=function(){var k=l;te(),k==null||k.unsubscribe()};return E(function(k,ft){d++,!L&&!y&&X();var qe=u=u!=null?u:r();ft.add(function(){d--,d===0&&!L&&!y&&(f=Ur(J,p))}),qe.subscribe(ft),!l&&d>0&&(l=new at({next:function(Fe){return qe.next(Fe)},error:function(Fe){L=!0,X(),f=Ur(te,n,Fe),qe.error(Fe)},complete:function(){y=!0,X(),f=Ur(te,a),qe.complete()}}),U(k).subscribe(l))})(c)}}function Ur(e,t){for(var r=[],o=2;oe.next(document)),e}function P(e,t=document){return Array.from(t.querySelectorAll(e))}function R(e,t=document){let r=fe(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function fe(e,t=document){return t.querySelector(e)||void 0}function Ie(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var wa=O(h(document.body,"focusin"),h(document.body,"focusout")).pipe(_e(1),Q(void 0),m(()=>Ie()||document.body),G(1));function et(e){return wa.pipe(m(t=>e.contains(t)),K())}function $t(e,t){return C(()=>O(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?Ht(r=>Le(+!r*t)):le,Q(e.matches(":hover"))))}function Jo(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Jo(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Jo(o,n);return o}function sr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function Tt(e){let t=x("script",{src:e});return C(()=>(document.head.appendChild(t),O(h(t,"load"),h(t,"error").pipe(v(()=>$r(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),_(()=>document.head.removeChild(t)),Te(1))))}var Xo=new g,Ta=C(()=>typeof ResizeObserver=="undefined"?Tt("https://unpkg.com/resize-observer-polyfill"):I(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>Xo.next(t)))),v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function ce(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ta.pipe(w(r=>r.observe(t)),v(r=>Xo.pipe(b(o=>o.target===t),_(()=>r.unobserve(t)))),m(()=>ce(e)),Q(ce(e)))}function St(e){return{width:e.scrollWidth,height:e.scrollHeight}}function cr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function Zo(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Ve(e){return{x:e.offsetLeft,y:e.offsetTop}}function en(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function tn(e){return O(h(window,"load"),h(window,"resize")).pipe(Me(0,me),m(()=>Ve(e)),Q(Ve(e)))}function pr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ne(e){return O(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe(Me(0,me),m(()=>pr(e)),Q(pr(e)))}var rn=new g,Sa=C(()=>I(new IntersectionObserver(e=>{for(let t of e)rn.next(t)},{threshold:0}))).pipe(v(e=>O(Ye,I(e)).pipe(_(()=>e.disconnect()))),G(1));function tt(e){return Sa.pipe(w(t=>t.observe(e)),v(t=>rn.pipe(b(({target:r})=>r===e),_(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function on(e,t=16){return Ne(e).pipe(m(({y:r})=>{let o=ce(e),n=St(e);return r>=n.height-o.height-t}),K())}var lr={drawer:R("[data-md-toggle=drawer]"),search:R("[data-md-toggle=search]")};function nn(e){return lr[e].checked}function Je(e,t){lr[e].checked!==t&&lr[e].click()}function ze(e){let t=lr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function Oa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function La(){return O(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function an(){let e=h(window,"keydown").pipe(b(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:nn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),b(({mode:t,type:r})=>{if(t==="global"){let o=Ie();if(typeof o!="undefined")return!Oa(o,r)}return!0}),pe());return La().pipe(v(t=>t?S:e))}function ye(){return new URL(location.href)}function lt(e,t=!1){if(B("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function sn(){return new g}function cn(){return location.hash.slice(1)}function pn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Ma(e){return O(h(window,"hashchange"),e).pipe(m(cn),Q(cn()),b(t=>t.length>0),G(1))}function ln(e){return Ma(e).pipe(m(t=>fe(`[id="${t}"]`)),b(t=>typeof t!="undefined"))}function Pt(e){let t=matchMedia(e);return ar(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function mn(){let e=matchMedia("print");return O(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function Nr(e,t){return e.pipe(v(r=>r?t():S))}function zr(e,t){return new j(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let a=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+a*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function je(e,t){return zr(e,t).pipe(v(r=>r.text()),m(r=>JSON.parse(r)),G(1))}function fn(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),G(1))}function un(e,t){let r=new DOMParser;return zr(e,t).pipe(v(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),G(1))}function dn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function hn(){return O(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(dn),Q(dn()))}function bn(){return{width:innerWidth,height:innerHeight}}function vn(){return h(window,"resize",{passive:!0}).pipe(m(bn),Q(bn()))}function gn(){return z([hn(),vn()]).pipe(m(([e,t])=>({offset:e,size:t})),G(1))}function mr(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=z([o,r]).pipe(m(()=>Ve(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:a,size:s},{x:p,y:c}])=>({offset:{x:a.x-p,y:a.y-c+i},size:s})))}function _a(e){return h(e,"message",t=>t.data)}function Aa(e){let t=new g;return t.subscribe(r=>e.postMessage(r)),t}function yn(e,t=new Worker(e)){let r=_a(t),o=Aa(t),n=new g;n.subscribe(o);let i=o.pipe(Z(),ie(!0));return n.pipe(Z(),Re(r.pipe(W(i))),pe())}var Ca=R("#__config"),Ot=JSON.parse(Ca.textContent);Ot.base=`${new URL(Ot.base,ye())}`;function xe(){return Ot}function B(e){return Ot.features.includes(e)}function Ee(e,t){return typeof t!="undefined"?Ot.translations[e].replace("#",t.toString()):Ot.translations[e]}function Se(e,t=document){return R(`[data-md-component=${e}]`,t)}function ae(e,t=document){return P(`[data-md-component=${e}]`,t)}function ka(e){let t=R(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>R(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function xn(e){if(!B("announce.dismiss")||!e.childElementCount)return S;if(!e.hidden){let t=R(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return C(()=>{let t=new g;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),ka(e).pipe(w(r=>t.next(r)),_(()=>t.complete()),m(r=>$({ref:e},r)))})}function Ha(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function En(e,t){let r=new g;return r.subscribe(({hidden:o})=>{e.hidden=o}),Ha(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))}function Rt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Tn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Rt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Sn(e){return x("button",{class:"md-clipboard md-icon",title:Ee("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}var Ln=Mt(qr());function Qr(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(p=>!e.terms[p]).reduce((p,c)=>[...p,x("del",null,(0,Ln.default)(c))," "],[]).slice(0,-1),i=xe(),a=new URL(e.location,i.base);B("search.highlight")&&a.searchParams.set("h",Object.entries(e.terms).filter(([,p])=>p).reduce((p,[c])=>`${p} ${c}`.trim(),""));let{tags:s}=xe();return x("a",{href:`${a}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(p=>{let c=s?p in s?`md-tag-icon md-tag--${s[p]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${c}`},p)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Ee("search.result.term.missing"),": ",...n)))}function Mn(e){let t=e[0].score,r=[...e],o=xe(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),a=r.findIndex(l=>l.scoreQr(l,1)),...p.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,p.length>0&&p.length===1?Ee("search.result.more.one"):Ee("search.result.more.other",p.length))),...p.map(l=>Qr(l,1)))]:[]];return x("li",{class:"md-search-result__item"},c)}function _n(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?sr(r):r)))}function Kr(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function An(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Ra(e){var o;let t=xe(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Cn(e,t){var o;let r=xe();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Ee("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Ra)))}var Ia=0;function ja(e){let t=z([et(e),$t(e)]).pipe(m(([o,n])=>o||n),K()),r=C(()=>Zo(e)).pipe(ne(Ne),pt(1),He(t),m(()=>en(e)));return t.pipe(Ae(o=>o),v(()=>z([t,r])),m(([o,n])=>({active:o,offset:n})),pe())}function Fa(e,t){let{content$:r,viewport$:o}=t,n=`__tooltip2_${Ia++}`;return C(()=>{let i=new g,a=new _r(!1);i.pipe(Z(),ie(!1)).subscribe(a);let s=a.pipe(Ht(c=>Le(+!c*250,kr)),K(),v(c=>c?r:S),w(c=>c.id=n),pe());z([i.pipe(m(({active:c})=>c)),s.pipe(v(c=>$t(c,250)),Q(!1))]).pipe(m(c=>c.some(l=>l))).subscribe(a);let p=a.pipe(b(c=>c),re(s,o),m(([c,l,{size:f}])=>{let u=e.getBoundingClientRect(),d=u.width/2;if(l.role==="tooltip")return{x:d,y:8+u.height};if(u.y>=f.height/2){let{height:y}=ce(l);return{x:d,y:-16-y}}else return{x:d,y:16+u.height}}));return z([s,i,p]).subscribe(([c,{offset:l},f])=>{c.style.setProperty("--md-tooltip-host-x",`${l.x}px`),c.style.setProperty("--md-tooltip-host-y",`${l.y}px`),c.style.setProperty("--md-tooltip-x",`${f.x}px`),c.style.setProperty("--md-tooltip-y",`${f.y}px`),c.classList.toggle("md-tooltip2--top",f.y<0),c.classList.toggle("md-tooltip2--bottom",f.y>=0)}),a.pipe(b(c=>c),re(s,(c,l)=>l),b(c=>c.role==="tooltip")).subscribe(c=>{let l=ce(R(":scope > *",c));c.style.setProperty("--md-tooltip-width",`${l.width}px`),c.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(K(),ve(me),re(s)).subscribe(([c,l])=>{l.classList.toggle("md-tooltip2--active",c)}),z([a.pipe(b(c=>c)),s]).subscribe(([c,l])=>{l.role==="dialog"?(e.setAttribute("aria-controls",n),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",n)}),a.pipe(b(c=>!c)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),ja(e).pipe(w(c=>i.next(c)),_(()=>i.complete()),m(c=>$({ref:e},c)))})}function mt(e,{viewport$:t},r=document.body){return Fa(e,{content$:new j(o=>{let n=e.title,i=wn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t})}function Ua(e,t){let r=C(()=>z([tn(e),Ne(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:a,height:s}=ce(e);return{x:o-i.x+a/2,y:n-i.y+s/2}}));return et(e).pipe(v(o=>r.pipe(m(n=>({active:o,offset:n})),Te(+!o||1/0))))}function kn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return C(()=>{let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({offset:s}){e.style.setProperty("--md-tooltip-x",`${s.x}px`),e.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),tt(e).pipe(W(a)).subscribe(s=>{e.toggleAttribute("data-md-visible",s)}),O(i.pipe(b(({active:s})=>s)),i.pipe(_e(250),b(({active:s})=>!s))).subscribe({next({active:s}){s?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Me(16,me)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?e.style.setProperty("--md-tooltip-0",`${-s}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(a),b(s=>!(s.metaKey||s.ctrlKey))).subscribe(s=>{s.stopPropagation(),s.preventDefault()}),h(n,"mousedown").pipe(W(a),re(i)).subscribe(([s,{active:p}])=>{var c;if(s.button!==0||s.metaKey||s.ctrlKey)s.preventDefault();else if(p){s.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(c=Ie())==null||c.blur()}}),r.pipe(W(a),b(s=>s===o),Ge(125)).subscribe(()=>e.focus()),Ua(e,t).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function Wa(e){return e.tagName==="CODE"?P(".c, .c1, .cm",e):[e]}function Da(e){let t=[];for(let r of Wa(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let a;for(;a=/(\(\d+\))(!)?/.exec(i.textContent);){let[,s,p]=a;if(typeof p=="undefined"){let c=i.splitText(a.index);i=c.splitText(s.length),t.push(c)}else{i.textContent=s,t.push(i);break}}}}return t}function Hn(e,t){t.append(...Array.from(e.childNodes))}function fr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,a=new Map;for(let s of Da(t)){let[,p]=s.textContent.match(/\((\d+)\)/);fe(`:scope > li:nth-child(${p})`,e)&&(a.set(p,Tn(p,i)),s.replaceWith(a.get(p)))}return a.size===0?S:C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=[];for(let[l,f]of a)c.push([R(".md-typeset",f),R(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(p)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of c)l?Hn(f,u):Hn(u,f)}),O(...[...a].map(([,l])=>kn(l,t,{target$:r}))).pipe(_(()=>s.complete()),pe())})}function $n(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return $n(t)}}function Pn(e,t){return C(()=>{let r=$n(e);return typeof r!="undefined"?fr(r,e,t):S})}var Rn=Mt(Br());var Va=0;function In(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return In(t)}}function Na(e){return ge(e).pipe(m(({width:t})=>({scrollable:St(e).width>t})),ee("scrollable"))}function jn(e,t){let{matches:r}=matchMedia("(hover)"),o=C(()=>{let n=new g,i=n.pipe(jr(1));n.subscribe(({scrollable:c})=>{c&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let a=[];if(Rn.default.isSupported()&&(e.closest(".copy")||B("content.code.copy")&&!e.closest(".no-copy"))){let c=e.closest("pre");c.id=`__code_${Va++}`;let l=Sn(c.id);c.insertBefore(l,e),B("content.tooltips")&&a.push(mt(l,{viewport$}))}let s=e.closest(".highlight");if(s instanceof HTMLElement){let c=In(s);if(typeof c!="undefined"&&(s.classList.contains("annotate")||B("content.code.annotate"))){let l=fr(c,e,t);a.push(ge(s).pipe(W(i),m(({width:f,height:u})=>f&&u),K(),v(f=>f?l:S)))}}return P(":scope > span[id]",e).length&&e.classList.add("md-code__content"),Na(e).pipe(w(c=>n.next(c)),_(()=>n.complete()),m(c=>$({ref:e},c)),Re(...a))});return B("content.lazy")?tt(e).pipe(b(n=>n),Te(1),v(()=>o)):o}function za(e,{target$:t,print$:r}){let o=!0;return O(t.pipe(m(n=>n.closest("details:not([open])")),b(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(b(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Fn(e,t){return C(()=>{let r=new g;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),za(e,t).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}var Un=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.entityTitleText{fill:var(--md-mermaid-label-fg-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var Gr,Qa=0;function Ka(){return typeof mermaid=="undefined"||mermaid instanceof Element?Tt("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):I(void 0)}function Wn(e){return e.classList.remove("mermaid"),Gr||(Gr=Ka().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Un,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),G(1))),Gr.subscribe(()=>co(this,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${Qa++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),a=r.attachShadow({mode:"closed"});a.innerHTML=n,e.replaceWith(r),i==null||i(a)})),Gr.pipe(m(()=>({ref:e})))}var Dn=x("table");function Vn(e){return e.replaceWith(Dn),Dn.replaceWith(An(e)),I({ref:e})}function Ya(e){let t=e.find(r=>r.checked)||e[0];return O(...e.map(r=>h(r,"change").pipe(m(()=>R(`label[for="${r.id}"]`))))).pipe(Q(R(`label[for="${t.id}"]`)),m(r=>({active:r})))}function Nn(e,{viewport$:t,target$:r}){let o=R(".tabbed-labels",e),n=P(":scope > input",e),i=Kr("prev");e.append(i);let a=Kr("next");return e.append(a),C(()=>{let s=new g,p=s.pipe(Z(),ie(!0));z([s,ge(e),tt(e)]).pipe(W(p),Me(1,me)).subscribe({next([{active:c},l]){let f=Ve(c),{width:u}=ce(c);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=pr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ne(o),ge(o)]).pipe(W(p)).subscribe(([c,l])=>{let f=St(o);i.hidden=c.x<16,a.hidden=c.x>f.width-l.width-16}),O(h(i,"click").pipe(m(()=>-1)),h(a,"click").pipe(m(()=>1))).pipe(W(p)).subscribe(c=>{let{width:l}=ce(o);o.scrollBy({left:l*c,behavior:"smooth"})}),r.pipe(W(p),b(c=>n.includes(c))).subscribe(c=>c.click()),o.classList.add("tabbed-labels--linked");for(let c of n){let l=R(`label[for="${c.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(p),b(f=>!(f.metaKey||f.ctrlKey)),w(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return B("content.tabs.link")&&s.pipe(Ce(1),re(t)).subscribe(([{active:c},{offset:l}])=>{let f=c.innerText.trim();if(c.hasAttribute("data-md-switching"))c.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let y of P("[data-tabs]"))for(let L of P(":scope > input",y)){let X=R(`label[for="${L.id}"]`);if(X!==c&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),L.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),s.pipe(W(p)).subscribe(()=>{for(let c of P("audio, video",e))c.pause()}),Ya(n).pipe(w(c=>s.next(c)),_(()=>s.complete()),m(c=>$({ref:e},c)))}).pipe(Ke(se))}function zn(e,{viewport$:t,target$:r,print$:o}){return O(...P(".annotate:not(.highlight)",e).map(n=>Pn(n,{target$:r,print$:o})),...P("pre:not(.mermaid) > code",e).map(n=>jn(n,{target$:r,print$:o})),...P("pre.mermaid",e).map(n=>Wn(n)),...P("table:not([class])",e).map(n=>Vn(n)),...P("details",e).map(n=>Fn(n,{target$:r,print$:o})),...P("[data-tabs]",e).map(n=>Nn(n,{viewport$:t,target$:r})),...P("[title]",e).filter(()=>B("content.tooltips")).map(n=>mt(n,{viewport$:t})))}function Ba(e,{alert$:t}){return t.pipe(v(r=>O(I(!0),I(!1).pipe(Ge(2e3))).pipe(m(o=>({message:r,active:o})))))}function qn(e,t){let r=R(".md-typeset",e);return C(()=>{let o=new g;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Ba(e,t).pipe(w(n=>o.next(n)),_(()=>o.complete()),m(n=>$({ref:e},n)))})}var Ga=0;function Ja(e,t){document.body.append(e);let{width:r}=ce(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=cr(t),n=typeof o!="undefined"?Ne(o):I({x:0,y:0}),i=O(et(t),$t(t)).pipe(K());return z([i,n]).pipe(m(([a,s])=>{let{x:p,y:c}=Ve(t),l=ce(t),f=t.closest("table");return f&&t.parentElement&&(p+=f.offsetLeft+t.parentElement.offsetLeft,c+=f.offsetTop+t.parentElement.offsetTop),{active:a,offset:{x:p-s.x+l.width/2-r/2,y:c-s.y+l.height+8}}}))}function Qn(e){let t=e.title;if(!t.length)return S;let r=`__tooltip_${Ga++}`,o=Rt(r,"inline"),n=R(".md-typeset",o);return n.innerHTML=t,C(()=>{let i=new g;return i.subscribe({next({offset:a}){o.style.setProperty("--md-tooltip-x",`${a.x}px`),o.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),O(i.pipe(b(({active:a})=>a)),i.pipe(_e(250),b(({active:a})=>!a))).subscribe({next({active:a}){a?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe(Me(16,me)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(pt(125,me),b(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?o.style.setProperty("--md-tooltip-0",`${-a}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),Ja(o,e).pipe(w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))}).pipe(Ke(se))}function Xa({viewport$:e}){if(!B("header.autohide"))return I(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Be(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),K()),o=ze("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),K(),v(n=>n?r:I(!1)),Q(!1))}function Kn(e,t){return C(()=>z([ge(e),Xa(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),K((r,o)=>r.height===o.height&&r.hidden===o.hidden),G(1))}function Yn(e,{header$:t,main$:r}){return C(()=>{let o=new g,n=o.pipe(Z(),ie(!0));o.pipe(ee("active"),He(t)).subscribe(([{active:a},{hidden:s}])=>{e.classList.toggle("md-header--shadow",a&&!s),e.hidden=s});let i=ue(P("[title]",e)).pipe(b(()=>B("content.tooltips")),ne(a=>Qn(a)));return r.subscribe(o),t.pipe(W(n),m(a=>$({ref:e},a)),Re(i.pipe(W(n))))})}function Za(e,{viewport$:t,header$:r}){return mr(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=ce(e);return{active:o>=n}}),ee("active"))}function Bn(e,t){return C(()=>{let r=new g;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=fe(".md-content h1");return typeof o=="undefined"?S:Za(o,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))})}function Gn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),K()),n=o.pipe(v(()=>ge(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return z([o,n,t]).pipe(m(([i,{top:a,bottom:s},{offset:{y:p},size:{height:c}}])=>(c=Math.max(0,c-Math.max(0,a-p,i)-Math.max(0,c+p-s)),{offset:a-i,height:c,active:a-i<=p})),K((i,a)=>i.offset===a.offset&&i.height===a.height&&i.active===a.active))}function es(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return I(...e).pipe(ne(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),G(1))}function Jn(e){let t=P("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Pt("(prefers-color-scheme: light)");return C(()=>{let i=new g;return i.subscribe(a=>{if(document.body.setAttribute("data-md-color-switching",""),a.color.media==="(prefers-color-scheme)"){let s=matchMedia("(prefers-color-scheme: light)"),p=document.querySelector(s.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");a.color.scheme=p.getAttribute("data-md-color-scheme"),a.color.primary=p.getAttribute("data-md-color-primary"),a.color.accent=p.getAttribute("data-md-color-accent")}for(let[s,p]of Object.entries(a.color))document.body.setAttribute(`data-md-color-${s}`,p);for(let s=0;sa.key==="Enter"),re(i,(a,s)=>s)).subscribe(({index:a})=>{a=(a+1)%t.length,t[a].click(),t[a].focus()}),i.pipe(m(()=>{let a=Se("header"),s=window.getComputedStyle(a);return o.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(p=>(+p).toString(16).padStart(2,"0")).join("")})).subscribe(a=>r.content=`#${a}`),i.pipe(ve(se)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),es(t).pipe(W(n.pipe(Ce(1))),ct(),w(a=>i.next(a)),_(()=>i.complete()),m(a=>$({ref:e},a)))})}function Xn(e,{progress$:t}){return C(()=>{let r=new g;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),_(()=>r.complete()),m(o=>({ref:e,value:o})))})}var Jr=Mt(Br());function ts(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function Zn({alert$:e}){Jr.default.isSupported()&&new j(t=>{new Jr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||ts(R(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>Ee("clipboard.copied"))).subscribe(e)}function ei(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function rs(e,t){let r=new Map;for(let o of P("url",e)){let n=R("loc",o),i=[ei(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let a of P("[rel=alternate]",o)){let s=a.getAttribute("href");s!=null&&i.push(ei(new URL(s),t))}}return r}function ur(e){return un(new URL("sitemap.xml",e)).pipe(m(t=>rs(t,new URL(e))),de(()=>I(new Map)))}function os(e,t){if(!(e.target instanceof Element))return S;let r=e.target.closest("a");if(r===null)return S;if(r.target||e.metaKey||e.ctrlKey)return S;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),I(new URL(r.href))):S}function ti(e){let t=new Map;for(let r of P(":scope > *",e.head))t.set(r.outerHTML,r);return t}function ri(e){for(let t of P("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return I(e)}function ns(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...B("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=fe(o),i=fe(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=ti(document);for(let[o,n]of ti(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Se("container");return We(P("script",r)).pipe(v(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new j(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),S}),Z(),ie(document))}function oi({location$:e,viewport$:t,progress$:r}){let o=xe();if(location.protocol==="file:")return S;let n=ur(o.base);I(document).subscribe(ri);let i=h(document.body,"click").pipe(He(n),v(([p,c])=>os(p,c)),pe()),a=h(window,"popstate").pipe(m(ye),pe());i.pipe(re(t)).subscribe(([p,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",p)}),O(i,a).subscribe(e);let s=e.pipe(ee("pathname"),v(p=>fn(p,{progress$:r}).pipe(de(()=>(lt(p,!0),S)))),v(ri),v(ns),pe());return O(s.pipe(re(e,(p,c)=>c)),s.pipe(v(()=>e),ee("pathname"),v(()=>e),ee("hash")),e.pipe(K((p,c)=>p.pathname===c.pathname&&p.hash===c.hash),v(()=>i),w(()=>history.back()))).subscribe(p=>{var c,l;history.state!==null||!p.hash?window.scrollTo(0,(l=(c=history.state)==null?void 0:c.y)!=null?l:0):(history.scrollRestoration="auto",pn(p.hash),history.scrollRestoration="manual")}),e.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),t.pipe(ee("offset"),_e(100)).subscribe(({offset:p})=>{history.replaceState(p,"")}),s}var ni=Mt(qr());function ii(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,a)=>`${i}${a}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return a=>(0,ni.default)(a).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function jt(e){return e.type===1}function dr(e){return e.type===3}function ai(e,t){let r=yn(e);return O(I(location.protocol!=="file:"),ze("search")).pipe(Ae(o=>o),v(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:B("search.suggest")}}})),r}function si(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=Xr(n))==null?void 0:l.pathname;if(i===void 0)return;let a=ss(o.pathname,i);if(a===void 0)return;let s=ps(t.keys());if(!t.has(s))return;let p=Xr(a,s);if(!p||!t.has(p.href))return;let c=Xr(a,r);if(c)return c.hash=o.hash,c.search=o.search,c}function Xr(e,t){try{return new URL(e,t)}catch(r){return}}function ss(e,t){if(e.startsWith(t))return e.slice(t.length)}function cs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oS)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:a,aliases:s})=>a===i||s.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),v(n=>h(document.body,"click").pipe(b(i=>!i.metaKey&&!i.ctrlKey),re(o),v(([i,a])=>{if(i.target instanceof Element){let s=i.target.closest("a");if(s&&!s.target&&n.has(s.href)){let p=s.href;return!i.target.closest(".md-version")&&n.get(p)===a?S:(i.preventDefault(),I(new URL(p)))}}return S}),v(i=>ur(i).pipe(m(a=>{var s;return(s=si({selectedVersionSitemap:a,selectedVersionBaseURL:i,currentLocation:ye(),currentBaseURL:t.base}))!=null?s:i})))))).subscribe(n=>lt(n,!0)),z([r,o]).subscribe(([n,i])=>{R(".md-header__topic").appendChild(Cn(n,i))}),e.pipe(v(()=>o)).subscribe(n=>{var a;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let s=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(s)||(s=[s]);e:for(let p of s)for(let c of n.aliases.concat(n.version))if(new RegExp(p,"i").test(c)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let s of ae("outdated"))s.hidden=!1})}function ls(e,{worker$:t}){let{searchParams:r}=ye();r.has("q")&&(Je("search",!0),e.value=r.get("q"),e.focus(),ze("search").pipe(Ae(i=>!i)).subscribe(()=>{let i=ye();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=et(e),n=O(t.pipe(Ae(jt)),h(e,"keyup"),o).pipe(m(()=>e.value),K());return z([n,o]).pipe(m(([i,a])=>({value:i,focus:a})),G(1))}function pi(e,{worker$:t}){let r=new g,o=r.pipe(Z(),ie(!0));z([t.pipe(Ae(jt)),r],(i,a)=>a).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Je("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=R("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ls(e,{worker$:t}).pipe(w(i=>r.next(i)),_(()=>r.complete()),m(i=>$({ref:e},i)),G(1))}function li(e,{worker$:t,query$:r}){let o=new g,n=on(e.parentElement).pipe(b(Boolean)),i=e.parentElement,a=R(":scope > :first-child",e),s=R(":scope > :last-child",e);ze("search").subscribe(l=>s.setAttribute("role",l?"list":"presentation")),o.pipe(re(r),Wr(t.pipe(Ae(jt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:a.textContent=f.length?Ee("search.result.none"):Ee("search.result.placeholder");break;case 1:a.textContent=Ee("search.result.one");break;default:let u=sr(l.length);a.textContent=Ee("search.result.other",u)}});let p=o.pipe(w(()=>s.innerHTML=""),v(({items:l})=>O(I(...l.slice(0,10)),I(...l.slice(10)).pipe(Be(4),Vr(n),v(([f])=>f)))),m(Mn),pe());return p.subscribe(l=>s.appendChild(l)),p.pipe(ne(l=>{let f=fe("details",l);return typeof f=="undefined"?S:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(b(dr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),_(()=>o.complete()),m(l=>$({ref:e},l)))}function ms(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=ye();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function mi(e,t){let r=new g,o=r.pipe(Z(),ie(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),ms(e,t).pipe(w(n=>r.next(n)),_(()=>r.complete()),m(n=>$({ref:e},n)))}function fi(e,{worker$:t,keyboard$:r}){let o=new g,n=Se("search-query"),i=O(h(n,"keydown"),h(n,"focus")).pipe(ve(se),m(()=>n.value),K());return o.pipe(He(i),m(([{suggest:s},p])=>{let c=p.split(/([\s-]+)/);if(s!=null&&s.length&&c[c.length-1]){let l=s[s.length-1];l.startsWith(c[c.length-1])&&(c[c.length-1]=l)}else c.length=0;return c})).subscribe(s=>e.innerHTML=s.join("").replace(/\s/g," ")),r.pipe(b(({mode:s})=>s==="search")).subscribe(s=>{switch(s.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(b(dr),m(({data:s})=>s)).pipe(w(s=>o.next(s)),_(()=>o.complete()),m(()=>({ref:e})))}function ui(e,{index$:t,keyboard$:r}){let o=xe();try{let n=ai(o.search,t),i=Se("search-query",e),a=Se("search-result",e);h(e,"click").pipe(b(({target:p})=>p instanceof Element&&!!p.closest("a"))).subscribe(()=>Je("search",!1)),r.pipe(b(({mode:p})=>p==="search")).subscribe(p=>{let c=Ie();switch(p.type){case"Enter":if(c===i){let l=new Map;for(let f of P(":first-child [href]",a)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}p.claim()}break;case"Escape":case"Tab":Je("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof c=="undefined")i.focus();else{let l=[i,...P(":not(details) > [href], summary, details[open] [href]",a)],f=Math.max(0,(Math.max(0,l.indexOf(c))+l.length+(p.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}p.claim();break;default:i!==Ie()&&i.focus()}}),r.pipe(b(({mode:p})=>p==="global")).subscribe(p=>{switch(p.type){case"f":case"s":case"/":i.focus(),i.select(),p.claim();break}});let s=pi(i,{worker$:n});return O(s,li(a,{worker$:n,query$:s})).pipe(Re(...ae("search-share",e).map(p=>mi(p,{query$:s})),...ae("search-suggest",e).map(p=>fi(p,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ye}}function di(e,{index$:t,location$:r}){return z([t,r.pipe(Q(ye()),b(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>ii(o.config)(n.searchParams.get("h"))),m(o=>{var a;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let s=i.nextNode();s;s=i.nextNode())if((a=s.parentElement)!=null&&a.offsetHeight){let p=s.textContent,c=o(p);c.length>p.length&&n.set(s,c)}for(let[s,p]of n){let{childNodes:c}=x("span",null,p);s.replaceWith(...Array.from(c))}return{ref:e,nodes:n}}))}function fs(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:a},{offset:{y:s}}])=>(a=a+Math.min(n,Math.max(0,s-i))-n,{height:a,locked:s>=i+n})),K((i,a)=>i.height===a.height&&i.locked===a.locked))}function Zr(e,o){var n=o,{header$:t}=n,r=so(n,["header$"]);let i=R(".md-sidebar__scrollwrap",e),{y:a}=Ve(i);return C(()=>{let s=new g,p=s.pipe(Z(),ie(!0)),c=s.pipe(Me(0,me));return c.pipe(re(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*a}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),c.pipe(Ae()).subscribe(()=>{for(let l of P(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2})}}}),ue(P("label[tabindex]",e)).pipe(ne(l=>h(l,"click").pipe(ve(se),m(()=>l),W(p)))).subscribe(l=>{let f=R(`[id="${l.htmlFor}"]`);R(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),fs(e,r).pipe(w(l=>s.next(l)),_(()=>s.complete()),m(l=>$({ref:e},l)))})}function hi(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return st(je(`${r}/releases/latest`).pipe(de(()=>S),m(o=>({version:o.tag_name})),De({})),je(r).pipe(de(()=>S),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return je(r).pipe(m(o=>({repositories:o.public_repos})),De({}))}}function bi(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return st(je(`${r}/releases/permalink/latest`).pipe(de(()=>S),m(({tag_name:o})=>({version:o})),De({})),je(r).pipe(de(()=>S),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),De({}))).pipe(m(([o,n])=>$($({},o),n)))}function vi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return hi(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return bi(r,o)}return S}var us;function ds(e){return us||(us=C(()=>{let t=__md_get("__source",sessionStorage);if(t)return I(t);if(ae("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return S}return vi(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>S),b(t=>Object.keys(t).length>0),m(t=>({facts:t})),G(1)))}function gi(e){let t=R(":scope > :last-child",e);return C(()=>{let r=new g;return r.subscribe(({facts:o})=>{t.appendChild(_n(o)),t.classList.add("md-source__repository--active")}),ds(e).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function hs(e,{viewport$:t,header$:r}){return ge(document.body).pipe(v(()=>mr(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function yi(e,t){return C(()=>{let r=new g;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(B("navigation.tabs.sticky")?I({hidden:!1}):hs(e,t)).pipe(w(o=>r.next(o)),_(()=>r.complete()),m(o=>$({ref:e},o)))})}function bs(e,{viewport$:t,header$:r}){let o=new Map,n=P(".md-nav__link",e);for(let s of n){let p=decodeURIComponent(s.hash.substring(1)),c=fe(`[id="${p}"]`);typeof c!="undefined"&&o.set(s,c)}let i=r.pipe(ee("height"),m(({height:s})=>{let p=Se("main"),c=R(":scope > :first-child",p);return s+.8*(c.offsetTop-p.offsetTop)}),pe());return ge(document.body).pipe(ee("height"),v(s=>C(()=>{let p=[];return I([...o].reduce((c,[l,f])=>{for(;p.length&&o.get(p[p.length-1]).tagName>=f.tagName;)p.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return c.set([...p=[...p,l]].reverse(),u)},new Map))}).pipe(m(p=>new Map([...p].sort(([,c],[,l])=>c-l))),He(i),v(([p,c])=>t.pipe(Fr(([l,f],{offset:{y:u},size:d})=>{let y=u+d.height>=Math.floor(s.height);for(;f.length;){let[,L]=f[0];if(L-c=u&&!y)f=[l.pop(),...f];else break}return[l,f]},[[],[...p]]),K((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([s,p])=>({prev:s.map(([c])=>c),next:p.map(([c])=>c)})),Q({prev:[],next:[]}),Be(2,1),m(([s,p])=>s.prev.length{let i=new g,a=i.pipe(Z(),ie(!0));if(i.subscribe(({prev:s,next:p})=>{for(let[c]of p)c.classList.remove("md-nav__link--passed"),c.classList.remove("md-nav__link--active");for(let[c,[l]]of s.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",c===s.length-1)}),B("toc.follow")){let s=O(t.pipe(_e(1),m(()=>{})),t.pipe(_e(250),m(()=>"smooth")));i.pipe(b(({prev:p})=>p.length>0),He(o.pipe(ve(se))),re(s)).subscribe(([[{prev:p}],c])=>{let[l]=p[p.length-1];if(l.offsetHeight){let f=cr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=ce(f);f.scrollTo({top:u-d/2,behavior:c})}}})}return B("navigation.tracking")&&t.pipe(W(a),ee("offset"),_e(250),Ce(1),W(n.pipe(Ce(1))),ct({delay:250}),re(i)).subscribe(([,{prev:s}])=>{let p=ye(),c=s[s.length-1];if(c&&c.length){let[l]=c,{hash:f}=new URL(l.href);p.hash!==f&&(p.hash=f,history.replaceState({},"",`${p}`))}else p.hash="",history.replaceState({},"",`${p}`)}),bs(e,{viewport$:t,header$:r}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))})}function vs(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:a}})=>a),Be(2,1),m(([a,s])=>a>s&&s>0),K()),i=r.pipe(m(({active:a})=>a));return z([i,n]).pipe(m(([a,s])=>!(a&&s)),K(),W(o.pipe(Ce(1))),ie(!0),ct({delay:250}),m(a=>({hidden:a})))}function Ei(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new g,a=i.pipe(Z(),ie(!0));return i.subscribe({next({hidden:s}){e.hidden=s,s?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(a),ee("height")).subscribe(({height:s})=>{e.style.top=`${s+16}px`}),h(e,"click").subscribe(s=>{s.preventDefault(),window.scrollTo({top:0})}),vs(e,{viewport$:t,main$:o,target$:n}).pipe(w(s=>i.next(s)),_(()=>i.complete()),m(s=>$({ref:e},s)))}function wi({document$:e,viewport$:t}){e.pipe(v(()=>P(".md-ellipsis")),ne(r=>tt(r).pipe(W(e.pipe(Ce(1))),b(o=>o),m(()=>r),Te(1))),b(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,B("content.tooltips")?mt(n,{viewport$:t}).pipe(W(e.pipe(Ce(1))),_(()=>n.removeAttribute("title"))):S})).subscribe(),B("content.tooltips")&&e.pipe(v(()=>P(".md-status")),ne(r=>mt(r,{viewport$:t}))).subscribe()}function Ti({document$:e,tablet$:t}){e.pipe(v(()=>P(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ne(r=>h(r,"change").pipe(Dr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),re(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function gs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Si({document$:e}){e.pipe(v(()=>P("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),b(gs),ne(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Oi({viewport$:e,tablet$:t}){z([ze("search"),t]).pipe(m(([r,o])=>r&&!o),v(r=>I(r).pipe(Ge(r?400:100))),re(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ys(){return location.protocol==="file:"?Tt(`${new URL("search/search_index.js",eo.base)}`).pipe(m(()=>__index),G(1)):je(new URL("search/search_index.json",eo.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ot=Go(),Ut=sn(),Lt=ln(Ut),to=an(),Oe=gn(),hr=Pt("(min-width: 960px)"),Mi=Pt("(min-width: 1220px)"),_i=mn(),eo=xe(),Ai=document.forms.namedItem("search")?ys():Ye,ro=new g;Zn({alert$:ro});var oo=new g;B("navigation.instant")&&oi({location$:Ut,viewport$:Oe,progress$:oo}).subscribe(ot);var Li;((Li=eo.version)==null?void 0:Li.provider)==="mike"&&ci({document$:ot});O(Ut,Lt).pipe(Ge(125)).subscribe(()=>{Je("drawer",!1),Je("search",!1)});to.pipe(b(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=fe("link[rel=prev]");typeof t!="undefined"&<(t);break;case"n":case".":let r=fe("link[rel=next]");typeof r!="undefined"&<(r);break;case"Enter":let o=Ie();o instanceof HTMLLabelElement&&o.click()}});wi({viewport$:Oe,document$:ot});Ti({document$:ot,tablet$:hr});Si({document$:ot});Oi({viewport$:Oe,tablet$:hr});var rt=Kn(Se("header"),{viewport$:Oe}),Ft=ot.pipe(m(()=>Se("main")),v(e=>Gn(e,{viewport$:Oe,header$:rt})),G(1)),xs=O(...ae("consent").map(e=>En(e,{target$:Lt})),...ae("dialog").map(e=>qn(e,{alert$:ro})),...ae("palette").map(e=>Jn(e)),...ae("progress").map(e=>Xn(e,{progress$:oo})),...ae("search").map(e=>ui(e,{index$:Ai,keyboard$:to})),...ae("source").map(e=>gi(e))),Es=C(()=>O(...ae("announce").map(e=>xn(e)),...ae("content").map(e=>zn(e,{viewport$:Oe,target$:Lt,print$:_i})),...ae("content").map(e=>B("search.highlight")?di(e,{index$:Ai,location$:Ut}):S),...ae("header").map(e=>Yn(e,{viewport$:Oe,header$:rt,main$:Ft})),...ae("header-title").map(e=>Bn(e,{viewport$:Oe,header$:rt})),...ae("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?Nr(Mi,()=>Zr(e,{viewport$:Oe,header$:rt,main$:Ft})):Nr(hr,()=>Zr(e,{viewport$:Oe,header$:rt,main$:Ft}))),...ae("tabs").map(e=>yi(e,{viewport$:Oe,header$:rt})),...ae("toc").map(e=>xi(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Lt})),...ae("top").map(e=>Ei(e,{viewport$:Oe,header$:rt,main$:Ft,target$:Lt})))),Ci=ot.pipe(v(()=>Es),Re(xs),G(1));Ci.subscribe();window.document$=ot;window.location$=Ut;window.target$=Lt;window.keyboard$=to;window.viewport$=Oe;window.tablet$=hr;window.screen$=Mi;window.print$=_i;window.alert$=ro;window.progress$=oo;window.component$=Ci;})(); +//# sourceMappingURL=bundle.88dd0f4e.min.js.map + diff --git a/assets/javascripts/bundle.88dd0f4e.min.js.map b/assets/javascripts/bundle.88dd0f4e.min.js.map new file mode 100644 index 00000000..dab2a875 --- /dev/null +++ b/assets/javascripts/bundle.88dd0f4e.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2024 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n *\n * @class BehaviorSubject\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an +
+ +

Reflow + columnSelector widget

+ Set table width: +
    +
  • Auto
  • +
  • +
  • +
  • +
  • +
  • +
+ +
+ +
+ +

Reflow2 widget (multiple thead rows)

+ Set table width: +
    +
  • Auto
  • +
  • +
  • +
  • +
  • +
  • +
+ +
+ +
+ + + +

HTML

+
+
<h3>Reflow widget only</h3>
+<table id="table1">
+  <thead>
+    <tr>
+      <th>Rank</th>
+      <th>Age</th>
+      <th>Total</th>
+      <th>Discount</th>
+      <th>Date</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr><td>1</td><td>25</td><td>$5.95</td><td>22%</td><td>Jun 26, 2013 7:22 AM</td></tr>
+    <tr><td>11</td><td>12</td><td>$82.99</td><td>5%</td><td>Aug 21, 2013 12:21 PM</td></tr>
+    <tr><td>12</td><td>51</td><td>$99.29</td><td>18%</td><td>Oct 13, 2013 1:15 PM</td></tr>
+    <tr><td>51</td><td>28</td><td>$9.99</td><td>20%</td><td>Jul 6, 2013 8:14 AM</td></tr>
+    <tr><td>21</td><td>33</td><td>$19.99</td><td>25%</td><td>Dec 10, 2012 5:14 AM</td></tr>
+    <tr><td>013</td><td>18</td><td>$65.89</td><td>45%</td><td>Jan 12, 2013 11:14 AM</td></tr>
+    <tr><td>005</td><td>45</td><td>$153.19</td><td>45%</td><td>Jan 18, 2014 9:12 AM</td></tr>
+    <tr><td>10</td><td>3</td><td>$5.29</td><td>4%</td><td>Jan 8, 2013 5:11 PM</td></tr>
+    <tr><td>16</td><td>24</td><td>$14.19</td><td>14%</td><td>Jan 14, 2014 11:23 AM</td></tr>
+    <tr><td>66</td><td>22</td><td>$13.19</td><td>11%</td><td>Jan 18, 2013 9:12 AM</td></tr>
+    <tr><td>100</td><td>18</td><td>$55.20</td><td>15%</td><td>Feb 12, 2013 7:23 PM</td></tr>
+    <tr><td>55</td><td>65</td><td>$123.00</td><td>32%</td><td>Jan 20, 2014 1:12 PM</td></tr>
+    <tr><td>9</td><td>25</td><td>$22.09</td><td>17%</td><td>Jun 11, 2013 10:55 AM</td></tr>
+    <tr><td>13</td><td>12</td><td>$19.99</td><td>18%</td><td>Jan 20, 2014 7:45 PM</td></tr>
+    <tr><td>73</td><td>58</td><td>$129.19</td><td>39%</td><td>Jan 20, 2014 10:11 AM</td></tr>
+  </tbody>
+</table>
+
+<h3>Reflow   columnSelector widget</h3>
+<!-- This selector markup is completely customizable -->
+<div class="columnSelectorWrapper">
+  <input id="colSelect1" type="checkbox" class="hidden">
+  <label class="columnSelectorButton" for="colSelect1">Column</label>
+  <div id="columnSelector" class="columnSelector">
+    <!-- this div is where the column selector is added -->
+  </div>
+</div> (When "Auto" is set, the table becomes responsive; resize the browser window to see it work)
+
+<table id="table2">
+  <thead>
+    <tr>
+      <th data-priority="critical">Name</th>
+      <th data-priority="critical">Major</th>
+      <th data-priority="6" data-name="Gender">Sex</th>
+      <th data-priority="4">English</th>
+      <th data-priority="5">Japanese</th>
+      <th data-priority="3">Calculus</th>
+      <th data-priority="2">Geometry</th>
+    </tr>
+  </thead>
+  <tfoot>
+    <tr><th>Name</th><th>Major</th><th>Sex</th><th>English</th><th>Japanese</th><th>Calculus</th><th>Geometry</th></tr>
+  </tfoot>
+  <tbody>
+    <tr><td>Student03</td><td>Languages</td><td>female</td><td>85</td><td>95</td><td>80</td><td>85</td></tr>
+    <tr><td>Student04</td><td>Languages</td><td>male</td><td>60</td><td>55</td><td>100</td><td>100</td></tr>
+    <tr><td>Student05</td><td>Languages</td><td>female</td><td>68</td><td>80</td><td>95</td><td>80</td></tr>
+    <tr><td>Student12</td><td>Mathematics</td><td>female</td><td>100</td><td>75</td><td>70</td><td>85</td></tr>
+    <tr><td>Student13</td><td>Languages</td><td>female</td><td>100</td><td>80</td><td>100</td><td>90</td></tr>
+    <tr><td>Student14</td><td>Languages</td><td>female</td><td>50</td><td>45</td><td>55</td><td>90</td></tr>
+    <tr><td>Student15</td><td>Languages</td><td>male</td><td>95</td><td>35</td><td>100</td><td>90</td></tr>
+    <tr><td>Student16</td><td>Languages</td><td>female</td><td>100</td><td>50</td><td>30</td><td>70</td></tr>
+    <tr><td>Student17</td><td>Languages</td><td>female</td><td>80</td><td>100</td><td>55</td><td>65</td></tr>
+    <tr><td>Student18</td><td>Mathematics</td><td>male</td><td>30</td><td>49</td><td>55</td><td>75</td></tr>
+    <tr><td>Student19</td><td>Languages</td><td>male</td><td>68</td><td>90</td><td>88</td><td>70</td></tr>
+    <tr><td>Student20</td><td>Mathematics</td><td>male</td><td>40</td><td>45</td><td>40</td><td>80</td></tr>
+    <tr><td>Student01</td><td>Languages</td><td>male</td><td>80</td><td>70</td><td>75</td><td>80</td></tr>
+    <tr><td>Student02</td><td>Mathematics</td><td>male</td><td>90</td><td>88</td><td>100</td><td>90</td></tr>
+    <tr><td>Student06</td><td>Mathematics</td><td>male</td><td>100</td><td>99</td><td>100</td><td>90</td></tr>
+    <tr><td>Student07</td><td>Mathematics</td><td>male</td><td>85</td><td>68</td><td>90</td><td>90</td></tr>
+    <tr><td>Student08</td><td>Languages</td><td>male</td><td>100</td><td>90</td><td>90</td><td>85</td></tr>
+    <tr><td>Student09</td><td>Mathematics</td><td>male</td><td>80</td><td>50</td><td>65</td><td>75</td></tr>
+    <tr><td>Student10</td><td>Languages</td><td>male</td><td>85</td><td>100</td><td>100</td><td>90</td></tr>
+    <tr><td>Student11</td><td>Languages</td><td>male</td><td>86</td><td>85</td><td>100</td><td>100</td></tr>
+  </tbody>
+</table>
+
+<h3>Reflow2 widget (multiple thead rows)</h3>
+<table id="table3">
+  <thead>
+    <tr>
+      <th class="ui-table-reflow-ignore sorter-false">Paris</th>
+      <th colspan="2">Average Temperatures (C)</th>
+      <th colspan="2">Average Rainfall</th>
+    </tr>
+    <tr>
+      <th>Month</th>
+      <th>Minimum Temp</th>
+      <th>Maximum Temp</th>
+      <th>Precipitaion (mm)</th>
+      <th>Rainfall Days</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr><th>January</th><td>3</td><td>8</td><td>17.8</td><td>10</td></tr>
+    <tr><th>February</th><td>2</td><td>9</td><td>21.7</td><td>9</td></tr>
+    <tr><th>March</th><td>4</td><td>13</td><td>24.2</td><td>10</td></tr>
+    <tr><th>April</th><td>6</td><td>15</td><td>24.6</td><td>11</td></tr>
+    <tr><th>May</th><td>10</td><td>20</td><td>26.2</td><td>10</td></tr>
+    <tr><th>June</th><td>13</td><td>23</td><td>25.1</td><td>9</td></tr>
+    <tr><th>July</th><td>15</td><td>25</td><td>21.7</td><td>7</td></tr>
+    <tr><th>August</th><td>15</td><td>25</td><td>21.4</td><td>7</td></tr>
+    <tr><th>September</th><td>11</td><td>21</td><td>15.6</td><td>8</td></tr>
+    <tr><th>October</th><td>9</td><td>17</td><td>25.3</td><td>11</td></tr>
+    <tr><th>November</th><td>5</td><td>11</td><td>22.4</td><td>12</td></tr>
+    <tr><th>December</th><td>3</td><td>8</td><td>26.6</td><td>12</td></tr>
+  </tbody>
+</table>
+
+ +

Javascript

+
+
$(function() {
+
+	// simple reflow widget (table with 1 header row)
+	$("#table1").tablesorter({
+		theme: 'blue',
+		widgets: ['zebra', 'reflow'],
+		widgetOptions : {
+			// class name added to make it responsive (class name within media query)
+			reflow_className    : 'ui-table-reflow',
+			// header attribute containing modified header name
+			reflow_headerAttrib : 'data-name',
+			// data attribute added to each tbody cell
+			// it contains the header cell text, visible upon reflow
+			reflow_dataAttrib   : 'data-title'
+		}
+	});
+
+	// simple reflow widget + columnSelector & stickyHeaders widgets
+	$("#table2").tablesorter({
+		theme: 'blue',
+		widgets: ['zebra', 'reflow', 'columnSelector', 'stickyHeaders'],
+		widgetOptions : {
+			// target the column selector markup
+			columnSelector_container : $('#columnSelector'),
+			// data attribute containing column name to use in the selector container
+			// make it use the same as reflow_headerAttrib
+			columnSelector_name : 'data-name',
+
+			// header attribute containing modified header name
+			reflow_headerAttrib : 'data-name'
+		}
+	});
+
+	// reflow2 widget (table with multiple header rows)
+	$("#table3").tablesorter({
+		theme: 'blue',
+		widgets: ['zebra', 'reflow2'],
+		widgetOptions: {
+			// class name added to make it responsive (class name within media query)
+			reflow2_className    : 'ui-table-reflow',
+			// ignore header cell content with this class name
+			reflow2_classIgnore  : 'ui-table-reflow-ignore',
+			// header attribute containing modified header name
+			reflow2_headerAttrib : 'data-name',
+			// class name applied to thead labels
+			reflow2_labelClass   : 'ui-table-cell-label',
+			// class name applied to first row thead label
+			reflow2_labelTop     : 'ui-table-cell-label-top'
+		}
+	});
+
+});
+
+ + + + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-reflow1.html b/compare/thirdparty/tablesorter/docs/example-widget-reflow1.html new file mode 100644 index 00000000..474ec92c --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-reflow1.html @@ -0,0 +1,104 @@ + + + + + jQuery tablesorter 2.0 - Table Reflow Widget (beta) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankAgeTotalDiscountDate
125$5.9522%Jun 26, 2013 7:22 AM
1112$82.995%Aug 21, 2013 12:21 PM
1251$99.2918%Oct 13, 2013 1:15 PM
5128$9.9920%Jul 6, 2013 8:14 AM
2133$19.9925%Dec 10, 2012 5:14 AM
01318$65.8945%Jan 12, 2013 11:14 AM
00545$153.1945%Jan 18, 2014 9:12 AM
103$5.294%Jan 8, 2013 5:11 PM
1624$14.1914%Jan 14, 2014 11:23 AM
6622$13.1911%Jan 18, 2013 9:12 AM
10018$55.2015%Feb 12, 2013 7:23 PM
5565$123.0032%Jan 20, 2014 1:12 PM
925$22.0917%Jun 11, 2013 10:55 AM
1312$19.9918%Jan 20, 2014 7:45 PM
7358$129.1939%Jan 20, 2014 10:11 AM
+ + + \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/example-widget-reflow2.html b/compare/thirdparty/tablesorter/docs/example-widget-reflow2.html new file mode 100644 index 00000000..96ec330e --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-reflow2.html @@ -0,0 +1,250 @@ + + + + + jQuery tablesorter 2.0 - Table Reflow Widget (beta) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+
(When "Auto" is set, the table becomes responsive; resize the browser window to see it work) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameMajorSexEnglishJapaneseCalculusGeometry
NameMajorSexEnglishJapaneseCalculusGeometry
Student03Languagesfemale85958085
Student04Languagesmale6055100100
Student05Languagesfemale68809580
Student12Mathematicsfemale100757085
Student13Languagesfemale1008010090
Student14Languagesfemale50455590
Student15Languagesmale953510090
Student16Languagesfemale100503070
Student17Languagesfemale801005565
Student18Mathematicsmale30495575
Student19Languagesmale68908870
Student20Mathematicsmale40454080
Student01Languagesmale80707580
Student02Mathematicsmale908810090
Student06Mathematicsmale1009910090
Student07Mathematicsmale85689090
Student08Languagesmale100909085
Student09Mathematicsmale80506575
Student10Languagesmale8510010090
Student11Languagesmale8685100100
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-reflow3.html b/compare/thirdparty/tablesorter/docs/example-widget-reflow3.html new file mode 100644 index 00000000..a582655f --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-reflow3.html @@ -0,0 +1,125 @@ + + + + + jQuery tablesorter 2.0 - Table Reflow Widget (beta) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParisAverage Temperatures (C)Average Rainfall
MonthMinimum TempMaximum TempPrecipitaion (mm)Rainfall Days
January3817.810
February2921.79
March41324.210
April61524.611
May102026.210
June132325.19
July152521.77
August152521.47
September112115.68
October91725.311
November51122.412
December3826.612
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-resizable.html b/compare/thirdparty/tablesorter/docs/example-widget-resizable.html new file mode 100644 index 00000000..21cbae9b --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-resizable.html @@ -0,0 +1,434 @@ + + + + + jQuery tablesorter 2.0 - Resizable Columns Widget + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +

+
+
+ +

Notes

+
+
    +
  • IMPORTANT If using jQuery versions older than 1.8, css box-sizing for the table MUST be set as box-sizing: content-box; or the resizable widget will not work properly.
  • +
  • IMPORTANT The resizable will not work properly if the widthFixed option is set to true. Make sure it is set to false (default setting).
  • +
  • IMPORTANT The resize div ends up with a zero height if the header cell is empty. Please include at least a &nbsp; in the cell to allow it to render properly (ref). No longer necessary as the resizable widget no longer adds elements inside the table header cells.

  • + +
  • In v2.29.0, added a resizableComplete event.
  • +
  • In v2.28.8, added the resizable_includeFooter option.
  • +
  • In v2.28.5, +
      +
    • A resizableUpdate event can be triggered on the table to update the resizable handles.
    • +
    • A resizableReset event can be triggered to reset the resizable columns to their default widths.
    • +
    +
  • +
  • In v2.22.2, resizable will now work with tables inside an overflow wrapper (see issue #953).
  • +
  • In v2.21.5, this widget now works with the columnSelector widget & tables with margins (see issue #864).
  • +
  • In v2.21.3 +
      +
    • Performed a major overhaul on the resizable widget to add resizable handles outside of the table, so now the resizable handles can be used over the entire height of the table!
    • +
    • This change allows the resizable widget to work seemlessly with the stickyHeaders widget (included in this demo); sadly, to make it work with the scroller widget will require more work.
    • +
    +
  • +
  • In v2.17.4, modified the bindings so the mouse move will now work on the document instead of only within the table header; this makes interaction consistent with what the user expects.
  • +
  • In v2.15.12, added resizable_widths option which allows the setting of default & reset header widths.
  • +
  • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
  • +
  • This widget now saves all changed column widths to local storage, or it falls back to a cookie! (v2.1)
  • +
  • Column width saving requires the new "$.tablesorter.storage()" function included with the "jquery.tablesorter.widgets.js" file (v2.1).
  • +
  • Right clicking (opening the context menu) will now reset the resized columns (v2.4).
  • +
  • Holding down the shift key while resizing will force the last column or the table to resize instead of the next column, but only if the table is full width (v2.7.4).
  • +
  • Prevent resizing a column by adding any of the following (they all do the same thing), set in order of priority (v2.7.4): +
      +
    • jQuery data data-resizable="false".
    • +
    • metadata class="{ resizable: 'false'}". This requires the metadata plugin.
    • +
    • headers option headers : { 0 : { resizable : 'false' } }.
    • +
    • header class name class="resizable-false".
    • +
    +
  • +
  • Setting the resizable widget option to false, will only prevent the saving of resized columns, it has nothing to do with preventing a column from being resized.
  • +
  • Because this widget uses jQuery's closest() (jQuery 1.3+) and index() (jQuery 1.4+) functions, it requires these newer versions of jQuery in order to work.
  • +
  • In order to save the resized widths, jQuery version 1.4.1+ should be used because jQuery's parseJson() function is needed.
  • +
  • Setting the resizable_addLastColumn widget option to true will add the resizable handle to the last column, see the "non-full" width table below (v2.9.0).
  • +
+
+ +

Options

+
+

Resizable widget defaults (added inside of tablesorter widgetOptions)

+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
trueWhen true and the storage widget is included, all column widths will be saved to storage. +
+

This means that when the page is reloaded, any adjusted column widths will be restored.

+

The adjusted column sizes can be reset at any time by right-clicking on the table header.

+
+
falseWhen true, the last column of the table is made resizable. +
+

If the table is full width, adjusting the right edge would actually shrink or stretch all columns without moving the right border.

+

On narrower tables, the table width will be adjusted.

+
+
[ ]Set any default column widths within this zero-based-column-indexed array +
+

This option allows the setting of column widths initially (before any resizing occurs, or if there are no saved column widths) and when resetting (right-click on the column header) or triggering a resizableReset event on the table.

+

The array may contain any css allowed width definitions (e.g. percentage, pixels, em, etc).

+

Undefined column widths to not add a specified width to the column

+

Here is an example from the second table in this demo showing how to set this option:

+
// Note that the "Age" column is not resizable,
+// but the width can still be set to 40px here
+resizable_widths : [ '10%', '10%', '40px', '10%', '100px' ]
+
+
falseWhen this option is set to a number, or true, the resizing event from the window is throttled. +
+

Essentially, throttling the resize event limits the number of times the javascript is executed while resizing the window.

+

Without any throttling, slower browsers may lag while the javascript adjusts the column widths of the table.

+

With excessive throttling, the user will notice the table column width lag (while will be seen as the width jumping sporatically to catch up to the mouse) behind the mouse movement.

+

When this option is set to true, a default of 5 (milliseconds) is used.

+

If setting a number, try to keep this number in the 0 to 10 range.

+
+
falseWhen true, the last column will be targeted for resizing. +
+

When true, resizing a column will change the size of the selected column, and the last column, not the selected column's neighbor.

+

When false, resizing a column will move the column border between it's neighbors.

+

Also, in a full width table, if this option is false, the same behavior as when this option is true can be seen when resizing a column while holding down the Shift key on the keyboard - the last column is resized.

+
+
+
+ +

Methods

+
+

resizableUpdate

+
+ A resizableUpdate event (added v2.28.5) can be triggered on the table to force an update of the resizable handles. This behaves the same as triggering a resize event on the window without causing any unwanted page reflow. +
// equivalent to $( window ).trigger( 'resize' );
+$( 'table' ).trigger( 'resizableUpdate' );
+
+ +

resizableReset

+
+ A resizableReset event (added v2.28.5) can be triggered on the table reset the resizable columns to their original, or set resizable_widths value. This is the same as the user right-clicking on the table header causing a reset. +
$( 'table' ).trigger( 'resizableReset' );
+
+
+ +

Events

+
+

resizableComplete

+
+ A resizableComplete event (added v2.29.0) is triggered on the table after the user has resized a column and +
$( 'table' ).on( 'resizableComplete', function(event) {
+  var resizable_vars = this.config.widgetOptions.resizable_vars;
+  var saved = {
+    // resizable_vars.storedSizes is an empty array when no resizing was done
+    // otherwise it contains each column width (padding & border width not included) in pixels
+    columnWidths: resizable_vars.storedSizes,
+    // overall table width
+    tableWidth: resizable_vars.tableWidth
+  };
+  // do something with the values
+});
+
+
+ +
+ +

Demo

+
+

+ Non-full width table + + (individual columns resize) +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
First NameLast NameAgeTotalDiscountDate
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ Resized values for this table are saved to session storage - values are lost once the browser is closed. + +
+ +

+ Overflow table + + (text-overflow: ellipsis set) +

+
+ + + + + + + + + + + + + + + + + + +
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+
+ +
+ +

+ Full width table + + (use shift to force last column to resize) +

+ + + + + + + + + + + + + + + + + + +
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ Resized values for this table are saved to local storage - values are not lost once the browser is closed. +
+

+ +

Page Header

+
+
<!-- blue theme stylesheet with additional css styles added in v2.0.17 -->
+<link rel="stylesheet" href="../css/theme.blue.css">
+<!-- tablesorter plugin -->
+<script src="../js/jquery.tablesorter.js"></script>
+
+<!-- tablesorter widget file - loaded after the plugin -->
+<script src="../js/jquery.tablesorter.widgets.js"></script>
+
+ +

CSS

+
+

+	
+ +

Javascript

+
+

+	
+ +

HTML

+
+
<!-- The Age column is set to not be resizable -->
+<table class="tablesorter" style="width:auto">
+  <thead>
+    <tr>
+      <th>First Name</th>
+      <th>Last Name</th>
+      <th class="resizable-false">Age</th>
+      <th>Total</th>
+      <th>Discount</th>
+      <th>Date</th>
+    </tr>
+  </thead>
+  <tbody>
+    ...
+  </tbody>
+</table>
+
+ + + +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-savesort.html b/compare/thirdparty/tablesorter/docs/example-widget-savesort.html new file mode 100644 index 00000000..e67d6a74 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-savesort.html @@ -0,0 +1,200 @@ + + + + + jQuery tablesorter 2.0 - Save Sort Widget + + + + + + + + + + + + + + + + + + + + + +
+ +

+ NOTE! +

+
    +
  • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
  • +
  • Sort one or more columns, then reload the page to see that this widget remembers the last table sort.
  • +
  • Sort saving requires the new "$.tablesorter.storage()" function included with the "jquery.tablesorter.widgets.js" file (v2.1).
  • +
  • Because this widget uses jQuery's parseJson() function, it requires jQuery version 1.4.1+.
  • +
  • Added a saveSortReset method which only clears the stored information (v2.7.11).
  • +
+ +

Demo

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Account #First NameLast NameAgeTotalDiscountDiff
A43PeterParker289.9920.3%+3
A255JohnHood3319.9925.1%-7
A33ClarkKent1815.8944.2%-15
A1BruceAlmighty45153.1944%+19
A102BruceEvans56153.1923%+9
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankRatingTitleVotes
1.9.2The Shawshank Redemption (1994)734,327
2.9.2The Godfather (1972)548,857
3.9.0The Godfather: Part II (1974)346,072
4.8.9Pulp Fiction (1994)577,426
5.8.9The Good, the Bad and the Ugly (1966)229,564
+ +

Page Header

+
+
+<link rel="stylesheet" href="css/blue/style.css">
+<script src="js/jquery-latest.min.js"></script>
+<script src="js/jquery.tablesorter.min.js"></script>
+<script src="js/jquery.tablesorter.widgets.min.js"></script>
+
+

Javascript

+
+

+	
+

HTML

+
+

+	
+ + + +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-scroller.html b/compare/thirdparty/tablesorter/docs/example-widget-scroller.html new file mode 100644 index 00000000..335d1291 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-scroller.html @@ -0,0 +1,776 @@ + + + + + jQuery tablesorter 2.0 - Scroller Widget + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+
+ +

Notes

+
+
    +
  • In v2.28.5 a scrollerComplete event is now triggered on the table once the scroller has completed rendering.
  • +
  • In v2.22.2, +
      +
    • Add support for multiple tbodies in fixed columns: +
        +
      • *WARN* colspans within information only tbodies are still problematic!
      • +
      • If a colspan is to be used with fixed columns, then split it so that the colspan splits at the edge of fixed column; please see this comment for more details & a demo.
      • +
      +
    • +
    • The horizontal scrollbar no longer appears under the fixed column: +
        +
      • There is now a gap visible below the content when scrolled to the bottom.
      • +
      • This gap is filled by a div and can be styled by targeting the tablesorter-scroller-bar-spacer class name. See the css code block for an example.
      • +
      +
    • +
    • Removed: +
        +
      • The automatic setting of the widthFixed option to true.
      • +
      • Extra colgroup which were copied into each table clone.
      • +
      • Extra hidden elements in the fixed column.
      • +
      +
    • +
    • Updated RTL (right-to-left) support: +
        +
      • Changed the default class from tablesorter-scroller-rtl to ts-scroller-rtl, which is added to the table element to indicate where the fixed column is placed.
      • +
      • RTL support does require the direction: rtl css setting to be applied to the table wrapper.
      • +
      +
    • +
    • Fixed slow mousewheel scrolling when a fixed column is active in Firefox & older versions of Internet Explorer.
    • +
    • Fixed filtering of table content causing the fixed column to misalign; especially when few to no results are found.
    • +
    • Added compatibility to the following widgets: +
        +
      • pager
      • +
      • columnSelector
      • +
      +
    • +
    • Thanks to TheSin- for all the work he put into updating this widget... he really didn't like that scrollbar under the fixed column LOL.
    • +
    +
  • +
+ +
+

Older Notes

+
+
    +
  • In v2.22.0, +
      +
    • Horizontal scrollbar now only appears on overflow.
    • +
    • border-box is now applied to all tables with scroller widget applied.
    • +
    • Fixed columns no longer have pointer events disabled.
    • +
    • Fixed column vertical scrollbar is now hidden in IE9 & older.
    • +
    • Fixed tabbing through fixed column header cells & inputs.
    • +
    • The "setFixedColumnSize" can now be called with no set size to refresh the fixed column.
    • +
    • Add scroller_addFixedOverlay option which when true, adds an overlay on the fixed columns for styling.
    • +
    • Refresh column sizes after update.
    • +
    +
  • +
  • In v2.21.3 +
      +
    • * NOTE * Prior to v2.21.3, this widget would work with jQuery v1.4.4+, now it requires jQuery v1.7+.
    • +
    • The scroller widget was almost completely rewritten! The functionality is the same, but it now allows the adding of fixed columns!
    • +
    • Added scroller_fixedColumns which allows setting the number of fixed columns to add to the scroller (see options section for more details).
    • +
    • Added scroller_rowHighlight which adds a hover highlight class name to the row in both the fixed column and main table (see options section for more details).
    • +
    • Note: +
        +
      • Yes, there are some alignment & padding issues when using the jQuery UI theme with a fixed column, I'll look into resolving this; all other themes are nearly pixel perfect.
      • +
      • The fixed column widget includes extra css to prevent tbody cell content from wrapping (see the css code below); it was for demo purposes only, it is not a requirement.
      • +
      • This update does not include optimizations to allow this widget to work with very large tables; so please be conscious of this fact.
      • +
      • A lot of Firefox tweaks were needed to make it work! I need to investigate why the mousewheel scrolling is so slow (at least in Firefox for Windows) when a fixed column is included.
      • +
      • There were lots of other tweaks to the core and other widgets (e.g. zebra & filter widgets) to get these changes to work seamlessly, so please make sure to update everything!
      • +
      +
    • +
    +
  • +
  • In v2.21.1, columns now line up, especially while scrolling horizontally. It may not be pixel perfect, but it looks pretty good if table css "box-sizing" is set to "border-box".
  • +
  • In v2.21.0 +
      +
    • This widget was updated to include the tfoot rows.
    • +
    • To maintain the column widths across all copied tables: +
        +
      • The widthFixed options is now forcibly set to true; I apologize if this causes any inconvience.
      • +
      • This option adds a <colgroup> containing percentage width <col> elements to each section of the scroller.
      • +
      • If this option does cause issues, use css to set each column width as a percentage.
      • +
      +
    • +
    +
  • +
  • In v2.17+ +
      +
    • The scroller widget will now work properly with predefined column widths.
    • +
    • Shrinking the browser window will now hide the header overflow.
    • +
    • Horizontal scrolling of the table body will now properly horizontally scroll the header.
    • +
    • Changed the default scroll bar width from 17 to 18.
    • +
    • Added scroller_upAfterSort option.
    • +
    +
  • +
+
+
+
    +
  • This widget can not be applied to the original plugin and requires jQuery version 1.7+ to function properly; if you need to make it work with older versions of jQuery and the plugin, please use this version of the widget.
  • +
  • This widget was originally written by Connell & Associates, Inc. and is dual-licensed under the MIT and GPL licenses. It has been modified to work with tablesorter version 2.9+.
  • +
+
+ +

Incompatibilities

+
+ The scroller widget is known to be currently incompatible with these widgets (there may be more): +
    +
  • Editable
  • +
  • Math
  • +
  • Reorder
  • +
  • Resizable
  • +
  • StickyHeaders
  • +
+ There is partial compatibility with these widgets: + +
+ +

Options

+
+

Scroller widget defaults (added inside of tablesorter widgetOptions)

+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
300Set the height of the scroll window in pixels (v2.25.5) +
+
+ As of v2.25.5, setting this option to an empty string or zero will make the scroller full height. +
+
nullSet the width of the scroll bar in pixels (v2.19.0) +
+
+ As of v2.19.0, this option's default was changed to null because internal code was added to detect the scroll bar width which changes dramatically depending on the browser window zoom level.
+
+ If you are using a custom scroll bar plugin, this option will still accept a scroll bar width value which overrides the scroll bar width auto-detection. +
+
trueWhen true, the scroller automatically scrolls the inner window back to the top after sorting. +
+
+ Set this option to false to prevent this behaviour, or to stop the window from scrolling after interacting with a table cell (e.g. clicking on a checkbox); new in v2.17.3 +
+
trueBring the header into view while scrolling. +
+

When true, this option makes the table header jump into view when the table body is not scrolled to the top and while scrolling up the page. It's not perfect, but it works. Disable it as desired.

+

* To see the difference, toggle the button in the demo below, then scroll down & up the page using a mouse wheel with the cursor at the horizontal center of the page and about 100 pixels from the top, so the cursor is within the table body.

+
+
0This allows setting the number of fixed columns to add to the scroller (v2.21.3; v2.22.2). +
+

If the content is set a RTL direction, add a class name of ts-scroller-rtl (name set in $.tablesorter.css.scrollerRtl) to the table to align the fixed header on the right.

+

default value changed in v2.22.2 because tablesorter is set up to assume a theme name has already been added to the table when it encounters a class name starting with tablesorter-; if the original tablesorter-scroller-rtl class were added, the theme option setting would be ignored (because a theme named scroller-rtl is already set), and would require the developer to add the class name to the table (e.g. tablesorter-blue).

+

To change this method internally would require a breaking change where all css files would need to be updated and all theme classes would start with tablesorter-theme-; this will be planned for the Abelt update.

+
+
falseSetting this to true will add a fixed overlay which can be used for styling (v2.22.0). +
+

A class name of "tablesorter-scroller-fixed-panel" is added to the overlay.

+

Here is are two examples:

+

Semi-transparent overlay*

+
div.tablesorter-scroller-fixed-panel {
+  background-color: rgba( 0, 0, 0, 0.4);
+  z-index: 2;
+  opacity: 0.15;
+  margin-top: 10px;
+  margin-bottom: 15px;
+}
+

Black border*

+
div.tablesorter-scroller-fixed-panel {
+  margin-top: 10px;
+  margin-bottom: 15px;
+  border-right: 2px solid black;
+}
+

* Demos by TheSin- from issue 887.

+

*WARNING* interacting with elements under this overlay requires that the browser supports pointer-events.

+
+

*NOTE* Personally, I would not recommend setting this option to true because of the interference from the overlay - the following css will add a border to the fixed column using css3 :after to acheive the same effect.

+
/* add border to right side (LTR pages) of fixed column */
+.tablesorter-scroller-fixed:after {
+  content: '';
+  border-right: 2px solid black;
+  width: 2px;
+  position: absolute;
+  top: 0;
+  bottom: 0;
+  z-index: 2;
+  /* change this to zero for non-jquery ui themes; and use "left" here for RTL pages */
+  right: -1px;
+  /* match the margins set to the table to keep the border the same height as the table */
+  margin: 10px 0 15px;
+}
+
+
"hover"Set this to a class name to use when hovering over a fixed column row (v2.21.3). +
+

This option adds a hover highlight class name to the hovered row in both the fixed column and main table to ensure the hover highlight shows up in both areas.

+

If this highlighting causes unnecessary lag, it can be disabled by setting this option to an empty string.

+

All current themes have been updated to use this class name for row highlighting; adjust any custom themes as necessary.

+
+
Deprecated/Removed Options
"s_"This option contains a prefix string which is added to a random number (Removed). +
+
+ This option was been completely removed in v2.18.0 as the id is now obtained from the unique namespace. +
+
+
+ +

Methods

+
+

Change Fixed Column Size Dynamically

+
+ Set the number of fixed columns on any table with a scroller widget applied as follows: +
$( 'table' ).trigger( 'setFixedColumnSize', size );
+

Where size is a value between zero and one less than the total number of columns.

+ If size is: +
    +
  • A non-integer, it will be rounded down.
  • +
  • Zero or less, no fixed columns will be applied.
  • +
  • Undefined, the current number of fixed columns will be refreshed.
  • +
+
+
+ +

Events

+
+

scrollerComplete

+
+ A scrollerComplete event (added v2.28.5) is triggered on the table after the scroller widget has completed updating (including frozen columns). This event is triggered after an updateComplete event as the widget binds to that event to update the table. +
$( 'table' ).on( 'scrollerComplete', function() {
+  // do something
+} );
+
+
+ +

RTL Support

+
+ Two things are required to get the fixed column properly aligned on the right side: +
    +
  1. + A class of ts-scroller-rtl must be added to the table +
    <table class="ts-scroller-rtl">...</table>
    + the reason for the weird naming ts instead of tablesorter is because with a class name of tablesorter-scroller-rtl, the code will think you're trying to add a theme named scroller-rtl. +

    If you want to change this class name, use the following code before the document ready function:

    +
    $.tablesorter.css.scrollerRtl = "ts-scroller-rtl";
    +
  2. +
  3. + Whatever element is wrapping the table needs to be set with the css direction:rtl. So if your entire page (body is set, that will work just fine. If you only want the table to be set as rtl, then set this css on the scroller's wrapper: +
    .tablesorter-scroller { direction: rtl; }
    +
  4. +
+ Here is a jsFiddle demo. +
+ +
+ +

CSS

+
+

+	
+ +

Javascript

+
+

+	
+ +

Demo

+

Fixed columns | Full-width | Half-width

+ + Choose Theme: +
+
+
+ scroller_jumpToHeader : true (see the note above)* +
+ scroller_upAfterSort : true +

+ +

+ Fixed Column Demo
+ (Shrink the browser window if the horizontal scrollbar is not visible) +

+ +
+ Set number of fixed columns: 2
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
IndexFirst NameLast NameIDAddressStateZipTelephoneEmailNotes
IndexFirst NameLast NameIDAddressStateZipTelephoneEmailNotes
1SherryMills2233792 Sit RdNE69836(627) 124-8760DRamirez@massa.lysagittis amet mattis facilisis vitae molestie nec dolor id sed
2MarkellaLessenberry8955979 Sit LnVA97761(768) 233-5399SPeltier@pulvinar.netelementum nullam lacus elit magna libero sed dolor pulvinar orci
3LeeTrenkelbach7195656 Adipiscing AveID30972(149) 293-0691LSheppard@lectus.netvestibulum at rutrum molestie convallis vestibulum nec egestas consequat vitae
4SherryeLlc9082348 Pharetra StUT50740(772) 682-4268MAltar@turpis.iosit elit consequat amet elit consectetur vestibulum vel quis consequat
5JuanitoLindall5345181 Lacus LnID71689(528) 174-6999CKeen@augue.lydui porttitor vestibulum mattis ipsum nunc et morbi dolor ipsum
6AndreFuller7168224 Pulvinar AveWY12382(654) 593-0007AMelvin@sed.netdolor curabitur nec molestie lacus odio id velit sed neque
7SajidCattanach9973762 Ac RdWA16719(425) 909-2771BMaas@ac.orgmorbi tincidunt ac sollicitudin nec ac orci vitae donec non
8HeidiRush7381425 Porta RdSD14708(341) 095-7183DSchrag@sapien.netnullam sed aliquam turpis et sagittis libero sed tellus dui
9GaneshComeau3852641 Hendrerit DrMT66612(201) 977-9765SBurks@ipsum.lysit vestibulum ac pretium eget augue sit augue sagittis tellus
10RegineMarina4796204 Ac RdWI55667(824) 223-9924VPeschke@pharetra.orgamet fringilla vitae tortor vestibulum egestas eget eget amet sed
11WichayaThomas487861 Risus DrKY63224(599) 823-8970BMohamed@adipiscing.lyplacerat dolor pulvinar nec morbi amet porta sagittis lacus magna
12DouglassHolliday4888829 Sit AveIA86970(113) 897-8276CLieb@convallis.iodolor magna id amet lorem eros ipsum magna non aliquam
13PrinceSchwartzberg7901757 Sit CtIL94739(299) 471-3039DFavus@quis.orgmagna tincidunt ipsum hendrerit ac vitae tortor consequat magna elit
14LolaHensley4868159 Dui RdMN88877(788) 031-7392MBradley@scelerisque.orgsuspendisse donec placerat nec suspendisse lacus ipsum elementum at lorem
15DainiusFinn3556072 Massa DrVT75787(638) 345-2650MWilcox@velit.lymorbi pretium dui dolor elit sed non vestibulum sit dui
16AishaSchuhmann3289709 Sagittis LnND44161(147) 281-5251GDech@mi.commassa sed placerat libero egestas at massa sagittis libero orci
17MariaPeacock2692241 Amet AveMO81339(656) 079-5964POliva@tincidunt.orgac hendrerit molestie ipsum facilisis massa odio fringilla hendrerit neque
18OtiliaDenbesten7781965 Nunc CtSC98692(985) 745-0452SWieber@porttitor.netaliquam orci odio id ac magna magna aenean sagittis lectus
19JianSanchez7966439 Sit LnOR24879(168) 754-6725AVrtis@porttitor.netsapien ipsum nullam pretium quis tincidunt tincidunt sit mattis porttitor
20TannyWieland8147331 Et RdSD65216(414) 322-1606ACasariego@et.orgodio augue in id lacus tincidunt morbi tempor orci tincidunt
21AlejandroSidaway9958125 Placerat RdVA98301(202) 112-1194RHaag@nullam.lylacus aliquam et sagittis etiam aenean lorem ipsum non facilisis
22YolandaSwanson530276 Amet CtVT42999(621) 796-4436TDenard@lectus.govlacus vitae quis dui orci ipsum odio pulvinar et nunc
23AzraFilleul443499 Sagittis DrOK59700(485) 530-0532LSwetland@consectetur.netmagna mattis sollicitudin placerat vel facilisis sed ac non et
24CassandraLabrucherie6066849 Tellus CtSC44245(960) 044-9381WLafave@lectus.ioac nullam magna ante sapien tellus tincidunt eros magna sagittis
25TheodoreSaver1505 Tincidunt AveIL58210(152) 001-4978KTorres@sed.ioac tincidunt nec rutrum pulvinar id aliquam placerat pharetra placerat
26SusiMurphy9001614 Hendrerit StAZ80212(532) 030-5050ZRingling@sit.ioquis odio et sapien sit amet eros et augue turpis
27CorazonLawrence5773571 Facilisis RdCA35195(318) 200-2692REckhart@scelerisque.iotincidunt sit dolor mi donec magna massa vel suspendisse placerat
28KhosrowFisher8024323 Tortor LnAK38949(493) 277-9670FHarmati@magna.lylectus tortor ac ipsum sagittis lacus dolor ac fringilla nec
29MelgemaLopez8931738 Vestibulum LnMS32638(953) 255-2707ABurgin@tortor.lymolestie at vel at placerat pharetra consequat dolor nunc placerat
30ChunLester9289681 Vestibulum CtND21333(474) 719-0476ETierney@pulvinar.govvestibulum ipsum tincidunt placerat eros turpis morbi ipsum sit malesuada
31DwayneBalasubramanian4183129 Pharetra DrSD57558(457) 589-4620ESyner@aenean.comamet sit nec amet lectus augue consequat consectetur vel sed
32JoshuaWilliamson7026313 Vestibulum StNJ39057(346) 084-7444VFeldman@elit.lysit tempor at lacus libero lorem sit nullam tempor aliquam
33ShawnDartmann1244892 Aliquam RdIA17745(500) 390-3779RNesbitt@massa.netsit sapien magna libero augue consectetur fringilla orci ac tortor
34RupaHelgren5945944 Sed AveCO96680(174) 841-4348FShebish@morbi.netat elementum neque ac in consequat elementum magna vitae mattis
35GiaMarks6352519 Convallis DrGA66324(188) 657-9140SForsyth@adipiscing.govpharetra pulvinar amet dui elementum suspendisse et odio tellus amet
36TyethaAhn5439900 Eros CtPA53390(482) 686-6141LHobbs@non.iopulvinar lorem quis fringilla eget velit sed augue rutrum augue
37GregoryChristine527751 Ipsum RdAK44543(258) 666-2073HCrooker@donec.commassa vitae dolor sollicitudin consectetur sollicitudin malesuada massa velit egestas
38YelenaShanoski6461612 Donec AveKY30729(905) 301-3407JMcmenamin@massa.govsapien rutrum adipiscing et sed sollicitudin vestibulum pharetra at neque
39ThursdayHelm6665568 Nec RdNE94390(487) 403-7019RMorin@sed.orgrisus hendrerit sollicitudin vestibulum placerat lorem eros consequat lorem magna
40MelindaOliva5256973 Rutrum CtIA38519(964) 234-5172FStephens@in.comdolor lacus nec odio in etiam facilisis lorem ipsum et
41LatashaSlocum2108374 Fringilla StNV23913(678) 453-4263JWelchert@id.govdonec massa dolor elementum scelerisque risus lacus id lorem in
42KennethZazzara2693364 Tempor DrMO30642(874) 633-3017TGarayan@tincidunt.lysollicitudin nullam egestas aenean molestie velit nec sed tincidunt scelerisque
43FrancineDaniels3662141 Amet RdOH90786(856) 781-7309SIngham@pretium.lyegestas amet vel ac pretium lacus nec tortor facilisis et
44MarieKrebs1244229 Sit LnDE41786(572) 023-1295EClagg@curabitur.iolacus elit sagittis quis nec vestibulum malesuada mattis morbi etiam
45StanleyZehnacker4564610 Odio StVT70223(257) 234-6755HPennell@morbi.govvel aliquam mattis libero sed pharetra donec malesuada pharetra lectus
46FurdellaToffoli9518366 Placerat RdAZ20381(740) 422-8726YEarls@morbi.netnon aliquam neque tortor egestas pulvinar tempor amet molestie lorem
47TalathaNye8086884 Dolor DrMS97282(589) 157-7730DAcosta@rutrum.govipsum turpis vestibulum at convallis sit neque elementum sed adipiscing
48BrentVollrath4795362 Eget DrMO74989(664) 642-4525YMarina@sollicitudin.netmassa elementum elit pretium malesuada mi vestibulum non aliquam neque
49MelissaQuintana4051302 Lacus DrPA38853(354) 903-9363AMarrinson@lacus.iomorbi quis neque placerat malesuada tincidunt morbi risus at tortor
50GuadalupeEseltine2065495 Rutrum CtAR37546(540) 500-5816TLester@morbi.comporttitor sit massa ipsum ac massa ac hendrerit vitae malesuada
+ +

Full-width demo

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameMajorSexEnglishJapaneseCalculusGeometry
NameMajorSexEnglishJapaneseCalculusGeometry
Student01Languagesmale80707580
Student02Mathematicsmale908810090
Student03Languagesfemale85958085
Student04Languagesmale6055100100
Student05Languagesfemale68809580
Student06Mathematicsmale1009910090
Student07Mathematicsmale85689090
Student08Languagesmale100909085
Student09Mathematicsmale80506575
Student10Languagesmale8510010090
Student11Languagesmale8685100100
Student12Mathematicsfemale100757085
Student13Languagesfemale1008010090
Student14Languagesfemale50455590
Student15Languagesmale953510090
Student16Languagesfemale100503070
Student17Languagesfemale801005565
Student18Mathematicsmale30495575
Student19Languagesmale68908870
Student20Mathematicsmale40454080
Student21Languagesmale5045100100
Student22Mathematicsmale1009910090
Student23Languagesfemale85808080
student23Mathematicsmale8277079
student24Languagesfemale100911382
student25Mathematicsmale22968253
student26Languagesfemale37295659
student27Mathematicsmale86826923
student28Languagesfemale4425431
student29Mathematicsmale77472238
student30Languagesfemale19352310
student31Mathematicsmale90271750
student32Languagesfemale60753338
student33Mathematicsmale4313715
student34Languagesfemale77978144
student35Mathematicsmale5815195
student36Languagesfemale70617094
student37Mathematicsmale6036184
student38Languagesfemale6339011
student39Mathematicsmale50463238
student40Languagesfemale5175253
student41Mathematicsmale43342878
student42Languagesfemale11896095
student43Mathematicsmale48921888
student44Languagesfemale8225973
student45Mathematicsmale91733739
student46Languagesfemale481210
student47Mathematicsmale8910611
student48Languagesfemale90322118
student49Mathematicsmale42494972
student50Languagesfemale56376754
+ +

Half-width demo

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Account #First NameLast NameAgeTotalDiscountDiff
Account #First NameLast NameAgeTotalDiscountDiff
A43PeterParker289.9920.3%+3
A255JohnHood3319.9925.1%-7
A33ClarkKent1815.4944.2%-13
A11BruceAlmighty45153.1944%+19
A102BruceEvans56153.1923%+9
A23MikePeters225.6920.3%+2
A55LeslieKent3315.9925.1%-3
A3FrankMint4412.5944.2%-12
A21JoeThomas4515.2544%+12
A12TessEvans6613.5923%+4
A21PeterDunn122.9921.1%+2
A33HarryJones1319.4922.2%-6
A13JohnJames1613.8942.1%-13
A71NickParker4513.8944%+29
A21CharlesDunn1915.4922%+3
A42TeraJones8314.1913%+5
A51PaulDaniels765.9920%+1
A36HarveyPhillips8422.5023%+2
A5JamesMicheal1112.9944.4%-3
A1NormaHarry4312.3941%-9
A91CharleyDuncan2214.4412%-1
+
+ +
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-sort-tbodies.html b/compare/thirdparty/tablesorter/docs/example-widget-sort-tbodies.html new file mode 100644 index 00000000..643c8854 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-sort-tbodies.html @@ -0,0 +1,447 @@ + + + + + jQuery tablesorter 2.0 - Sort multiple tbodies by primary row + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+
+ +

Notes

+
+
    +
  • In v2.27.9, added `sortTbody_lockHead` option. Thanks to Chris Rogers!
  • +
  • This widget (v2.22.2) allows the sorting of tbodies (not rows) based on a specified row set by the sortTbody_primaryRow option.
  • +
  • This widget replaces the cssInfoBlock setting: +
      +
    • The value is replaced with the value from sortTbody_noSort option so as to include info-only tbodies in the tbody sort.
    • +
    • Due to this modification, tbodies with colspan and rowspan cells may not sort or filter as expected, so it would be best to avoid colspan and rowspan cells in the primary row!
    • +
    +
  • +
  • This widget also modifies the serverSideSorting option: +
      +
    • This value is set to true to disable tbody internal row sorting.
    • +
    • This value is set to false (default) when the sortTbody_sortRows widget option is true.
    • +
    • This modification should not dramatically affect other widgets; at least none that I can think of right now.
    • +
    +
    +
  • +
  • In these demos, +
      +
    • Primary Rows are styled with a light yellow background as an indicator.
    • +
    • The primary row color difference is important in the third table when the sorting of internal rows is enabled (sortTbody_sortRows is true).
    • +
    • The first two tables: +
        +
      • Primary rows are clickable. Clicking on them toggles the sibling rows (in the first table), or child rows (in the second table).
      • +
      • Primary rows have a dark top border to add a visual separation of each tbody.
      • +
      • *NOTE* when filtering the content of the first table, sibling rows that do not match the query will be hidden. Toggling their view is only possible because the "filtered" class (used by the filter widget & set by the filter_filteredRow widget option) is toggled.
      • +
      +
    • +
    +
    +
  • +
  • *ALERT* This widget requires tablesorter v2.22.2+ and jQuery 1.4+
  • +
+
+ +

Options

+
+
+ TIP! Click on the link in the option column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
falseWhen true, the primary row will be moved to the top of the sorted tbody. +
+
+ Setting this option will also require setting the sortTbody_sortRows option to true. See the Sort tbodies and lock the header (primary) row demo below. +
+
nullSet this option to a string containing a jQuery selector pointing to the primary row to use for tbody sorting. +
+
+

If a sortable tbody does not contain a primary row, or the primary row is not found, it will be sorted at the bottom of the table.

+

In these demos, all primary rows have a class name of "main", so this option is then set as follows:

+
sortTbody_primaryRow : '.main'
+

*NOTE* This will accept any jQuery selector, so it is possible to target multiple classes '.main, .primary', but only the first found cell in the sorted column will be used when sorting the tbody.

+ *WARNING* This widget was not designed to deal with colspan or rowspan within the primary row, so it is best to avoid them. +
+
sortTbody_sortRowsfalseWhen true, all rows within each tbody will also be sorted; the primary row is included!
sortTbody_noSort'tablesorter-no-sort-tbody'Add the class name from this option to any tbodies that are to remain static. They will not be sorted.
+
+ +
+ +

+

Demo

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sort info-only tbodies by their primary row
RankFirst NameLast NameSuperhero Name
3BruceWayneBatman
Batman is the superhero protector of Gotham City, a man dressed like a bat who fights against evil and strikes terror into the hearts of criminals everywhere. In his secret identity he is Bruce Wayne, billionaire industrialist and notorious playboy. Although he has no superhuman powers, he is one of the world's smartest men and greatest fighters. His physical prowess and technical ingenuity make him an incredibly dangerous opponent. He is also a founding member of the Justice League and the Outsiders.
4HalJordanGreen Latern
Hal Jordan is the most well-known Green Lantern. He was the first earthman ever inducted into the Green Lantern Corps, and has been heralded as possibly the greatest Green Lantern of all time. Green Lantern is also a founding member of the Justice League of America.
2ClarkKentSuperman
Superman, also known as the Man of Steel, is one of the most powerful superheroes in the DC Universe. His abilities include incredible super-strength, super-speed, invulnerability, freezing breath, flight, and heat-vision.
5CharlesXavierProfessor X
Charles Francis Xavier was born in New York City to the wealthy Brian Xavier, a well-respected nuclear scientist, and Sharon Xavier. After Brian died in an accident, his science partner Kurt Marko comforts and marries the grieving Sharon. When Xavier's telepathic mutant powers emerge, he discovers Kurt cares only about his mother's money.
1SusanRichardsInvisible Woman
Susan's primary power deals with light waves, allowing her to render herself and others invisible.
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sort tbodies by their primary row (using child rows for extra info)
RankFirst NameLast NameSuperhero Name
3BruceWayneBatman
Batman is the superhero protector of Gotham City, a man dressed like a bat who fights against evil and strikes terror into the hearts of criminals everywhere. In his secret identity he is Bruce Wayne, billionaire industrialist and notorious playboy. Although he has no superhuman powers, he is one of the world's smartest men and greatest fighters. His physical prowess and technical ingenuity make him an incredibly dangerous opponent. He is also a founding member of the Justice League and the Outsiders.
4HalJordanGreen Latern
Hal Jordan is the most well-known Green Lantern. He was the first earthman ever inducted into the Green Lantern Corps, and has been heralded as possibly the greatest Green Lantern of all time. Green Lantern is also a founding member of the Justice League of America.
2ClarkKentSuperman
Superman, also known as the Man of Steel, is one of the most powerful superheroes in the DC Universe. His abilities include incredible super-strength, super-speed, invulnerability, freezing breath, flight, and heat-vision.
5CharlesXavierProfessor X
Charles Francis Xavier was born in New York City to the wealthy Brian Xavier, a well-respected nuclear scientist, and Sharon Xavier. After Brian died in an accident, his science partner Kurt Marko comforts and marries the grieving Sharon. When Xavier's telepathic mutant powers emerge, he discovers Kurt cares only about his mother's money.
1SusanRichardsInvisible Woman
Susan's primary power deals with light waves, allowing her to render herself and others invisible.
+ +
+
+ + +sortTbody_sortRows: (sort internal rows) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sortable & non-sortable tbodies
#IDFirstLastStateInfo
#IDFirstLastStateInfo
1337DeniseHarrisCTlectus tortor vestibulum
2268BrienBarrowKYipsum aenean eros
3665MikelAbsalomFLamet vestibulum adipiscing
4540LaShondaBuikemaSDpulvinar at consectetur
Tbody "tablesorter-no-sort-tbody" spacer 1
5212LeifChoateUTporttitor mattis sit
6367ErickaRagusaMAat sollicitudin hendrerit
7136WaddieMaynardNHsollicitudin ac mi
8305KenWeissmannAKmagna sit at
Tbody "tablesorter-no-sort-tbody" spacer 2
9983PetrKunselmanWIamet convallis eros
10306MelanieNaborsMAtincidunt risus porttitor
11345EmilCorsonMOdui aliquam aliquam
12335TheodoreBrillWVsit pulvinar eros
Tbody "tablesorter-no-sort-tbody" spacer 3
13430NishalSmithDEtortor tincidunt porttitor
14470SherylScheppeIDsed massa hendrerit
15795ChristopherPollardCTsed lacus adipiscing
16400RaymondJessicaINmi vestibulum amet
Tbody "tablesorter-no-sort-tbody" spacer 4
17927DonovanJoslinMNamet augue aliquam
18882NingMccollumKSsed egestas vestibulum
19772SarahRadistDEipsum odio dui
20975AishaBadertscherCTmi nec massa
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sort tbodies and lock the header (primary) row
DealerJanFebMarAprMayJunJulAugSepOctNovDec
Dealer 48 JanFebMarAprMayJunJulAugSepOctNovDec
Site 3778419733136166158581701549225
Site 1815118711291155748192521403109
Site 251772412516811612212314113104166
Site 4131461885637162160178733999165
Site 2087206561871391715975108119171
Dealer 29 JanFebMarAprMayJunJulAugSepOctNovDec
Site 345251881213163189150635750189
Site 1812261211699013165741887818082
Site 25721518338121235549211318538
Site 4796253173181939584857129
Site 2033511751041251231566615984117
Dealer 62 JanFebMarAprMayJunJulAugSepOctNovDec
Site 385561687811510481511442010560
Site 1818928768816971941443147192176
Site 25156145132811931301029196489937
Site 415453158311191843291698746
Site 2013194182174481771571913485176
Dealer 25 JanFebMarAprMayJunJulAugSepOctNovDec
Site 31755819618688464109187167109104
Site 18109253170163116361254357179194
Site 2510913169411662038175691801768
Site 4129281376138701562001411787670
Site 20365691621141791011575614913485
+ +
+ +

Page Header

+
+
<link rel="stylesheet" href="../css/theme.blue.css">
+<script src="js/jquery-latest.min.js"></script>
+<script src="../js/jquery.tablesorter.js"></script>
+
+<!-- Tablesorter: widgets (optional) -->
+<script src="../js/jquery.tablesorter.widgets.js"></script>
+<script src="../js/widgets/widget-sortTbodies.js"></script>
+
+ +

CSS

+
+

+	
+

Javascript

+
+

+	
+

HTML

+
+

+	
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-sort-to-hash.html b/compare/thirdparty/tablesorter/docs/example-widget-sort-to-hash.html new file mode 100644 index 00000000..55a8853b --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-sort-to-hash.html @@ -0,0 +1,594 @@ + + + + + jQuery tablesorter 2.0 - Sort2Hash Widget (Beta) + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+ +
+ +

Notes

+
+
    +
  • In v2.24.4, +
      +
    • Added a getParam (get URL parameter) & a removeParam utility function to help deal with parameters in the URL hash. See the "Functions" section below for more details.
    • +
    • Alex Weissman has also provided code for a custom hash transformation in this gist; this code sets hash parameters that include the table id & named columns, e.g. &filter[table0][first_name]=foobar.
    • +
    +
  • +
  • In v2.24.0, lots of changes were made: +
      +
    • Removed sort2Hash_useHeaderText and sort2Hash_processHeaderText options.
    • +
    • Added sort2Hash_headerTextAttr option to replace the above two options.
    • +
    • Added sort2Hash_encodeHash, sort2Hash_decodeHash and sort2Hash_cleanHash options to allow custom value to hash processing.
    • +
    • This widget now works with the pager & filter, so the hash now includes pager current page, pager page size and current filters in the hash.
    • +
    • Lots of internal tweaks.
    • +
    +
  • +
  • Added v2.22.4. Instead of using the saveSort widget, this widget updates the hash tag to allow saving & sharing a sort applied to a tablesorter table.
  • +
  • Sort the tables in the demo below. Notice the changes made to the location hash, then reload the page to have the hash applied to the tables.
  • +
  • This widget requires jQuery version 1.7+.
  • +
  • This widget does NOT work with tablesorter v2.0.5.
  • +
+
+ +

Options

+
+

Sort2Hash widget default options (added inside of tablesorter widgetOptions)

+
+ TIP! Click on the link in the option column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
sort2hash_hash'#' + The hash should always be there. This option was added to allow setting extra hash parameters and/or hashbang or whatever. +
',' + Change the hash separator using this option. There are some limitations. +
+
+ In the location hash, the sort parameters are added as &tableID=column,direction, ... ,column,direction (no spaces). This option allows changing the column-direction separator, a comma by default, into the chosen separator. +

*NOTE* Do not set this option to use a hash (#), ampersand (&) or equal sign (=) as it will interfere with how the hash parameters are set up.

+
+
null + Set an ID here to override the table id attribute. +
+
+ In the location hash, the sort parameters are added as &tableID=column,direction, ... ,column,direction (no spaces). The tableID is set by this option. +

This option setting is prioritized over the actual table ID attribute. If neither are set, the tableID will be set as the table's zero-based index on the page.

+
sort2Hash_tableID > table.id attribute > table index
+
+
'data-header' + Data attribute on header cell containing alternate column identifier added to location hash. +
+
+ This option replaces both sort2Hash_useHeaderText and sort2Hash_processHeaderText options. +

The contents of this attribute will be used in the hash to identify a sorted column.

+

Use the header attribute as follows:

+
<th data-header="first">First Name</th>
+
+
[ 0, 1 ] + Set the direction text shown in the URL. +
+
+ Only the first two values will be used from this array. The first value is assigned to ascending sorts and the second is assigned to descending sorts. +

Use the option as follows:

+
sort2Hash_directionText : [ 'asc', 'desc' ]
+ *NOTE* When converting the hash into a value, if the direction hash does not match the second value ('desc' in the example above), it will fallback to an ascending sort no matter what text in contained within the first value. +
+
sort2Hash_overrideSaveSortfalseif true, the hash sort will override any stored sort (saveSort widget).
falseChange how the browser history is managed (v2.29.0) +
+

If true, all hash changes are not saved to browser history, so when the user presses the back arrow, they will be returned to the previous page.

+ If false, all hash changes are preserved in the browser history, so when the user presses the back arrow, the hash will show the previous state, but the table won't update unless the page is refreshed. +
+
null + Add a function to create a custom hash (v2.24.4). +
+
+ The following example is a duplicate of the internal code that encodes the hash. Adapt to fit your needs: +
sort2Hash_encodeHash : function( config, tableId, component, value, rawValue ) {
+  // config = table.config settings
+  // tableId = processed table ID
+  // component: 'sort', 'page', 'size' or 'filter'
+  // value = string value that has encodeURIComponent applied
+  // rawValue = value ( array or number )
+  // return false to let the widget encode the string
+  return '&' + component + '[' + tableId + ']=' + value;
+}
+ Return a string to add to the window.location.hash directly. +

If this function returns false, then the internal code will encode the hash for that component. This was done in case only one parameter needs special handling:

+
sort2Hash_encodeHash : function( config, tableId, component, value, rawValue ) {
+  // only the filter gets special handling
+  if ( component === 'filter' ) {
+    // if working with rawValue, make sure to use
+    // encodeURIComponent on the results
+    var newValue = encodeURIComponent( rawValue.join( '--' ) );
+    return tableId + 'filter=' + newValue;
+  }
+  return false;
+}
+
+
null + Add a function to process a custom hash (v2.24.4). +
+
+ The following example is a duplicate of the internal code that decodes the hash. Adapt to fit your needs: +
sort2Hash_decodeHash : function( config, tableId, component ) {
+  // config = table.config settings
+  // tableId = processed table ID
+  // component: 'sort', 'page', 'size' or 'filter'
+  // return false to let the widget decode the hash
+
+  // $.tablesorter.sort2Hash.getParam( parameter, url ); function added in v2.24.4
+  // parameter = desired parameter to extract
+  // url (optional) = hash or href string to extract the parameter value from
+  return $.tablesorter.sort2Hash.getParam( component + '[' + tableId + ']' ) || '';
+}
+ Return the component value or an empty string. +

If this function returns false, then the internal code will attempt to decode the hash for that component. This was done in case only one parameter needs special handling:

+
sort2Hash_decodeHash : function( config, tableId, component ) {
+  // only the filter gets special handling
+  if ( component === 'filter' ) {
+    return $.tablesorter.sort2Hash.getParam( tableId + 'filter' ) || '';
+  }
+  return false;
+}
+
+
null + Add a function to remove a custom hash (v2.24.4). +
+
+ The following example is a duplicate of the internal code that removes unused values in the hash. Adapt to fit your needs: +
sort2Hash_cleanHash : function( config, tableId, component, hash ) {
+  // removeParam function added v2.24.4
+  return $.tablesorter.sort2Hash.removeParam( component + '[' + tableId + ']' );
+}
+ Return a string of the remaining components or an empty string. +

If this function returns false, then the internal code will attempt to clean the hash for that component. This was done in case only one parameter needs special handling:

+
sort2Hash_cleanHash : function( config, tableId, component, hash ) {
+  // config = table.config settings
+  // tableId = processed table ID
+  // component: 'sort', 'page', 'size' or 'filter'
+  // return false to let the widget decode the hash
+
+  if ( component === 'filter' ) {
+    // removeParam function added v2.24.4
+    return $.tablesorter.sort2Hash.removeParam( tableId + 'filter' );
+    /* ORIGINAL METHOD, use if the "getParam" function doesn't work on your custom parameter
+    var index,
+      result = [],
+      // regular expression to match the component & value
+      regex = new RegExp( tableId + 'filter=([^&]*)' );
+      // split hash into component parts
+      parts = ( hash || '' ).slice(1).split( '&' ),
+      len = parts.length;
+    // cycle through each component part
+    for ( index = 0; index < len; index++ ) {
+      // if the component doesn't match the regular expression...
+      if ( !regex.test( parts[ index ] ) ) {
+        // then save it
+        result.push( parts[ index ] );
+      }
+    }
+    // if we still have something left, join the components back together
+    // and return it so the next component can be processed
+    // we don't update the window.location.hash, or the page jumps to the top!
+    return result.length ? c.widgetOptions.sort2Hash_hash + result.join( '&' ) : '';
+    */
+  }
+  return false;
+}
+
+
Deprecated/Removed Options
false + This option has been removed in v2.24.0! It has been replaced by sort2Hash_headerTextAttr. +
+

If true, text from the header is used instead of a zero-based column index.

+ Please be aware that if the column text contains spaces or special characters, they will be encoded in the URL. So, "First £$€¤¥¢ Name" will become "First%20%C2%A3$%E2%82%AC%C2%A4%C2%A5%C2%A2%20Name". This would make the hash very difficult to read. +

Further processing of this header cell text can be done using the sort2Hash_processHeaderText function.

+
+
null + This option has been removed in v2.24.0! It has been replaced by sort2Hash_headerTextAttr. +
+

If the sort2Hash_useHeaderText option is true, a function here will further process the header cell text.

+ Use this function to perform any processing on the header cell text, as desired. +

At this point, the header cell text has not been encoded.

+

Here is one example:

+
sort2Hash_processHeaderText : function( text, config, columnIndex ) {
+  // remove all non-alphanumeric characters (including spaces)
+  return text.replace( /[^a-z0-9]/gi, '' );
+}
+ Another example: +
sort2Hash_processHeaderText : function( text, config, columnIndex ) {
+  // completely custom text to use for the hash
+  // this method assumes that the table layout is constant
+  // (i.e. columns are not added, removed or rearranged)
+  return [ 'first', 'last', 'age', 'total', 'disc', 'date' ][ columnIndex ];
+}
+
+
+
+ +

Functions

+
+

Sort2Hash utility functions (added to $.tablesorter.sort2Hash)

+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + +
FunctionDescription
+ Get URL Parameter (getParam) function +
+

This function will extract the value of the name passed to the function. Use it as follows:

+
// $.tablesorter.sort2Hash.getParam( name, hash );
+// name = parameter name (key)
+// hash = (optional) if undefined, it will get the string value from window.location.hash
+var hash = '&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10&filter[table-users][user_name]=ad',
+	result = $.tablesorter.sort2Hash.getParam( 'filter[table-users][user_name]', hash );
+// result = "ad";
+
+
+ Remove parameter from hash +
+

This function will remove the key & value from the passed hash. Use it as follows:

+
// $.tablesorter.sort2Hash.removeParam( name, hash );
+// name = parameter name (key)
+// hash = (optional) if undefined, it will get the string value from window.location.hash
+var hash = '&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10&filter[table-users][user_name]=ad',
+	result = $.tablesorter.sort2Hash.removeParam( 'filter[table-users][user_name]', hash );
+// result = "&sort[table-users][user_name]=asc&page[table-users]=1&size[table-users]=10";
+ *NOTE* This function does not update window.location.hash. +
+
+
+ +
+ +

Demo

+

Basic Usage

+ + + + + + + + + + + + + + + + + + +
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ This table shows up as "table4" because there is no ID assigned, and it is the fifth table (zero-based index) on the page. The first table is in the "Options" section. The second is actually the stickyheader table for "Options". The third & forth (sticky header) are in the "Functions" section. + +

Data Header (Using symbols for headers)

+ + + + + + + + + + + + + + + + + + +
First Name (^)Last Name (&)Age (#)Total ($)Discount (%)Date (*)
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ +
+

Pager (page & size)

+
+ First + Prev + + Next + Last + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameMajorSexEnglishJapaneseCalculusGeometry
NameMajorSexEnglishJapaneseCalculusGeometry
Student01Languagesmale80707580
Student02Mathematicsmale908810090
Student03Languagesfemale85958085
Student04Languagesmale6055100100
Student05Languagesfemale68809580
Student06Mathematicsmale1009910090
Student07Mathematicsmale85689090
Student08Languagesmale100909085
Student09Mathematicsmale80506575
Student10Languagesmale8510010090
Student11Languagesmale8685100100
Student12Mathematicsfemale100757085
Student13Languagesfemale1008010090
Student14Languagesfemale50455590
Student15Languagesmale953510090
Student16Languagesfemale100503070
Student17Languagesfemale801005565
Student18Mathematicsmale30495575
Student19Languagesmale68908870
Student20Mathematicsmale40454080
Student21Languagesmale5045100100
Student22Mathematicsmale1009910090
Student23Mathematicsmale8277079
Student24Languagesfemale100911382
Student25Mathematicsmale22968253
Student26Languagesfemale37295659
Student27Mathematicsmale86826923
Student28Languagesfemale4425431
Student29Mathematicsmale77472238
Student30Languagesfemale19352310
Student31Mathematicsmale90271750
Student32Languagesfemale60753338
Student33Mathematicsmale4313715
Student34Languagesfemale77978144
Student35Mathematicsmale5815195
Student36Languagesfemale70617094
Student37Mathematicsmale6036184
Student38Languagesfemale6339011
Student39Mathematicsmale50463238
Student40Languagesfemale5175253
Student41Mathematicsmale43342878
Student42Languagesfemale11896095
Student43Mathematicsmale48921888
Student44Languagesfemale8225973
Student45Mathematicsmale91733739
Student46Languagesfemale481210
Student47Mathematicsmale8910611
Student48Languagesfemale90322118
Student49Mathematicsmale42494972
Student50Languagesfemale56376754
+ +
+ +

Javascript

+
+

+	
+ +

HTML

+
+

+	
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-static-row.html b/compare/thirdparty/tablesorter/docs/example-widget-static-row.html new file mode 100644 index 00000000..921b5db2 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-static-row.html @@ -0,0 +1,282 @@ + + + + + jQuery tablesorter 2.0 - Static Row Widget + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+
+ +

Notes

+
+
    +
  • + In v2.19.1, the staticRow_event option was added. +

    +
  • +
  • This widget will only work in tablesorter version 2.8+.
  • +
  • The widget was modified from Tablesorter-Static-Row-Plugin by ascii-soup (MIT license).
  • +
  • It has been updated to work in tables with multiple tbodies, but not within information-only tbodies.
  • +
  • This widget was not tested, nor was it meant to be used with the pager.
  • +
  • Note This demo was updated to allow toggling of static rows.
  • +
+
+ +

Options

+
+

Group widget default options (added inside of tablesorter widgetOptions)

+
+ TIP! Click on the link in the option column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
staticRow_class".static" + Set a class name to use to lock a row in place, include a period. +
staticRow_data"static-index" + Set the jQuery data name to use with the row element. This data name saves the row index, and is only available as an option to avoid conflicts. +
staticRow_event"staticRowsRefresh" + Set the event that the widget listens for to perform a refresh. +
"row-index" + Use this data-attribute to set the desired static row location +
+
+ Set the desired location using either of these two methods: +
    +
  • + Use a zero-based index of the row +
    <tr data-row-index="5">...</tr>
    + Note setting this index to much more than the number of table rows ensures that it will stay at the bottom when adding new rows.
    +
    +
  • +
  • + Use a percentage if total rows within it's tbody +
    <tr data-row-index="50%">...</tr>
    +
  • +
+ These values take priority over the actual row index, so as shown in the "Single tbody" demo below, the header row is set to 50%, but initially, it is located at the top of the table.
+
+ If these values are changed dynamically, the static row location can be updated +
+
+
+ +

Methods / Events

+
+

Method

+ If adding or removing rows from the table, using any of the update methods will automatically refresh the static row indexes. Use the "Add Row" button below to test this.
+
+ To modify or refresh the static row indexing without updating, trigger a staticRowsRefresh event on the table: +
var $row = $("tr:contains('Iguana')");
+// move Iguana row down one (remember, this only works on static rows)
+$row.data('row-index', $row.data('row-index') + 1);
+$(table).trigger('staticRowsRefresh');
+ this allows moving a static row dynamically; try the Move "Ignuana" row buttons above the "Single tbody" demo. +

Event

+ A staticRowsComplete event is triggered after the static rows widget has completed moving the static rows back into place. Use it as follows: +
$(table).bind('staticRowsComplete', function(table) {
+	console.log('static rows applied to ' + table.id);
+});
+
+ +
+ +

Demo

+ Note Make any row static or normal by toggling the static class name using Ctrl + left click ( + click on Mac)
+

Single tbody

+

Move "Iguana" row:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Column 1Column 2Column 3
Column 1Column 2Column 3
D4Dog
E5Emu
F6Frog
G7Goat
A1Anteater
B2Bear
C3Cat
H8Horse
I9Iguana
J10Jellyfish
V22Vole
W23Walrus
X24Xantis
K11Koala
L12Lemur
Q17Quail
R18Rhino
S19Seal
M13Mink
N14Nightingale
O15Octopus
P16Pig
T20Tiger
U21Urchin
Y25Yak
Z26Zebra
Total CountLots! 
+ +

Multiple tbody

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankFirst NameLast NameAgeTotalDiscountDate
1Philip Aaron WongJohnson Sr Esq25$5.9522%Jun 26, 2004 7:22 AM
11AaronHibert12$2.995%Aug 21, 2009 12:21 PM
12Brandon ClarkHenry Jr51$42.2918%Oct 13, 2000 1:15 PM
111PeterParker28$9.9920%Jul 6, 2006 8:14 AM
21JohnHood33$19.9925%Dec 10, 2002 5:14 AM
013ClarkKent Sr.18$15.8944%Jan 12, 2003 11:14 AM
Second tbody
005BruceAlmighty Esq45$153.1944%Jan 18, 2021 9:12 AM
10AlexDumass13$5.294%Jan 8, 2012 5:11 PM
16JimFranco24$14.1914%Jan 14, 2004 11:23 AM
166Bruce LeeEvans22$13.1911%Jan 18, 2007 9:12 AM
100Brenda DexterMcMasters18$55.2015%Feb 12, 2010 7:23 PM
55DennisBronson65$123.0032%Jan 20, 2001 1:12 PM
9MarthadelFuego25$22.0917%Jun 11, 2011 10:55 AM
+ +

Javascript

+
+

+	
+

CSS

+
+

+	
+

HTML

+
+

+	
+ +
+
+ + + \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/example-widget-sticky-header.html b/compare/thirdparty/tablesorter/docs/example-widget-sticky-header.html new file mode 100644 index 00000000..190f79ea --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-sticky-header.html @@ -0,0 +1,719 @@ + + + + + jQuery tablesorter 2.0 - Sticky Header Widget + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+ +
+ +

Notes

+
+
    +
  • In v2.18.4, added a Magnific popup example to this page. Note Please take note of the extra css needed to keep the sticky header aligned.
  • +
  • In v2.18.0, +
      +
    • Nested tables with sticky headers now stack properly. See the new example added to the bottom of this demo page.
    • +
    • Added stickyHeaders_xScroll and stickyHeaders_yScroll widget options.
    • +
    • Any defined onRenderHeader function is now executed on the cloned sticky header table cells; the onRenderTemplate function is not! See the change log section for more details.
    • +
    +
    +
  • + +
  • Note To access the added sticky table content from your code without worrying about using the ID, you can use table.config.widgetOptions.$sticky.
  • +
  • Note Add the class name sticky-false to any header rows you don't want to become sticky (v2.1.18).

  • + +
  • You will need to modify the headerTemplate option to include the jQuery UI icon! See the example in the code (v2.7).
  • +
  • Scroll down the page to see the headers stick. Then sort the columns using the sticky headers!
  • +
  • Multiple rows in the header, including the filter row, will become sticky.
  • + +
  • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
  • +
  • Because of the limitations of Internet Explorer version 7 and older, this widget will not work.
  • + +
+
+ +

Options

+
+

stickyHeaders widget defaults (added inside of tablesorter widgetOptions)

+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
''Include any extra class name to be added to the sticky header table (v2.1; v2.18.0) +
+
+ Note prior to v2.18.0, this class was applied to the sticky table thead; after v2.18.0, the thead and caption are wrapped in a div that is made sticky, with this class name.
+
+ Modified in v2.11 so that "tablesorter-stickyHeader" class is always added and this option only adds additional classes. +
+
stickyHeaders_offset0Set this to a number (in pixels) or jquery selector targeting the position:fixed element in which to place the sticky header below while scrolling.
'-sticky'If the original table has an ID, then the value from this option is added to the end of the cloned table to maintain a unique ID (v2.9). +
+
    +
  • It contains a suffix to add to any table id.
  • +
  • Its default value is -sticky
  • +
+
+
stickyHeaders_addResizeEventtrueWhen true, the $.tablesorter.addHeaderResizeEvent function is applied to the table header cells so a "resize" event is triggered and the sticky headers widget can then properly resize the cloned table header cells to match the originals.
stickyHeaders_includeCaptiontrueIf false and a table caption exist, it won't be included in the sticky header.
stickyHeaders_zIndex2Adjust the zIndex of the stickyHeaders element as desired.
nullSet this option as a jQuery selector or object where the sticky header will be attached (v2.14.4). +
+
    +
  • Setting this option with either a jQuery selector string (".wrapper") or jQuery object ($(".wrapper")).
  • +
  • This option contains the target to which the sticky header will attach - see the second example below.
  • +
+
+
trueScroll table top into view after filtering (v2.16.2) +
+
+ This is needed when the user choses to filter the table which results in fewer rows than are currently visible in the browser viewport. So, the sticky header may still exist, but the table body may not be seen. Setting this option to true forces the original table header to scroll back into view. +
+
nulljQuery selector or object that will be used to monitor horizontal scroll position(v2.18.0) +
+
+ Defaults: xScroll > attachTo > window
+
+ Indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored. +
+
nulljQuery selector or object that will be used to monitor vertical scroll position (v2.18.0) +
+
+ Defaults: yScroll > attachTo > window
+
+ Indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored. +
+
+
+ +

Change log

+
+
    +
  • v2.18.0: +
      +
    • Nested tables with sticky headers now stack properly. See the new example added to the bottom of this demo page.
    • +
    • Added stickyHeaders_xScroll and stickyHeaders_yScroll options to indicate the element (jQuery selector or obect) in which to monitor for changes in scroll position; If undefined (null by default), the window is monitored.
    • +
    • Any defined onRenderHeader function is now executed on the cloned sticky header table cells. There are now 3 parameters available to this function: +
        +
      • index - header cell index; this is not the column index!
      • +
      • config - the table.config settings for the table.
      • +
      • $table - the target of this parameter changes: +
          +
        • For the original table header cells, this parameter is a jQuery object pointing to the original table; this table has the class name hasStickyHeaders.
        • +
        • When the sticky table header cells are processed, this parameter is a jQuery object pointing to a copy of the table that is used as a sticky header; this table has the class name containsStickyHeaders.
        • +
        +
      • +
      + Note Any defined onRenderTemplate function is not called on the sticky header because it is a clone of the already rendered header cell; instead use onRenderHeader to attach event listeners, etc. +
    • +
    • Removed jQuery UI theme from the theme selector in this demo to accomodate adding the accordion. With the current setup, changing the table theme would continue to show the jQuery UI class names on the table (uitheme is not removed when switching themes), so the tables became a chimera of jQuery UI and the selected theme, and the accordion would become un-styled; it was a real mess.
      +
      +
    • +
    +
  • +
  • v2.14.4: Added cssStickyHeaders_attachTo option (default set to null). +
      +
    • Setting this option with either a jQuery selector string (".wrapper") or jQuery object ($(".wrapper")).
    • +
    • This option contains the target to which the sticky header will attach - see the second example below.
    • +
    +
  • +
  • v2.9: +
      +
    • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
    • +
    • Added a widget option named stickyHeaders_cloneId +
        +
      • It contains a suffix to add to any table id.
      • +
      • Its default value is -sticky
      • +
      +
    • +
    • Table captions and any additional rows (filter widget row) will also be included in the sticky header.
    • +
    +
  • +
  • v2.7: You will need to modify the headerTemplate option to include the jQuery UI icon! See the example in the code.
  • +
  • v2.1.18: Add the class name sticky-false to any header rows you don't want to become sticky.
  • +
  • v2.1.17: Multiple rows in the header will become sticky.
  • +
  • v2.1 (updated v2.11): Added a widget option named stickyHeaders option which contains the css class name applied to the actual sticky header. Modified in v2.11 so that "tablesorter-stickyHeader" class is always added and this option only adds additional classes.
  • +
+
+ +
+ +

CSS

+
+

+	
+ +

Javascript

+
+

+	
+ +

Demo

+ Choose Theme: + +

+ Include Caption: +

+ +

Sticky headers in a popup window

+ + + + +

Attach sticky header to browser window

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Student Grades
NameMajorSexEnglishJapaneseCalculusGeometry
NameMajorSexEnglishJapaneseCalculusGeometry
Student01Languagesmale80707580
Student02Mathematicsmale908810090
Student03Languagesfemale85958085
Student04Languagesmale6055100100
Student05Languagesfemale68809580
Student06Mathematicsmale1009910090
Student07Mathematicsmale85689090
Student08Languagesmale100909085
Student09Mathematicsmale80506575
Student10Languagesmale8510010090
Student11Languagesmale8685100100
Student12Mathematicsfemale100757085
Student13Languagesfemale1008010090
Student14Languagesfemale50455590
Student15Languagesmale953510090
Student16Languagesfemale100503070
Student17Languagesfemale801005565
Student18Mathematicsmale30495575
Student19Languagesmale68908870
Student20Mathematicsmale40454080
Student21Languagesmale5045100100
Student22Mathematicsmale1009910090
Student23Languagesfemale85808080
student23Mathematicsmale8277079
student24Languagesfemale100911382
student25Mathematicsmale22968253
student26Languagesfemale37295659
student27Mathematicsmale86826923
student28Languagesfemale4425431
student29Mathematicsmale77472238
student30Languagesfemale19352310
student31Mathematicsmale90271750
student32Languagesfemale60753338
student33Mathematicsmale4313715
student34Languagesfemale77978144
student35Mathematicsmale5815195
student36Languagesfemale70617094
student37Mathematicsmale6036184
student38Languagesfemale6339011
student39Mathematicsmale50463238
student40Languagesfemale5175253
student41Mathematicsmale43342878
student42Languagesfemale11896095
student43Mathematicsmale48921888
student44Languagesfemale8225973
student45Mathematicsmale91733739
student46Languagesfemale481210
student47Mathematicsmale8910611
student48Languagesfemale90322118
student49Mathematicsmale42494972
student50Languagesfemale56376754
+ +
+ +

Attach sticky header to its parent

+
+ + + + + + + + + + + + + + + + + + + + + + +
Student Grades
Account #First NameLast NameAgeTotalDiscountDiff
A43PeterParker289.9920.3%+3
A255JohnHood3319.9925.1%-7
A33ClarkKent1815.4944.2%-13
A11BruceAlmighty45153.1944%+19
A102BruceEvans56153.1923%+9
A23MikePeters225.6920.3%+2
A55LeslieKent3315.9925.1%-3
A3FrankMint4412.5944.2%-12
A21JoeThomas4515.2544%+12
A12TessEvans6613.5923%+4
A21PeterDunn122.9921.1%+2
A33HarryJones1319.4922.2%-6
A13JohnJames1613.8942.1%-13
A71NickParker4513.8944%+29
A21CharlesDunn1915.4922%+3
+
+ +
+

Nested sticky header tables

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Main table header
Main table - row 1
Main table - row 2
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Header for the nested table 1
Second header row for nested table 1
data in the nested table 1 - row 1
data in the nested table 1 - row 2
data in the nested table 1 - row 3
data in the nested table 1 - row 4
data in the nested table 1 - row 5
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nested table 2 caption
Header for the nested table 2
Second header row for nested table 2
data in the nested table 2 - row 1
data in the nested table 2 - row 2
data in the nested table 2 - row 3
data in the nested table 2 - row 4
data in the nested table 2 - row 5
data in the nested table 2 - row 6
data in the nested table 2 - row 7
data in the nested table 2 - row 8
data in the nested table 2 - row 9
data in the nested table 2 - row 10
data in the nested table 2 - row 11
data in the nested table 2 - row 12
data in the nested table 2 - row 13
data in the nested table 2 - row 14
data in the nested table 2 - row 15
data in the nested table 2 - row 16
data in the nested table 2 - row 17
data in the nested table 2 - row 18
data in the nested table 2 - row 19
data in the nested table 2 - row 20
+
data in the nested table 1 - row 6
data in the nested table 1 - row 7
data in the nested table 1 - row 8
data in the nested table 1 - row 9
data in the nested table 1 - row 10
data in the nested table 1 - row 11
data in the nested table 1 - row 12
data in the nested table 1 - row 13
data in the nested table 1 - row 14
data in the nested table 1 - row 15
data in the nested table 1 - row 16
data in the nested table 1 - row 17
data in the nested table 1 - row 18
data in the nested table 1 - row 19
data in the nested table 1 - row 20
+
Main table - row 3
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nested table 3 caption
Header for the nested table 3
data in the nested table 3 - row 1
data in the nested table 3 - row 2
data in the nested table 3 - row 3
data in the nested table 3 - row 4
data in the nested table 3 - row 5
data in the nested table 3 - row 6
data in the nested table 3 - row 7
data in the nested table 3 - row 8
data in the nested table 3 - row 9
data in the nested table 3 - row 10
data in the nested table 3 - row 11
data in the nested table 3 - row 12
data in the nested table 3 - row 13
data in the nested table 3 - row 14
data in the nested table 3 - row 15
data in the nested table 3 - row 16
data in the nested table 3 - row 17
data in the nested table 3 - row 18
data in the nested table 3 - row 19
data in the nested table 3 - row 20
+
Main table - row 4
+ +
+ + +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-toggle-tablesorter.html b/compare/thirdparty/tablesorter/docs/example-widget-toggle-tablesorter.html new file mode 100644 index 00000000..f115bbf7 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-toggle-tablesorter.html @@ -0,0 +1,220 @@ + + + + + jQuery tablesorter 2.0 - Toggle Widget + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+
+
+ +

Notes

+
+
    +
  • This widget allows the enabling or disabling of tablesorter sorting & filtering.
  • +
  • The widget does not reset an applied sort or filters. If you want to include this when disabling, use the following code: + +
    +
  • +
  • *ALERT* This widget requires tablesorter v2.22.4+ and jQuery 1.7+
  • +
+
+ +

Options

+
+
+ TIP! Click on the link in the option column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
toggleTS_hideFilterRowfalseIf true, hide filter row; when false, associated filters are disabled.
nullThis function is called after tablesorter has been enabled or disabled. +
+

Use it as follows:

+
$( function() {
+  $( 'table' ).tablesorter({
+    widget : [ 'toggle-ts' ],
+    widgetOptions : {
+      toggleTS_callback : function( c, isEnabled ) {
+        c.$table.toggleClass( 'isDisabled', !isEnabled );
+      }
+    }
+  });
+});
+
+
+
+ +
+ +

+

Demo

+ +

+ Tablesorter & Filter: + Hide Filter Row: +

+ +Filter any column: + +

Filter (this works even if tablesorter & filter are disabled)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RankFirst NameLast NameAgeTotal (filter disabled)Discount (sort disabled)Date
1Philip Aaron WongJohnson Sr Esq25$5.9522%Jun 26, 2004 7:22 AM
11AaronHibert12$2.995%Aug 21, 2009 12:21 PM
12Brandon ClarkHenry Jr51$42.2918%Oct 13, 2000 1:15 PM
111PeterParker28$9.9920%Jul 6, 2006 8:14 AM
21JohnHood33$19.9925%Dec 10, 2002 5:14 AM
013ClarkKent Sr.18$15.8944%Jan 12, 2003 11:14 AM
005BruceAlmighty Esq45$153.1944%Jan 18, 2021 9:12 AM
10AlexDumass13$5.294%Jan 8, 2012 5:11 PM
16JimFranco24$14.1914%Jan 14, 2004 11:23 AM
166Bruce LeeEvans22$13.1911%Jan 18, 2007 9:12 AM
100Brenda DexterMcMasters18$55.2015%Feb 12, 2010 7:23 PM
55DennisBronson65$123.0032%Jan 20, 2001 1:12 PM
9MarthadelFuego25$22.0917%Jun 11, 2011 10:55 AM
+ +

Javascript

+
+

+	
+ +

HTML

+
+

+	
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-ui-theme.html b/compare/thirdparty/tablesorter/docs/example-widget-ui-theme.html new file mode 100644 index 00000000..7a269dfc --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-ui-theme.html @@ -0,0 +1,240 @@ + + + + + jQuery tablesorter 2.0 - jQuery UITheme Widget (jQuery UI) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+ NOTE! +

+
    +
  • In v2.27.0, the icon class name changes used by jQuery 1.12.0 have been included. Icon class names were renamed from using "carat" to "caret"; this widget now includes both for backwards compatibility.
  • +
  • In v2.19.0, this widget allows dynamic changing of themes; including switching from jQuery UI or Bootstrap to any other theme. To change a theme, do the following: +
    var $table = $('table');
    +$table[0].config.theme = 'grey';
    +$table.trigger('applyWidgets');
    + To see this work, check out the scroller widget demo. +
  • +
  • As of tablesorter version 2.9+, this widget can no longer be applied to versions of tablesorter prior to version 2.8.
  • +
  • You will need to modify the headerTemplate option to include the bootstrap icon! See the example in the code (v2.7).
  • +
  • The original "widgetUitheme" option has been replaced by "widgetOptions.uitheme". See the javascript block below for more details (v2.1).
  • +
  • In tablesorter v2.4, the uitheme option has changed to indicate the theme instead of an array of icons to use: +
      +
    • All theme class names are now contained within $.tablesorter.themes with the jQuery UI theme saved to $.tablesorter.themes.jui
    • +
    • The themes variable allows you to modify the class names for the table, header, sort icons, active state, hover state, filter inputs and zebra striping. See the code below on how to extend these variables.
    • +
    • Set the uitheme widget option to "jui" to set the widget to use the jQuery UI theme. See the bootstrap demo for another example.
    • +
    +
  • +
  • Earlier widget versions required jQuery 1.4+. The UITheme widget for tablesorter 2.4+ requires jQuery 1.2.6+.
  • +
+ +

Demo

+
+ jQuery UI Theme: +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Some interesting caption
First NameLast NameAgeTotalDiscountDate
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ +

Page Header

+
+
<!-- ui theme stylesheet - contents shown below -->
+<link rel="stylesheet" href="../css/theme.jui.css">
+<!-- jQuery UI theme (cupertino example here) -->
+<link rel="stylesheet" href="css/jquery-ui.min.css">
+
+<!-- load jQuery first -->
+<script src="js/jquery.min.js"></script>
+
+<!-- tablesorter plugin -->
+<script src="../js/jquery.tablesorter.js"></script>
+<!-- tablesorter widget file - loaded after the plugin -->
+<script src="../js/jquery.tablesorter.widgets.js"></script>
+
+ +

Javascript

+
+

+	
+ +

HTML

+
+

+	
+ + + +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-vertical-group.html b/compare/thirdparty/tablesorter/docs/example-widget-vertical-group.html new file mode 100644 index 00000000..b85a3a97 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-vertical-group.html @@ -0,0 +1,124 @@ + + + + + jQuery tablesorter 2.0 - Vertical Group Widget + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+ NOTE! +

+
    +
  • Widget added in v2.29.1. Thanks to aavmurphy for sharing the code! +
      +
    • This widget works by enabling it in the widgets option and adding a tablesorter-vertical-group class name to the header column.
    • +
    • Make sure to include the additional CSS shown on this page.
    • +
    • Click to sort a column. Any sorted columns with duplicate cell entries will be grouped together and the zebra striping will be adjusted.
    • +
    • Sorting multiple columns will again adjust the duplicates and zebra striping.
    • +
    +
  • +
+ +

Demo

+
+ + + + + + + + + + + + + + + + + + + + + + + +
DayFirst NameLast Name
TuesdayPeterJones
ThursdayPeterSmith
ThursdayFredSmith
FridayFredJones
TuesdayMikeSnow
MondayMikeJones
WednesdayMikeSmith
FridayFredSnow
TuesdayFredSmith
ThursdayPeterSnow
FridayMikeSmith
MondayFredJones
TuesdayPeterSnow
FridayMikeJones
WednesdayPeterSmith
+ +

Page Header

+
+
+<link rel="stylesheet" href="css/blue/style.css">
+<script src="js/jquery-latest.min.js"></script>
+<script src="js/jquery.tablesorter.min.js"></script>
+<script src="js/widgets/widget-vertical-group.min.js"></script>
+
+

CSS

+
+

+	
+

Javascript

+
+

+	
+

HTML

+
+

+	
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-view.html b/compare/thirdparty/tablesorter/docs/example-widget-view.html new file mode 100644 index 00000000..4a5ce457 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-view.html @@ -0,0 +1,795 @@ + + + + + jQuery tablesorter 2.0 - View Widget + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+
+ +

Notes

+
+
    +
  • This widget will only work in tablesorter version 2.24.0+ and jQuery version 1.7+.
  • +
  • When it's done building the view 'viewComplete' will be tiggered
  • +
  • {coln} will be replaced with the row/coln value wrapped by a span that has the classes/attributes of the original unless :raw is use, then the text value of row/coln will be used. ie: {col0:raw}
  • +
+
+ +

Options

+
+
+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OptionDefaultDescription
'#ts-view-toolbar'Element to insert toolbar (view buttons) into. +
+

+ This option allows you to select which element the view buttons will be injected into. +
+
'#ts-view'Element to insert the view into. +
+

+ This option allows you to select which element the view will be injected into. +
+
'#ts-view-caption'Element to copy the tabel caption into curing views. +
+

+ This option allows you to select which element to copy thre caption into during views. +
+
'ts-view-switcher'Class name to be added to all view buttons. +
+

+ Name of the class to be added to all view buttons. +

+ * Note * Just put in the name of the class do NOT put a . in front of it. +
+
falseDefault view to display on startup. +
+

+ Default view to display on startup, false means none. The value must be an object in view_layouts. +
+
{ 'view': { <options> } }List of available layouts and it's options. +
+

+ In this object you setup each view. +
    +
  • icon - This is the class for the view button
  • +
  • title - Title of this view
  • +
  • container - Wrapper node for this view
  • +
  • tpml - HTML layout for this view, {coln} will be replaced with the value in that col n
  • +
  • raw - This is in case you want the raw table data, container and tpml are ignored when raw is true.
  • +
+
+
+
+ +
+

+ +

View Demo

+

+
+
+
+
+

+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Our Products
PNNameDescriptionPrice
default thumb001-10001Widget 1This is Widget 1, it's nice soft and unique.19.99
default thumb001-10002Widget 2This is Widget 2, it's nice fluffy and unique.1.99
+ on sale + default thumb + 001-10003Widget 3This is Widget 3, it's nice soft and unique.89.99
default thumb001-10004Widget 4This is Widget 4, it's nice soft and unique. I'ts also a little fluffy but not nice and fluffy.49.99
+ on sale + default thumb + 001-10005Widget 5This is Widget 5, it's nice soft and unique. This one also have a unique smell and texture. Not for human consumption.18.49
default thumb001-10006Widget 6This is Widget 6, it's nice soft and unique. Nothing special here but I do want a longer description so I can see how things line up, or in this case don't line up. Oh but this is the only Widget we have in purple!69.98
default thumb001-10007Widget 7This is Widget 7, it's nice soft and unique.4.99
default thumb001-10008Widget 8This is Widget 8, it's nice soft and unique.17.89
default thumb001-10009Widget 9This is Widget 9, it's nice soft and unique.17.49
default thumb001-10010Widget 10This is Widget 10, it's nice soft and unique.119.99
default thumb001-10011Widget 11This is Widget 11, it's nice soft and unique.18.88
default thumb001-10012Widget 12This is Widget 12, it's nice soft and unique.19.97
default thumb001-10013Widget 13This is Widget 13, it's nice soft and unique.9.98
default thumb001-10014Widget 14This is Widget 14, it's nice soft and unique.29.99
default thumb001-10015Widget 15This is Widget 15, it's nice soft and unique.44.95
default thumb001-10016Widget 16This is Widget 16, it's nice soft and unique.8.69
default thumb001-10017Widget 17This is Widget 17, it's nice soft and unique.17.99
default thumb001-10018Widget 18This is Widget 18, it's nice soft and unique.219.99
default thumb001-10019Widget 19This is Widget 19, it's nice soft and unique.1.98
default thumb001-10020Widget 20This is Widget 20, it's nice soft and unique.9.97
+ + +
+
+ + + + + + +
+
+
+ +

Page Header

+
+
+<!-- jQuery -->
+<script src="js/jquery-latest.min.js"></script>
+
+<!-- Demo stuff -->
+<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css">
+
+<!-- Tablesorter: required -->
+<link rel="stylesheet" href="../css/theme.blue.css">
+<script src="../js/jquery.tablesorter.js"></script>
+<script src="../js/widgets/widget-view.js"></script>
+
+<!-- Tablesorter: optional -->
+<script src="../js/jquery.tablesorter.widgets.js"></script>
+<script src="../js/widgets/widget-filter.js"></script>
+<script src="../js/widgets/widget-pager.js"></script>
+
+ +

Javascript

+
+

+	
+ +

CSS

+
+

+	
+ +

HTML

+
+

+	
+ +
+ + + + + + + diff --git a/compare/thirdparty/tablesorter/docs/example-widget-zebra.html b/compare/thirdparty/tablesorter/docs/example-widget-zebra.html new file mode 100644 index 00000000..4b5fe2f3 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widget-zebra.html @@ -0,0 +1,146 @@ + + + + + jQuery tablesorter 2.0 - Zebra Stripe Widget + + + + + + + + + + + + + + + + + + + + + + + +
+ +

+ NOTE! +

+
    +
  • This widget is part of the plugin, but has been modified from the original.
  • +
  • The original "widgetZebra" option has been replaced by "widgetOptions.zebra". See the javascript block below for more details (v2.1).
  • +
  • If the "widgetZebra" option exists, it will over-ride this newer "widgetOptions.zebra" option in order to maintain backwards compatibility.
  • +
  • The zebra widget only applies to visible rows, so if the table isn't visible in the beginning, you must use $('table').trigger('applyWidgets'); when the table becomes visible (See issue #224)
  • +
+ +

Demo

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
First NameLast NameAgeTotalDiscountDate
PeterParker28$9.9920%Jul 6, 2006 8:14 AM
JohnHood33$19.9925%Dec 10, 2002 5:14 AM
ClarkKent18$15.8944%Jan 12, 2003 11:14 AM
BruceAlmighty45$153.1944%Jan 18, 2001 9:12 AM
BruceEvans22$13.1911%Jan 18, 2007 9:12 AM
+ +

Javascript

+
+

+	
+

CSS

+
+

+	
+

HTML

+
+

+	
+ + + +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/example-widgets.html b/compare/thirdparty/tablesorter/docs/example-widgets.html new file mode 100644 index 00000000..b4c16125 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/example-widgets.html @@ -0,0 +1,251 @@ + + + + + jQuery tablesorter 2.0 - Writing custom widgets + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ Notes about the addWidget template: +
    +
  • The id block: +
      +
    • The widget id, or name, must be unique!
    • +
    • The id, or name, can contain special characters and/or spaces.
    • +
    • This setting is required.
    • +
    +
  • +
  • The priority block (added v2.9): +
      +
    • Set the widget priority using any number; think of it like setting the css z-index.
    • +
    • This tells the plugin the order in which to run the widgets, lowest number priority first.
    • +
    • The default widgets have priorities set in intervals of 10 (see the widget priority table), so to run your custom widget before a specific widget, set your widget priority to less than that number.
    • +
    • This block is not supported in older versions!
    • +
    • This setting is optional, but if no priority is specified, it defaults to 10.
    • +
    +
  • +
  • The options block (added v2.8): +
      +
    • Include any widget options to be automatically be extended with any set widgetOptions (from table.config.widgetOptions).
    • +
    • As of v2.8, no included widgets will be using this to maintain backwards compatibility with older versions. This changed when v2.9 was released.
    • +
    • This block is not supported in older versions!
    • +
    • This block is optional.
    • +
    +
  • +
  • The init block (added v2.0.28): +
      +
    • This code is called only after tablesorter has initialized, but before initial sort and before any of the widgets are applied (via the format block); it is only run once.
    • +
    • Since, v2.0.28, only the saveSort widget uses this block to ensure that the stored sort is applied before; but some of the newer widgets (post v2.9) are using this code block.
    • +
    • This block is not supported in older versions.
    • +
    • This block is optional.
    • +
    +
  • +
  • The format block (modified v2.0.23): +
      +
    • This block is part of the original addWidget template and is required.
    • +
    • In the original template, only the table parameter is provided. After v2.0.28, config and widgetOptions were provided as additional parameters (sorry the previous docs were incorrect).
    • +
    • The initFlag is correctly set in v2.8+.
    • +
    +
  • +
  • The remove block (added v2.4): +
      +
    • In v2.19.0 the refreshing parameter was added: +
        +
      • It is a parameter used to indicate that the refreshWidgets method was triggered.
      • +
      • When widgets are refreshed, the remove method is called, then the widget init function is immediately called to reapply the widget.
      • +
      • In this update, this parameter is used by the filter widget to retain filtered rows and prevent a "flash" of showing all rows, then returning to its previous state after applying the filter again.
      • +
      +
    • +
    • This code is called when either the refreshWidgets or destroy methods are called.
    • +
    • The code contained within this block must remove all additional content/elements added by the widget. Also, any rows or content that is hidden must be restored.
    • +
    • If additional rows are added to the table, make sure to include the class name within the selectorRemove option.
    • +
    • This block was added in v2.4, and not supported in older versions.
    • +
    • This block is optional.
    • +
    +
  • +
+
+ +

addWidget Template

+
+
// addWidget Template
+// *******************
+// parameters:
+// table = table object (DOM)
+// config = config object (from table.config)
+// widgetOptions = all widget options (from table.config.widgetOptions)
+$.tablesorter.addWidget({
+  id: 'myWidget',
+  // set the priority of the widget (optional)
+  priority: 10,
+  // widget options (added v2.8) - added to table.config.widgetOptions
+  options: {
+    myWidget_option1 : 'setting1',
+    myWidget_option2 : 'setting2'
+  },
+  // The init function (added v2.0.28) is called only after tablesorter has
+  // initialized, but before initial sort & before any of the widgets are applied.
+  init: function(table, thisWidget, config, widgetOptions) {
+    // widget initialization code - this is only *RUN ONCE*
+    // but in this example, only the format function is called to from here
+    // to keep the widget backwards compatible with the original tablesorter
+    thisWidget.format(table, config, widgetOptions, true);
+  },
+  format: function(table, config, widgetOptions, initFlag) {
+    // widget code to apply to the table *AFTER EACH SORT*
+    // set the initFlag to true when this format is called from the init
+    // function above otherwise initFlag is undefined
+    // * see the saveSort widget for a full example *
+  },
+  remove: function(table, config, widgetOptions, refreshing) {
+    // do what ever needs to be done to remove stuff added by your widget
+    // unbind events, restore hidden content, etc.
+    // refreshing flag is true when the refreshWidgets method is triggered, meaning
+    // the widget will be removed, then immediately reapplied
+  }
+});
+
+ +

Demo

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameMajorSexEnglishJapaneseCalculusGeometry
NameMajorSexEnglishJapaneseCalculusGeometry
Student12Mathematicsfemale100757085
Student13Languagesfemale1008010090
Student14Languagesfemale50455590
Student15Languagesmale953510090
Student16Languagesfemale100503070
Student17Languagesfemale801005565
Student18Mathematicsmale30495575
Student19Languagesmale68908870
Student20Mathematicsmale40454080
Student21Languagesmale5045100100
Student22Mathematicsmale1009910090
Student23Languagesfemale85808080
Student01Languagesmale80707580
Student02Mathematicsmale908810090
Student03Languagesfemale85958085
Student04Languagesmale6055100100
Student05Languagesfemale68809580
Student06Mathematicsmale1009910090
Student07Mathematicsmale85689090
Student08Languagesmale100909085
Student09Mathematicsmale80506575
Student10Languagesmale8510010090
Student11Languagesmale8685100100
Student24Languagesfemale100911382
+ +

Javascript

+

Repeat Headers Widget

+ +
+

+	
+ +
+
+ Next up: Pager plugin ›› +
+ +
+ + + diff --git a/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.eot b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 00000000..b93a4953 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.eot differ diff --git a/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.svg b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 00000000..94fb5490 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.ttf b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 00000000..1413fc60 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.ttf differ diff --git a/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 00000000..9e612858 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff differ diff --git a/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff2 b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 00000000..64539b54 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/compare/thirdparty/tablesorter/docs/img/cat.gif b/compare/thirdparty/tablesorter/docs/img/cat.gif new file mode 100644 index 00000000..4aa6bf97 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/cat.gif differ diff --git a/compare/thirdparty/tablesorter/docs/img/external.png b/compare/thirdparty/tablesorter/docs/img/external.png new file mode 100644 index 00000000..0f8533b0 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/external.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/fish.gif b/compare/thirdparty/tablesorter/docs/img/fish.gif new file mode 100644 index 00000000..8bb39439 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/fish.gif differ diff --git a/compare/thirdparty/tablesorter/docs/img/grid-default-thumb.png b/compare/thirdparty/tablesorter/docs/img/grid-default-thumb.png new file mode 100644 index 00000000..ea022708 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/grid-default-thumb.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/link.png b/compare/thirdparty/tablesorter/docs/img/link.png new file mode 100644 index 00000000..af541d09 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/link.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/new-product-banner.png b/compare/thirdparty/tablesorter/docs/img/new-product-banner.png new file mode 100644 index 00000000..2b697d9e Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/new-product-banner.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/octopus.gif b/compare/thirdparty/tablesorter/docs/img/octopus.gif new file mode 100644 index 00000000..524693b9 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/octopus.gif differ diff --git a/compare/thirdparty/tablesorter/docs/img/sale-banner.png b/compare/thirdparty/tablesorter/docs/img/sale-banner.png new file mode 100644 index 00000000..3e9e5ed9 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/sale-banner.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/screens-blue.png b/compare/thirdparty/tablesorter/docs/img/screens-blue.png new file mode 100644 index 00000000..19c6d6b5 Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/screens-blue.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/screens.png b/compare/thirdparty/tablesorter/docs/img/screens.png new file mode 100644 index 00000000..eb007dac Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/screens.png differ diff --git a/compare/thirdparty/tablesorter/docs/img/x.gif b/compare/thirdparty/tablesorter/docs/img/x.gif new file mode 100644 index 00000000..98bbd41d Binary files /dev/null and b/compare/thirdparty/tablesorter/docs/img/x.gif differ diff --git a/compare/thirdparty/tablesorter/docs/img/zenhub-badge.svg b/compare/thirdparty/tablesorter/docs/img/zenhub-badge.svg new file mode 100644 index 00000000..7b228177 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/img/zenhub-badge.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/index.html b/compare/thirdparty/tablesorter/docs/index.html new file mode 100644 index 00000000..df9b079d --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/index.html @@ -0,0 +1,8272 @@ + + + + +jQuery tablesorter 2.0 + + + + + + + + + + + + + + + + + + + +
+ + + +

+ Original Author: Christian Bach
+ Maintainer of this fork: Rob Garrison (Mottie)
+ Version: 2.1+ (changelog)  + + +
+ Licence: + Dual licensed under MIT + or GPL licenses (pick one).
+

+ + +

Contents

+
    +
  1. Introduction
  2. +
  3. Demo
  4. +
  5. Getting started
  6. +
  7. Examples
  8. +
  9. Configuration
  10. +
  11. Widget & Pager Options (v2.1)
  12. +
  13. Methods
  14. +
  15. Events
  16. +
  17. API
  18. +
  19. Download
  20. +
  21. Compatibility
  22. +
  23. Support
  24. +
  25. Credits
  26. +
+ + +

Introduction

+

+ tablesorter is a jQuery plugin for turning a + standard HTML table with THEAD and TBODY tags into a sortable table without page refreshes. + tablesorter can successfully parse and sort many types of data including linked data in a cell. + It has many useful features including: +

+ +
    +
  • Multi-column sorting
  • +
  • Multi-tbody sorting - see the options table below
  • +
  • Parsers for sorting text, URIs, integers, currency, floats, IP addresses, dates (ISO, long and short formats), time. Add your own easily
  • +
  • Support secondary "hidden" sorting (e.g., maintain alphabetical sort when sorting on other criteria)
  • +
  • Extensibility via widget system
  • +
  • Cross-browser: IE 6.0+, FF 2+, Safari 2.0+, Opera 9.0+
  • +
  • Works with jQuery 1.2.6+ (jQuery 1.4.1+ needed with some widgets).
  • +
  • Works with jQuery 1.9+ ($.browser.msie was removed; needed in the original version).
  • +
  • Small code size
  • +
+ + +

Demo

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Account #First NameLast NameAgeTotalDiscountDifferenceDate
A42bPeterParker28$9.9920.9%+12.1Jul 6, 2006 8:14 AM
A255BruceJones33$13.1925%+12Dec 10, 2002 5:14 AM
A33ClarkEvans18$15.8944%-26Jan 12, 2003 11:14 AM
A1BruceAlmighty45$153.1944.7%+77Jan 18, 2001 9:12 AM
A102BruceEvans22$13.1911%-100.9Jan 18, 2007 9:12 AM
A42aBruceEvans22$13.1911%0Jan 18, 2007 9:12 AM
+ +

+ TIP! Sort multiple columns simultaneously by holding down the Shift key and clicking a second, third or even fourth column header! +

+ + +

Getting started

+

+ To use the tablesorter plugin, include the jQuery + library and the tablesorter plugin inside the <head> tag + of your HTML document: +

+ +
<!-- choose a theme file -->
+<link rel="stylesheet" href="/path/to/theme.default.css">
+<!-- load jQuery and tablesorter scripts -->
+<script type="text/javascript" src="/path/to/jquery-latest.js"></script>
+<script type="text/javascript" src="/path/to/jquery.tablesorter.js"></script>
+
+<!-- tablesorter widgets (optional) -->
+<script type="text/javascript" src="/path/to/jquery.tablesorter.widgets.js"></script>
+
+ +

tablesorter works on standard HTML tables. You must include THEAD and TBODY tags:

+ +
<table id="myTable" class="tablesorter">
+  <thead>
+    <tr>
+      <th>Last Name</th>
+      <th>First Name</th>
+      <th>Email</th>
+      <th>Due</th>
+      <th>Web Site</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>Smith</td>
+      <td>John</td>
+      <td>jsmith@gmail.com</td>
+      <td>$50.00</td>
+      <td>http://www.jsmith.com</td>
+    </tr>
+    <tr>
+      <td>Bach</td>
+      <td>Frank</td>
+      <td>fbach@yahoo.com</td>
+      <td>$50.00</td>
+      <td>http://www.frank.com</td>
+    </tr>
+    <tr>
+      <td>Doe</td>
+      <td>Jason</td>
+      <td>jdoe@hotmail.com</td>
+      <td>$100.00</td>
+      <td>http://www.jdoe.com</td>
+    </tr>
+    <tr>
+      <td>Conway</td>
+      <td>Tim</td>
+      <td>tconway@earthlink.net</td>
+      <td>$50.00</td>
+      <td>http://www.timconway.com</td>
+    </tr>
+  </tbody>
+</table>
+ +

Start by telling tablesorter to sort your table when the document is loaded:

+ +
$(function() {
+  $("#myTable").tablesorter();
+});
+ +

+ Click on the headers and you'll see that your table is now sortable! You can + also pass in configuration options when you initialize the table. This tells + tablesorter to sort on the first and second column in ascending order. +

+ +
$(function() {
+  $("#myTable").tablesorter({ sortList: [[0,0], [1,0]] });
+});
+ +

+ NOTE! tablesorter will auto-detect most data types including numbers, dates, ip-addresses for more information see Examples. +

+ + +

Examples

+

+ These examples will show what's possible with tablesorter. You need Javascript enabled to + run these samples, just like you and your users will need Javascript enabled to use tablesorter. +

+ +
+

Basic

+

Sorting

+ + +

Theming

+ +

+ +

Using Parsers / Extracting Content

+ + +
+

Advanced

+ +

Parsers / Extracting Content

+ + +

Widgets / Plugins

+ + +

Adding / Removing Content

+ + +

Change Header Style

+ + +
+

Other

+ +

Options & Events

+ + +

Metadata - setting inline options

+ + +
+ +
+ +

Demos

+ +

Playgrounds & Other demos

+ + +

Plugins / Widgets
+ these widgets are included in the jquery.tablesorter.widgets.js file (except for extra filter formatter functions). +
+ this widget is included with the plugin core. +

+ + + +

Custom Parsers

+ + +

Work-in-progress

+ + +
+
+ + +

Configuration

+ +
+

+ tablesorter has many options you can pass in at initialization to achieve different effects
+ TIP! Click on the link in the property column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDefaultDescriptionLink
PropertyTypeDefaultDescriptionLink
cancelSelectionBooleantrueIndicates if tablesorter should disable selection of text in the table header (TH). Makes header behave more like a button.
BooleantrueAny colspan cells in the tbody may have its content duplicated in the cache for each spanned column (v2.25.0; v2.25.8). +
+

If true, the cache will contain duplicated cell contents for every column the colspan includes. This makes it easier to sort & filter columns because a cell spanning all columns will only work with one parser.

+

In v2.25.8, the contents of cells that are spanned will be set to an empty string only if the textExtraction setting is left as a string. If a function is added, it will be used to attempt to extract other content from the spanned cell (see demo for an example).

+
// this row: <tr><td colspan="3">foo</td><td>bar</td></tr> results in this row cache:
+[ 'foo', 'foo', 'foo', 'bar' ] // if duplicateSpan = true
+[ 'foo', '',    '',    'bar' ] // if duplicateSpan = false (no textExtraction function)
+ *NOTE* +
    +
  • Cells in the tbody with a rowspan are not yet supported and will not sort or filter as you would expect.
  • +
  • Having these values in the cache does not automatically guarantee that all widgets will work as expected with colspans in the tbody (e.g. scroller widget with fixed columns)
  • +
+
+
Example
String""Additional CSS class applied to style the header with a ascending sort (v2.11). +

+ Changed to empty string ("") in v2.11, as the "tablesorter-headerAsc" class will always be added to a header cell with an ascending sort; this option now contains any additional class names to add. + +

Example from the blue theme:

+
.tablesorter-blue .tablesorter-headerAsc {
+  background-color: #9fbfdf;
+  background-image: url(black-asc.gif);
+}
+ Default changed v2.5 to "tablesorter-headerAsc". Default changed v2.1.7 to "tablesorter-headerSortUp". Original default: "headerSortUp"
+
cssChildRowString"tablesorter-childRow"Add this css class to a child row that should always be attached to its parent. Click on the "cssChildRow" link to toggle the view on the attached child row. Previous default was "expand-child" (Modified v2.4). + 1 + 2 +
This is an entirely new row, but attached to the row above while sorting
+ cssChildRow Example HTML: +
<table width="100%" border="1">
+  <thead>
+    <tr>
+      <th>Item #</th>
+      <th>Name</th>
+      <th>Available</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr>
+      <td>12345</td>
+      <td>Toy Car</td>
+      <td>5</td>
+    </tr>
+    <tr class="tablesorter-childRow"> <!-- this row will remain attached to the above row, and not sort separately -->
+      <td colspan="3">
+        It's a toy car!
+      </td>
+    </tr>
+    <tr>
+      <td>23456</td>
+      <td>Toy Plane</td>
+      <td>2</td>
+    </tr>
+    <tr class="tablesorter-childRow"> <!-- this row will remain attached to the above row, and not sort separately -->
+      <td colspan="3">
+        It's a toy plane!
+      </td>
+    </tr>
+    <tr class="tablesorter-childRow"> <!-- this row will remain attached to the above two rows, and not sort separately -->
+      <td colspan="3">
+        and it flies!
+      </td>
+    </tr>
+  </tbody>
+</table>
+
String""Additional CSS class applied to style the header with a descending sort (v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter-headerDesc" class will always be added to a header cell with a descending sort; this option now contains any additional class names to add. +

Example from the blue theme:

+
.tablesorter-blue .tablesorter-headerDesc {
+  background-color: #8cb3d9;
+  background-image: url(black-desc.gif);
+}
+ Default changed v2.5 to "tablesorter-headerDesc". Default changed v2.1.7 to "tablesorter-headerSortDown". Original default: "headerSortDown"
+
String""Additional CSS class applied to style the headers (v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter-header" class will always be added to the table headers; this option now contains any additional class names to add. +

Example from the blue theme:

+
.tablesorter-blue .tablesorter-header {
+  background-color: #99bfe6;
+  background-repeat: no-repeat;
+  background-position: center right;
+  padding: 4px 20px 4px 4px;
+  white-space: normal;
+  cursor: pointer;
+}
+ Default changed v2.1.7 to "tablesorter-header". Original default: "header"
+
String""Additional CSS class applied to style the header row (v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter-headerRow" class will always be added to a table header row; this option now contains any additional class names to add.
+

This CSS style was added in v2.4, prior to that the row would get the same class as the header cells. This class was added to make it easier to determine what element was being targeted in the plugin.

+
+
String"tablesorter-icon"The CSS style used to style the header cell icon (modified v2.7; v2.21.1). +

+ In v2.21.1, adding multiple class names to this option is now properly supported.
+
+ As of v2.7, the icon will only be added to the header if both the cssIcon option is set AND the headerTemplate option includes the icon tag ({icon}).
+
+ In v2.4, an <i> element, with this class name, is automatically appended to the header cells. To prevent the plugin from adding an <i> element to the headers, set the cssIcon option to an empty string.
+
+
String""The CSS style added to the header cell icon when the column has an ascending sort (v2.18.3). +

+ This class is only applied when the headerTemplate option includes a {icon} tag or an HTML element with the class name from the cssIcon option. +
+
String""The CSS style used to style the header cell icon when the column has a descending sort (v2.18.3) +

+ This class is only applied when the headerTemplate option includes a {icon} tag or an HTML element with the class name from the cssIcon option. +
+
String""The CSS style used to style the header cell icon when the column does not have a sort applied (v2.18.3) +

+ This class is only applied when the headerTemplate option includes a {icon} tag or an HTML element with the class name from the cssIcon option. +
+
String""The CSS style used to style the header cell icon when the column sort is disabled (v2.28.13) +

+ This class is only applied when the headerTemplate option includes a {icon} tag or an HTML element with the class name from the cssIcon option. +
+
String""Additional CSS class applied to style the header when no sort is applied (v2.15). +

+ A "tablesorter-headerUnSorted" class will always be added to an unsorted header cell; this option contains any additional class names to add. Currently, no themes use this class name. +
+
String""Additional CSS class applied to style the header cell while it is being sorted or filtered (v2.4; v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter-processing" class will always be added to a table cells during processing; this option now contains any additional class names to add.
+

This class name is added to the header cell that is currently being sorted or filted. To prevent this class name from being added, set the showProcessing option to false.

+
+
String"tablesorter-infoOnly"All tbodies with this class name will not have its contents sorted. (v2.2). +
+
+ With the addition of multiple tbody sorting in v2.2, you can now insert a non-sorting tbody within the table by adding this class to the tbody. +
<tbody class="tablesorter-infoOnly">
+  <tr>
+    <th>The contents of this tbody</th>
+  </tr>
+  <tr>
+    <td>will not be sorted</td>
+  </tr>
+</tbody>
+ As an example, I've split up this options table into three (3) tbodies. The first contains the active options, the second is the info block with a row that only contains the text "Deprecated Options", and the last tbody contains the deprecated options. Sort the table to see how each tbody sorts separately. +
+

+ NOTE! The pager plugin will only be applied to the first tbody, as always. I may work on modifying this behavior in the future, if I can figure out the best implementation. +

+
+
cssNoSortString"tablesorter-noSort"Class name added to element inside header. Clicking on that element, or any elements within it won't cause a sort. (v2.20.0).
String"tablesorter-ignoreRow"Class name to add to a table header row if you want all cells within this row to be ignored (v2.18.4). +
+
+ Header rows with this class name will not be added into the table.config.$headers variable. Meaning, any cells in the row will have sorting disabled, and any form element interaction within these cells will also be ignored by tablesorter.
+
+ This class name should be added to header rows that contain custom HTML, like for the pager controls, custom filter row or table toolbar. +
+
1 2
String"mmddyyyy"Set the date format. Here are the available options. (Modified v2.0.23). +
+
    +
  • "mmddyyyy" (default)
  • +
  • "ddmmyyyy"
  • +
  • "yyyymmdd"
  • +
+ In previous versions, this option was set as "us", "uk" or "dd/mm/yy". This option was modified to better fit needed date formats. It will only work with four digit years!
+
+ The sorter should be set to "shortDate" and the date format can be set in the "dateFormat" option or set for a specific columns within the "headers" option. + See the demo page to see it working. +
$(function() {
+  $("table").tablesorter({
+
+    dateFormat : "mmddyyyy", // default date format
+
+    // or to change the format for specific columns,
+    // add the dateFormat to the headers option:
+    headers: {
+      0: { sorter: "shortDate" }, // "shortDate" with the default dateFormat above
+      1: { sorter: "shortDate", dateFormat: "ddmmyyyy" }, // day first format
+      2: { sorter: "shortDate", dateFormat: "yyyymmdd" }  // year first format
+    }
+
+  });
+});
+ Individual columns can be modified by adding the following (they all do the same thing), set in order of priority (Modified v2.3.1): +
    +
  • jQuery data data-dateFormat="mmddyyyy".
  • +
  • metadata class="{ dateFormat: 'mmddyyyy'}". This requires the metadata plugin.
  • +
  • headers option headers : { 0 : { dateFormat : 'mmddyyyy' } }.
  • +
  • header class name class="dateFormat-mmddyyyy".
  • +
  • Overall dateFormat option.
  • +
+
+
Example
Boolean or Stringfalse + Set this option to provide useful for development debugging information in the console v2.30.0. +
+

In v2.30.0, set this option as a string containing either "core" and/or a specific widget name; or, a boolean value.

+

When a boolean flag is set, all debugging information is shown in the console.

+ To display debugging information specific to a widget, or widgets include the widget name: +
$(function() {
+  $("table").tablesorter({
+    // Show debugging info only for the filter and columnSelector widgets
+    // include "core" to only show the core debugging info
+    debug : "filter columnSelector"
+  });
+});
+ The option only tests if the string contains the widget name, so "corefilter" would show debugging for both the core and filter widget. +
+
Example
delayInitBooleanfalse + Setting this option to true will delay parsing of all table cell data until the user initializes a sort. This speeds up the initialization process of very large tables, but the data still needs to be parsed, so the delay is still present upon initial sort. + Example
String"bottom" + Option indicating how tablesorter should deal with empty table cells. (Modified v2.1.16, v2.16.2). +
+
    +
  • bottom - sort empty table cells to the bottom.
  • +
  • top - sort empty table cells to the top.
  • +
  • none or zero - sort empty table cells as if the cell has the value equal to zero.
  • +
  • emptyMax - sort empty table cells as having a value greater than the max (more positive) value (v2.16.2).
  • +
  • emptyMin - sort empty table cells as having a value greater than the min (more negative) value (v2.16.2).
  • +
+ Individual columns can be modified by adding the following (they all do the same thing), set in order of priority: +
    +
  • jQuery data data-empty="top".
  • +
  • metadata class="{ empty: 'top'}". This requires the metadata plugin.
  • +
  • headers option headers : { 0 : { empty : 'top' } }.
  • +
  • header class name class="empty-top".
  • +
  • Overall emptyTo option.
  • +
+ emptyToBottom option was added in v2.1.11, then replaced by the emptyTo option in v2.1.16. +
+
Example
Objectnull + An object of instructions for per-"header cell" controls in the format: headers: { 0: { option: setting }, ... } + (v2.17.1) +
+

+ Warning Do not use this option if your header contains multiple rows or any colspan or rowspan. Instead add a data-attribute or class name to the header (see the first demo for an example).

+ For example, to disable sorting on the first two columns of a table: headers: { 0: { sorter: false}, 1: {sorter: false} }.
+
+ The plugin attempts to detect the type of data that is contained in a column, but if it can't figure it out then it defaults to alphanumeric. You can easily override this by setting the header argument (or column parser). + See the full list of default parsers here or here, or write your own. +
$(function() {
+  $("table").tablesorter({
+    headers: {
+
+      // See example - Disable first header cell; parser false skips extracting content
+      0: { sorter: false, parser: false },
+
+      // WARNING! The "ipAddress" parser was MOVED into the "parser-network.js" file
+      // in v2.18.0
+      1: { sorter: "ipAddress" },
+
+      // See example 2: Sort column numerically & treat any text as if its value is:
+      2: { sorter: "digit", empty:  "top" }, // zero; sort empty cells to the top
+      3: { sorter: "digit", string: "max" }, // maximum positive value
+      4: { sorter: "digit", string: "min" }, // maximum negative value
+
+      // Sort the sixth column by date & set the format
+      5: { sorter: "shortDate", dateFormat: "yyyymmdd" }, // year first format
+
+      // See example 3: lock the sort order
+      // this option will not work if added as metadata
+      6: { lockedOrder: "asc" },
+
+      // See Example 4: Initial sort order direction of 8th header cell
+      7: { sortInitialOrder: "desc" },
+
+      // Set filter widget options for this column
+      // See the "Applying the filter widget" demo
+      8: { filter: false },    // disable filter widget for this column
+      9: { filter: "parsed" }, // use parsed data for this column in the filter search
+
+      // Set resizable widget options for this column
+      10: { resizable: false } // prevent resizing of the 11th column
+
+    }
+  });
+});
+ Please note that the headers index values corresponds to the table header cells index (zero-based) and not the actual columns. For example, given the following table thead markup, the header-index counts the header th cells and does not actually match the data-column index when extra rows and/or colspan or rowspan are included in any of the header cells: +
<thead>
+	<tr>
+		<th colspan="4" data-column="0">header-index 0</th>
+	</tr>
+	<tr>
+		<th data-column="0">header-index 1</th>
+		<th data-column="1">header-index 2</th>
+		<th data-column="2">header-index 3</th>
+		<th data-column="3">header-index 4</th>
+	</tr>
+	<tr>
+		<th colspan="2" data-column="0">header-index 5</th>
+		<th colspan="2" data-column="2">header-index 6</th>
+	</tr>
+</thead>
+ So, in the above example, to disable the sort of the second table column (data-column index of 1), the header cell of index 2 needs to be set as follows: 2 : { sorter : false }.
+
+ In v2.17.0, you can reference the column(s) using a class name, id or column index. +
headers : {
+    '.first-name' : { sorter: 'text' },
+    '.disabled'   : { sorter: false }
+}
+ Warning What won't work is if you try to target the header using a filtering selector that uses an index, e.g. "th:eq()", ":gt()", ":lt()", ":first", ":last", ":even" or ":odd", ":first-child", ":last-child", ":nth-child()", ":nth-last-child()", etc. +
+
+ 1 + 2 + 3 + 4 + 5 +
String"{content}"This is a template string which allows adding additional content to the header while it is being built (v2.7; v2.17.8). +

+ In v2.17.8, if this option is set to an empty string (''), an inner div will no longer be wrapped around the header content.
+
+ This template string has two modifying tags: {content} and {icon}.
+ {content} will be replaced by the current header HTML content.
+ {icon} will be replaced by <i class="tablesorter-icon"></i>, but only if a class name is defined in the cssIcon option.
+
+ This template string may also contain HTML, e.g ('<em>{content}</em> {icon}')
+
+ After the template string is built, the onRenderTemplate function is called to allow further manipulation. Please read more about this onRenderTemplate function and/or check out the example (link to the right). +
+
Example
ignoreCaseBooleantrueWhen true, text sorting will ignore the character case. If false, upper case characters will sort before lower case. (v2.2).
FunctionnullThis callback fires when tablesorter has completed initialization. (v2.2). +
+
$(function() {
+
+  // bind to tablesorter-initialized event BEFORE initializing tablesorter*
+  $("table")
+    .bind("tablesorter-initialized",function(e, table) {
+      // do something after tablesorter has initialized
+    });
+
+  // initialize the tablesorter plugin
+  $("table").tablesorter({
+    // this is equivalent to the above bind method
+    initialized : function(table) {
+      // do something after tablesorter has initialized
+    }
+  });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
BooleantrueApply widgets after table initializes (v2.3.5). +
+ When true, all widgets set by the widgets option will apply after tablesorter has initialized, this is the normal behavior.
+
+ If false, the each widget set by the widgets option will be initialized, meaning the "init" function is run, but the format function will not be run. This is useful when running the pager plugin after the table is set up. The pager plugin will initialize, then apply all set widgets.
+
+ Why you ask? Well, lets say you have a table with 1000 rows that will have the pager plugin applied to it. Before this option, the table would finish its setup, all widgets would be applied to the 1000 rows, pager plugin initializes and reapplies the widgets on the say 20 rows showing; making the widget application to 100 rows unnecessary and a waste of time. So, when this option is false, widgets will only be applied to the table after the pager is set up. +
+
String"widget-{name}"When the table has a class name that matches the template and a widget id that matches the {name}, the widget will automatically be added to the table (v2.18.0) +
+
+ By default, this option is set to 'widget-{name}'. So if the table has a class name of widget-zebra the zebra widget will be automatically added to the config.widgets option and applied to the table.
+
+ Some widget ID's with special characters may not be detected; ID's with letters, numbers, underscores and/or dashes will be correctly detected.
+
+ The template string *must* contain the {name} tag. +
+
Functionnull + This function is called after content is to the TH tags (after the template is procressed and added). You can use this to modify the HTML in each header tag for additional styling (v2.18.0). +
+
+ In versions 2.0.6+, all TH text is wrapped in a div with a class name of "tablesorter-inner" by default. In the example below, the header cell (TH) div is given a class name (source).
+

Function parameters:

+
    +
  • index - zero-based index of the current table header cell; this value is not indicative of the column index, as it is simply a count of header cells. So it will be effected by rowspan, colspan and multiple rows in the header.
  • +
  • config - The current table.config.
  • +
  • $table - This value is the current table jQuery object. If this function is being applied to cloned headers, as is does in the stickyHeaders widget, then this value will contain the sticky header clone added after the current table, and not the main table (v2.18.0).
  • +
+
$(function() {
+  $("table").tablesorter({
+    headerTemplate: '{content}',
+    onRenderHeader: function (index, config, $table) {
+      $(this).find('div').addClass('roundedCorners');
+    }
+  });
+});
and you'll end up with this HTML (only the thead is shown)
<thead>
+  <tr>
+    <th class="tablesorter-header"><div class="tablesorter-header-inner roundedCorners">Column 1</div></th>
+    <th class="tablesorter-header"><div class="tablesorter-header-inner roundedCorners">Column 2</div></th>
+  </tr>
+</thead>
+ * Note: this function adds additional rendering time to the table if any DOM manipulation is done. Because this time will be on top of the processing time already added by the template. +
+
Example
Functionnull + This function is called after the template string has been built, but before the template string is applied to the header and before the onRenderHeader function is called (v2.7). +
+
+ The onRenderTemplate function receives a column index and template string parameters. The template string, from the headerTemplate option, will already have the {icon} and {content} tags replaced; it's just a string of formatted HTML. When done manipulating this string, return it. +

Function parameters:

+
    +
  • index - zero-based index of the current table header cell; this value is not indicative of the column index, as it is simply a count of header cells. So it will be effected by rowspan, colspan and multiple rows in the header.
  • +
  • template - A rendered headerTemplate HTML string.
  • +
+
$(function() {
+  $("table").tablesorter({
+    headerTemplate: '{icon}{content}',
+    onRenderTemplate: function (index, template) {
+      return '<em>' + (index + 1) + ':</em> ' + template;
+    }
+  });
+});
The template parameter can be manipulated as a string, or if you prefer, turn it into a jQuery object (var $t = $(template)) to find and replace content as desired. Just make sure you return a string (return $t.html())
+
+From the example function above, you'll end up with something similar to this HTML (only the thead is shown)
<thead>
+  <tr>
+    <th class="tablesorter-header"><div class="tablesorter-header-inner"><em>1:</em> <i class="tablesorter-icon"></i>First Name</div></th>
+    <th class="tablesorter-header"><div class="tablesorter-header-inner"><em>2:</em> <i class="tablesorter-icon"></i>Last Name</div></th>
+  </tr>
+</thead>
+ * Note: If the cssIcon option is an empty string, the {icon} tag will also become an empty string. +
+
Example
String"> thead th, > thead td"jQuery selectors used to find cells in the header. +
+
+ You can change this, but the table will still need the required thead and tbody before this plugin will work properly. +
Added > to the selector in v2.3 to prevent targeting nested table headers. It was modified again in v2.4 to include td cells within the thead. +
+
String"tr.remove-me"This CSS class name can be applied to all rows that are to be removed prior to triggering a table update. (v2.1). +
+
+ It was necessary to add this option because some widgets add table rows for styling (see the writing custom widgets demo) and if a table update is triggered ($('table').trigger('update');) those added rows will automatically become incorporated into the table. +
+
selectorSortString"th, td"jQuery selector of content within selectorHeaders that is clickable to trigger a sort (v2.4).Example
BooleantrueWhen this option is true any applied sort on the table will be reapplied after an update method (v2.19.0). +
+
+ Specifically, this option applies to the updateAll, update, addRows and updateCell methods and is checked after the method has completed updating the internal cache.
+
+ If false, the widgets will still be refreshed for all but the updateCell method - this behavior was added in v2.19.0.
+
+ Note when triggering one of the above methods, and passing a defined resort parameter, it will override this setting.
+ Note if a sort is not reapplied, problems with some widgets may occur, namely with the grouping widget. +
+
serverSideSortingBooleanfalseSet to true if the server is performing the sorting. The ui and events will still be used (v2.5.3).
showProcessingBooleanfalseShow an indeterminate timer icon in the header when the table is sorted or filtered. Please note that due to javascript processing, the icon may not show as being animated. I'm looking into this further and would appreciate any feedback or suggestions with the coding (v2.4).Example
ArraynullUse to add an additional forced sort that is prepended to sortList. +
+

+ NOTE Only when the user interacts with the table will the sortForce and sortAppend settings be applied. During initialization, and while setting the sortList through the API, both the sortForce and sortAppend settings are ignored. This will allow you to have explicit control over the sorting. +

+ For example, sortForce: [[0,0]] will sort the first column in ascending order. After the forced sort, the user selected column(s), but not during initialzation, the sorting order defined in the sortList will follow. And lastly, the sort defined in the sortAppend option will be applied. More explicitly:
+
+ There are three options to determine the sort order and this is the order of priority: +
    +
  1. sortForce forces the user to have this/these column(s) sorted first (null by default).
  2. +
  3. SortList is the initial sort order of the columns.
  4. +
  5. SortAppend is the default sort that is added to the end of the users sort selection (null by default; v2.24.0).
  6. +
+ The value of these sort options is an array of arrays and can include one or more columns. The format is an array of instructions for per-column sorting and direction in the format: [[columnIndex, sortDirection], ... ] where columnIndex is a zero-based index for your columns left-to-right and sortDirection is 0 for Ascending and 1 for Descending. A valid argument that sorts ascending first by column 1 and then column 2 looks like: [[0,0],[1,0]]. +
$(function() {
+  $("table").tablesorter({
+    sortForce  : [[0,0]],        // Always sort first column first
+    sortList   : [[1,0], [2,0]], // initial sort columns (2nd and 3rd)
+    sortAppend : [[3,0]]         // Always add this sort on the end (4th column)
+  });
+});
+
Example
ArraynullUse to add an initial sort to the table. +
+

+ NOTE Only when the user interacts with the table will the sortForce and sortAppend settings be applied. During initialization, and while setting the sortList through the API, both the sortForce and sortAppend settings are ignored. This will allow you to have explicit control over the sorting. +

+ The value contains an array of instructions for per-column sorting and direction in the format: [[columnIndex, sortDirection], ... ] where columnIndex is a zero-based index for your columns left-to-right and sortDirection is 0 for Ascending and 1 for Descending. A valid argument that sorts ascending first by column 1 and then column 2 looks like: [[0,0],[1,0]]. Please see sortForce for more details on other sort order options.
+
$(function() {
+  $("table").tablesorter({
+    sortList : [[1,0], [2,0]] // initial sort columns (2nd and 3rd)
+  });
+});
+ This option can also be set using data-attributes (v2.3.1), jQuery data or metadata on the table: + + + + + + + + +
** Note: data-sortlist data is not supported in jQuery versions older than 1.4.
jQuery data<table data-sortlist="[[0,0],[4,0]]"> **
Meta data<table class="tablesorter {sortlist: [[0,0],[4,0]]}">
+
+
Example
Array or ObjectnullUse to add an additional forced sort that will be appended to the dynamic selections by the user (v2.24.0). +
+

+ NOTE Only when the user interacts with the table will the sortForce and sortAppend settings be applied. During initialization, and while setting the sortList through the API, both the sortForce and sortAppend settings are ignored. This will allow you to have explicit control over the sorting. +

+ For example, can be used to sort people alphabetically after some other user-selected sort that results in rows with the same value like dates or money due. It can help prevent data from appearing as though it has a random secondary sort.
+
$(function() {
+  $("table").tablesorter({
+    sortAppend : [[1,0], [2,0]] // always append sorting of 2nd and 3rd column
+  });
+});
+ The value contains an array of instructions for per-column sorting and direction in the format: [[columnIndex, sortDirection], ... ] where columnIndex is a zero-based index for your columns left-to-right and sortDirection is 0 for Ascending and 1 for Descending. A valid argument that sorts ascending first by column 1 and then column 2 looks like: [[0,0],[1,0]]. Please see sortForce for more details on other sort order options. +

In v2.24.0, the sortAppend option now accepts an object containing column indexes.

+

Also, the sort direction can be set using "a" (ascending), "d" (descending), "n" (next), "s" (same) & "o" (opposite):

+
$(function() {
+  $("table").tablesorter({
+    sortAppend: {
+      0 : [[ 1,'a' ]], // always apply ascending sort
+      2 : [[ 3,'o' ]], // sort "o"pposite of column 2 direction
+      4 : [[ 5,'s' ], [ 0,'s' ]]  // sort "s"ame as column 4 direction
+    }
+  });
+});
+
+
Example
String"asc" + This sets the direction a column will sort when clicking on the header for the first time. Valid arguments are "asc" for Ascending or "desc" for Descending.
+
+
+ This order can also be set by desired column using the headers option (v2.0.8).
+
+ Individual columns can be modified by adding the following (they all do the same thing), set in order of priority (modified v2.3.1): +
    +
  • jQuery data data-sortInitialOrder="asc".
  • +
  • metadata class="{ sortInitialOrder: 'asc'}". This requires the metadata plugin.
  • +
  • headers option headers : { 0 : { sortInitialOrder : 'asc' } }.
  • +
  • header class name class="sortInitialOrder-asc".
  • +
  • Overall sortInitialOrder option.
  • +
+
+
+ 1 + 2 +
Booleanfalse + Boolean flag indicating if certain accented characters within the table will be replaced with their equivalent characters. (Modified v2.2). +
+
    +
  • This option no longer switches the sort to use the String.localeCompare method.
  • +
  • When this option is true, the text parsed from table cells will convert accented characters to their equivalent to allow the alphanumeric sort to properly sort.
  • +
  • If false (default), any accented characters are treated as their value in the standard unicode order.
  • +
  • The following characters are replaced for both upper and lower case (information obtained from sugar.js sorting equivalents table): +
      +
    • áàâãä replaced with a
    • +
    • ç replaced with c
    • +
    • éèêë replaced with e
    • +
    • íìİîï replaced with i
    • +
    • óòôõöō replaced with o
    • +
    • úùûü replaced with u
    • +
    • ß replaced with S
    • +
    +
  • +
  • Please see the example page for instrcutions on how to modify the above equivalency table.
  • +
  • If you would like to continue using the String.localeCompare method, then set the sortLocaleCompare option to false and use the new textSorter option as follows: +
    $('table').tablesorter({
    +  textSorter: function(a,b) {
    +    return a.localeCompare(b);
    +  }
    +});
  • +
+

+ NOTE! See the Language wiki page for language specific examples and how to extend the character equivalent tables seen in the sortLocaleCompare demo. +

+ Boolean flag indicating whenever to use javascript String.localeCompare method or not.
+ This is only used when comparing text with international character strings. A sort using localeCompare will sort accented characters the same as their unaccented counterparts.
+
+
Example
Booleanfalse + Setting this option to true will allow you to click on the table header a third time to reset the sort direction. (v2.0.27). +
+
Don't confuse this option with the sortReset method. This option only resets the column sort after a third click, while the method immediately resets the entire table sort. +
+
Example
sortResetKeyString"ctrlKey"The key used to reset sorting on the entire table. Defaults to the control key. The other options are "shiftKey" or "altKey" (reference).
sortRestartBooleanfalse + Setting this option to true will start the sort with the sortInitialOrder when clicking on a previously unsorted column. (v2.0.31). + Example
Booleanfalse + Setting this option to true and sorting two rows with exactly the same content, the original sort order is maintained (v2.14). +

+ This isn't exactly a stable sort because the sort order maintains the original unsorted order when sorting the column in an ascending direction. When sorting the column in a descending order, the opposite of the original unsorted order is returned. If that doesn't make any sense, please refer to issue #419. +
+
Example
String"shiftKey" + The key used to select more than one column for multi-column sorting. Defaults to the Shift key. +
+

The other options include "ctrlKey" or "altKey" (reference)

+ To make a multisort always active, use any of the other event objects that are always "truthy" like "type" or "bubbles". See issue #1200). +
+
Example
String"max" + A key word indicating how tablesorter should deal with text inside of numerically sorted columns. (v2.1.16). +

+ String options was initially set in the header options only. Overall option added and values changed in version 2.1.16; setting the value to: +
    +
  • "max" will treat any text in that column as a value greater than the max (more positive) value. Renamed from "max+".
  • +
  • "min" will treat any text in that column as a value greater than the min (more negative) value. Renamed from "max-".
  • +
  • "top" will always sort the text to the top of the column.
  • +
  • "bottom" will always sort the text to the bottom of the column.
  • +
  • "none" or "zero" will treat the text as if it has a value of zero.
  • +
+ Individual columns can be modified by adding the following (they all do the same thing), set in order of priority: +
    +
  • jQuery data data-string="top".
  • +
  • metadata class="{ string: 'top'}". This requires the metadata plugin.
  • +
  • headers option headers : { 0 : { string : 'top' } }.
  • +
  • header class name class="string-top".
  • +
  • Overall stringTo option.
  • +
+
+
Example
tabIndexBooleantrueAdd a tabindex to the headers for keyboard accessibility; this was previously always applied (v2.14).
String""Additional CSS class applied to style the table (v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter" class will always be added to the table; this option now contains any additional class names to add. +

This class was required in the default markup in version 2.0.5. But in version 2.0.6, it was added as an option.

+ Modify this option if you are not using the default css, or if you are using a completely custom stylesheet. +
+
String"default"This option will add a theme css class name to the table "tablesorter-{theme}" for styling (v2.4; v2.18.0). +
+
+ *NOTE* If creating a custom theme file, make sure to include a filter definition to hide filtered out rows: +
.filtered { display: none; }
+

Note the .filtered class can be modified using the filter_filteredRow option.

+ When changing this theme option (the actual theme name is inside parentheses), make sure that the appropriate css theme file has also been loaded. Default theme files include: + see all themes
+ +
+

uitheme widget

+

You will need to use the uitheme widget to extend the theme to apply css from jQuery UI, Bootstrap (v3 & earlier) or other css libraries.

+

Modify a theme

+

If you want to modify the existing themes ("jui" and "bootstrap"), it can be done as follows:

+

Modify the class names by extending from the $.tablesorter.themes variable

+

Note there is no need to extend from the entire list of class names, just include the key:value pairs that are being changed:

+
// Extend a theme to modify any of the default class names
+$.extend($.tablesorter.themes.jui, {
+  // change default jQuery uitheme icons - find the full list of icons
+  // here: http://jqueryui.com/themeroller/ (hover over them for their name)
+  table        : 'ui-widget ui-widget-content ui-corner-all', // table classes
+  caption      : 'ui-widget-content',
+  // header class names
+  header       : 'ui-widget-header ui-corner-all ui-state-default', // header classes
+  sortNone     : '',
+  sortAsc      : '',
+  sortDesc     : '',
+  active       : 'ui-state-active', // applied when column is sorted
+  hover        : 'ui-state-hover',  // hover class
+  // icon class names
+  icons        : 'ui-icon', // icon class added to the <i> in the header
+  iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // "caret" class renamed in jQuery v1.12.0
+  iconSortAsc  : 'ui-icon-carat-1-n ui-icon-caret-1-n',
+  iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s',
+  // other
+  filterRow    : '',
+  footerRow    : '',
+  footerCells  : '',
+  even         : 'ui-widget-content', // even row zebra striping
+  odd          : 'ui-state-default'   // odd row zebra striping
+});
+ +

Custom theme

+ To add a new uitheme, define it as follows (replace "custom" with the name of your theme): +
$.tablesorter.themes.custom = {
+  table      : 'table',       // table classes
+  header     : 'header',      // header classes
+  footerRow  : '',
+  footerCells: '',
+  icons      : 'icon',        // icon class added to the <i> in the header
+  sortNone   : 'sort-none',   // unsorted header
+  sortAsc    : 'sort-asc',    // ascending sorted header
+  sortDesc   : 'sort-desc',   // descending sorted header
+  active     : 'sort-active', // applied when column is sorted
+  hover      : 'hover',       // hover class
+  filterRow  : 'filters',     // class added to the filter row
+  even       : 'even',        // even row zebra striping
+  odd        : 'odd'          // odd row zebra striping
+}
+ Then use it by adding the name of your theme to the theme option: +
$(function() {
+  $("table").tablesorter({
+    // set the new theme name from $.tablesorter.themes here
+    theme   : 'custom',
+    // add {icon} to template (if needed)
+    headerTemplate: '{content}{icon}',
+    // make sure to initialize uitheme widget
+    widgets : ["uitheme"]
+  });
+});
+
+
Example
String"data-text"This data-attribute can be added to any tbody cell and can contains alternate cell text (v2.16.0). +
+
+ This option contains the name of the data-attribute used by the textExtraction function. Add it to the cell(s) as follows: +
<td data-text="1">First repository</td>
+ Note This option only works when the textExtraction option is set to "basic". +

*NOTE* It is important to know that the filter widget is set to use extracted (parsed) content as a parsed value and the raw cell content as unparsed values; and most of the time the filter widget is searching unparsed content. But, when a data-attribute is used, both the parsed and raw cached data will contain the exact same content (ref)!

+
+
Multiple*"basic"Defines which method is used to extract data from a table cell for sorting (v2.19.0) +
+
+ * Note This option accepts multiple types (String, Object or Function); see below for further details. +

+ In v2.19.0, the code was further optimized. When set to "basic" (the default), the textExtraction code will check for data-attributes, otherwise, any other string value setting will skip the data-attribute value check; because of this change, there is a noticable lessening of initialization time in Internet Explorer. +

+ In v2.17.0, the textExtraction column can also be referenced by using a jQuery selector (e.g. class name, id or column index) that points to a table header cell.
+
textExtraction : {
+    // 'jQuery thead cell selector' : function ( new method )
+    '.styled' : function(node, table, cellIndex) {
+        return $(node).find('strong').text();
+    },
+    // columnIndex : function ( original method )
+    2 : function(node, table, cellIndex) {
+        return $(node).find('img').attr('title');
+    }
+}
+ Warning What won't work is if you try to target the header using a filtering selector that uses an index, e.g. "th:eq()", ":gt()", ":lt()", ":first", ":last", ":even" or ":odd", ":first-child", ":last-child", ":nth-child()", ":nth-last-child()", etc.
+
+ As of version 2.16.0, +
    +
  • The default text extraction method has been renamed and updated to get data from a data-attribute (set by the textAttribute option).
  • +
  • If you need to support older versions of IE, this may add a significant delay to the table initialization especially for large tables; in this case, set the textExtraction option to any name other than "basic".
  • +
  • Also, this option can now be set using a data-attribute named "data-text-extraction" on the table.
  • +
+ You can customize the text extraction by writing your own text extraction function "myTextExtraction" which you define like: +
var myTextExtraction = function(node, table, cellIndex) {
+  // extract data from markup and return it
+  // originally: return node.childNodes[0].childNodes[0].innerHTML;
+  return $(node).find('selector').text();
+}
+$(function() {
+  $("#myTable").tablesorter( { textExtraction: myTextExtraction } );
+});
+ tablesorter will pass the current table cell object for you to parse and return. Thanks to Josh Nathanson for the examples; updated to a jQuery example by Rob G (Mottie). +

Now if the text you are finding in the script above is say a number, then just include the headers sorter option to specify how to sort it. Also in this example, we will specify that the special textExtraction code is only needed for the second column (1 because we are using a zero-based index). All other columns will ignore this textExtraction function.

+

Added table and cellIndex variables to the textExtraction function in version 2.1.2 (this is not part of the original plugin).

+
$(function() {
+  $("table").tablesorter({
+    textExtraction: {
+      1: function(node, table, cellIndex) {
+           return $(node).find("span:last").text();
+      }
+    },
+    headers: {
+      1: { sorter : "digit" }
+    }
+  });
+});
+ The built-in option is "basic" (modified v2.16.0) which is the equivalent of doing this inside of the textExtraction function: $(node).text();. +
+
Example
Stringundefined + This option should contain a unique namespace for each table; it is used when binding to event listeners (v2.15.7). +
+
+ Notes about this namespace option: +
    +
  • If a namesspace is not defined, a (hopefully) unique random namespace will be generated.
  • +
  • If defined, any "non-word" characters (anything not "a-z", "0-9" or "_") within the namespace will be removed.
  • +
  • Added or not, the namespace will be saved with a leading period (e.g. ".myuniquetableid")
  • +
+
$(function() {
+  $("#mytable").tablesorter({
+    // if table id = "mytable", this namespace is saved as ".mytable"
+    namespace : $('#mytable')[0].id;
+  });
+});
+
+
Functionnull + Replace the default number sorting algorithm with a custom one using this option (v2.12). +
+
+ Here is an example: +
$(function() {
+  $("table").tablesorter({
+    numberSorter : function(a, b, direction, maxColumnValue) {
+      // direction; true = ascending; false = descending
+      // maxColumnValue = the maximum value of that column (ignoring its sign)
+      return a - b;
+    }
+  });
+});
The direction parameter (boolean) is merely for informational purposes as the plugin automatically switches a and b depending on the sort direction ( i.e. there's no need to worry about reverse sorting, it's taken care of by the plugin ). +
+
String"click"Use this option to change the click event (v2.22.0) +
+

Change this option if want to change the click event that is bound to the table headers. Add multiple events separated by spaces.

+ Warning If this option is set to fire multiple events (e.g. 'mouseup pointerup'), sorting may be initialized twice in rapid succession and make it appear that nothing changed. +
+
String"mousedown"Use this option to change the pointer down event (v2.22.0) +
+

Change this option if you're using pointer events (or the pointer events polyfill). Add multiple events separated by spaces.

+ Warning If this option is set to fire multiple events (e.g. 'mousedown pointerdown'), sorting may be initialized twice in rapid succession and make it appear that nothing changed. +
+
String"mouseup"Use this option to change the pointer up event (v2.22.0) +
+

Change this option if you're using pointer events (or the pointer events polyfill). Add multiple events separated by spaces.

+ Warning If this option is set to fire multiple events (e.g. 'mouseup pointerup'), sorting may be initialized twice in rapid succession and make it appear that nothing changed. +
+
Functionnull + Replace the default sorting algorithm with a custom one using this option (v2.27.6) - *NOTE* The parameters have changed!!. +
+
+ Include a script like naturalSort.js as follows: +
$(function() {
+  $("table").tablesorter({
+    textSorter : naturalSort
+  });
+});
+ or use the localeCompare sort +
$(function() {
+  $("table").tablesorter({
+    // replace the OVERALL text sorter function
+    textSorter: function(a, b, direction, columnIndex, table) {
+      // direction: true = ascending; false = descending
+      // columnIndex: zero-based index of the current table column being sorted
+      // table: table DOM element (access options by using table.config)
+      return a.localeCompare(b);
+    }
+  });
+});
In v2.27.6, the textSorter option will allow setting a sorter per column index or class name (column indexes were added in v2.12): +
$(function() {
+  $("table").tablesorter({
+    textSorter : {
+      // replace INDIVIDUAL COLUMN text sorter functions
+      0 : function(a, b, direction, columnIndex, table) {
+        // same as $.tablesorter.sortText (basic alphabetical sort)
+        // direction: true = ascending; false = descending
+        // columnIndex: zero-based index of the current table column being sorted
+        // table: table DOM element (access options by using table.config)
+        return a > b ? 1 : (a < b ? -1 : 0);
+      },
+      // same as the function in column 0 above (modified in v2.12)
+      1 : $.tablesorter.sortText,
+      // renamed v2.12 from $.tablesorter.sortText - performs natural sort
+      '.nat-sort' : $.tablesorter.sortNatural,
+      // alphanumeric sort from sugar v2.0+ (https://sugarjs.com/docs/#/Array/getOption)
+      '.sugar-sort' : Sugar.Array.getOption('sortCollate')
+    }
+  });
+});
The direction parameter (boolean) is merely for informational purposes as the plugin automatically switches a and b depending on the sort direction ( i.e. there's no need to worry about reverse sorting, it's taken care of by the plugin ). +
+
+ 1 + 2 +
Booleantrue + Indicates how tablesorter should deal with a numerical format: (v2.1.3). +
+ + + + + + + + + + + +
trueU.S.1,234,567.89
falseGerman:
French:
1.234.567,89
1 234 567,89
+
+
Example
widgetsArray[ ] (empty array) + Initialize widgets using this option ( e.g. widgets : ['zebra'], or custom widgets widgets: ['zebra', 'myCustomWidget'];, see this demo on how to write your own custom widget ). + Example
Booleanfalse + Indicates if tablesorter should apply fixed percentage-based widths to the table columns (modified v2.4). +
+ Prior to v2.4, this option set pixel widths to added colgroups to fix the column widths. This is useful for the Pager companion. +
+ Requires the jQuery dimension plugin to work. This is now part of the jQuery core. +
+
Example
Object{ } + In version 2.1, all widget options have been moved into this option. This is a move to store all widget specific options in one place so as not to polute the main table options. All current widgets have been modified to use this new option. (v2.1). +
+
+ Previously documented widget options widgetZebra, widgetColumns and widgetUitheme will be retained for backwards compatibility.
+
+ Use the widgetOptions option as follows, please note that each option is followed by a comma (except the last one): +
$(function() {
+  $("table").tablesorter({
+
+    // initialize a bunch of widgets (the order doesn't matter)
+    widgets: ["zebra", "uitheme", "columns", "filter", "resizable", "stickyHeaders"],
+
+    widgetOptions: {
+
+      // *** COLUMNS WIDGET ***
+      // change the default column class names primary is the 1st column
+      // sorted, secondary is the 2nd, etc
+      columns: [
+        "primary",
+        "secondary",
+        "tertiary"
+      ],
+
+      // If true, the class names from the columns option will also be added
+      // to the table tfoot
+      columns_tfoot: true,
+
+      // If true, the class names from the columns option will also be added
+      // to the table thead
+      columns_thead: true,
+
+      // *** FILTER WIDGET ***
+      // css class name added to the filter cell (string or array)
+      filter_cellFilter: '',
+
+      // If there are child rows in the table (rows with class name from
+      // "cssChildRow" option) and this option is true and a match is found
+      // anywhere in the child row, then it will make that row visible;
+      // default is false
+      filter_childRows: false,
+
+      // ( filter_childRows must be true ) if true = search
+      // child rows by column; false = search all child row text grouped
+      filter_childByColumn: false,
+
+      // if true, include matching child row siblings
+      filter_childWithSibs: true,
+
+      // if true, allows using '#:{query}' in AnyMatch searches
+      // ( column:query )
+      filter_columnAnyMatch: true,
+
+      // If true, a filter will be added to the top of each table column.
+      filter_columnFilters: true,
+
+      // css class name added to the filter row & each input in the row
+      // (tablesorter-filter is ALWAYS added)
+      filter_cssFilter: '',
+
+      // data attribute in the header cell that contains the default (initial)
+      // filter value
+      filter_defaultAttrib: 'data-value',
+
+      // add a default column filter type "~{query}" to make fuzzy searches
+      // default; "{q1} AND {q2}" to make all searches use a logical AND.
+      filter_defaultFilter: {},
+
+      // filters to exclude, per column
+      filter_excludeFilter: {},
+
+      // jQuery selector string (or jQuery object)
+      // of external filters
+      filter_external: '',
+
+      // class added to filtered rows; needed by pager plugin
+      filter_filteredRow: 'filtered',
+
+      // ARIA-label added to filter input/select; {{label}} is replaced by
+      // the column header "data-label" attribute, if it exists, or it uses the
+      // column header text
+      filter_filterLabel : 'Filter "{{label}}" column by...',
+
+      // add custom filter elements to the filter row
+      filter_formatter: null,
+
+      // Customize the filter widget by adding a select dropdown with content,
+      // custom options or custom filter functions;
+      // see http://goo.gl/HQQLW for more details
+      filter_functions: null,
+
+      // hide filter row when table is empty
+      filter_hideEmpty: true,
+
+      // Set this option to true to hide the filter row initially. The row is
+      // revealed by hovering over the filter row or giving any filter
+      // input/select focus. In v2.26.6, a function can be used to set when
+      // to hide the filter row.
+      filter_hideFilters: false,
+
+      // Set this option to false to keep the searches case sensitive
+      filter_ignoreCase: true,
+
+      // if true, search column content while the user types (with a delay)
+      // or, set a minimum number of characters that must be present before
+      // a search is initiated. In v2.27.3, this option can contain an
+      // object with column indexes or classnames; "fallback" is used
+      // for undefined columns
+      filter_liveSearch: true,
+
+      // global query settings ('exact' or 'match'); overridden by
+      // "filter-match" or "filter-exact" class
+      filter_matchType: {
+        'input': 'exact',
+        'select': 'exact'
+      },
+
+      // a header with a select dropdown & this class name will only show
+      // available (visible) options within the drop down
+      filter_onlyAvail: 'filter-onlyAvail',
+
+      // default placeholder text (overridden by any header
+      // "data-placeholder" setting)
+      filter_placeholder: {
+        search: '',
+        select: ''
+      },
+
+      // jQuery selector string of an element used to reset the filters.
+      filter_reset: null,
+
+      // Reset filter input when the user presses escape
+      // normalized across browsers
+      filter_resetOnEsc: true,
+
+      // Use the $.tablesorter.storage utility to save the most recent filters
+      filter_saveFilters: false,
+
+      // Delay in milliseconds before the filter widget starts searching;
+      // This option prevents searching for every character while typing
+      // and should make searching large tables faster.
+      filter_searchDelay: 300,
+
+      // allow searching through already filtered rows in special
+      // circumstances; will speed up searching in large tables if true
+      filter_searchFiltered: true,
+
+      // include a function to return an array of values to be added to the
+      // column filter select
+      filter_selectSource: null,
+
+      // filter_selectSource array text left of the separator is added to
+      // the option value, right into the option text
+      filter_selectSourceSeparator: '|',
+
+      // Set this option to true if filtering is performed on the
+      // server-side.
+      filter_serversideFiltering: false,
+
+      // Set this option to true to use the filter to find text from the
+      // start of the column. So typing in "a" will find "albert" but not
+      // "frank", both have a's; default is false
+      filter_startsWith: false,
+
+      // If true, ALL filter searches will only use parsed data. To only
+      // use parsed data in specific columns, set this option to false
+      // and add class name "filter-parsed" to the header
+      filter_useParsedData: false,
+
+      // *** RESIZABLE WIDGET ***
+      // If this option is set to false, resized column widths will not
+      // be saved. Previous saved values will be restored on page reload
+      resizable: true,
+
+      // If this option is set to true, a resizing anchor
+      // will be included in the last column of the table
+      resizable_addLastColumn: false,
+
+      // If this option is set to true, the resizable handle will extend
+      // into the table footer
+      resizable_includeFooter: true,
+
+      // Set this option to the starting & reset header widths
+      resizable_widths: [],
+
+      // Set this option to throttle the resizable events
+      // set to true (5ms) or any number 0-10 range
+      resizable_throttle: false,
+
+      // When true, the last column will be targeted for resizing,
+      // which is the same has holding the shift and resizing a column
+      resizable_targetLast: false,
+
+      // *** SAVESORT WIDGET ***
+      // If this option is set to false, new sorts will not be saved.
+      // Any previous saved sort will be restored on page reload.
+      saveSort: true,
+
+      // *** STICKYHEADERS WIDGET ***
+      // stickyHeaders widget: extra class name added to the sticky header
+      // row
+      stickyHeaders: '',
+
+      // jQuery selector or object to append sticky headers to
+      stickyHeaders_appendTo: null,
+
+      // jQuery selector or object to attach sticky headers scroll listener to
+      // overridden by xScroll & yScroll settings
+      stickyHeaders_attachTo: null,
+
+      // jQuery selector or object to monitor horizontal scroll position
+      // (defaults: xScroll > attachTo > window)
+      stickyHeaders_xScroll: null,
+
+      // jQuery selector or object to monitor vertical scroll position
+      // (defaults: yScroll > attachTo > window)
+      stickyHeaders_yScroll: null,
+
+      // number or jquery selector targeting the position:fixed element
+      stickyHeaders_offset: 0,
+
+      // scroll table top into view after filtering
+      stickyHeaders_filteredToTop: true,
+
+      // added to table ID, if it exists
+      stickyHeaders_cloneId: '-sticky',
+
+      // trigger "resize" event on headers
+      stickyHeaders_addResizeEvent: true,
+
+      // if false and a caption exist, it won't be included in the
+      // sticky header
+      stickyHeaders_includeCaption: true,
+
+      // The zIndex of the stickyHeaders, allows the user to adjust this
+      // to their needs
+      stickyHeaders_zIndex: 2,
+
+      // *** STORAGE WIDGET ***
+      // set storage type; this overrides any storage_useSessionStorage setting
+      // use the first letter of (l)ocal, (s)ession or (c)ookie
+      storage_storageType: '',
+      // DEPRECATED: allows switching between using local & session storage
+      storage_useSessionStorage: false,
+      // alternate table id (set if grouping multiple tables together)
+      storage_tableId: '',
+      // table attribute to get the table ID, if storage_tableId
+      // is undefined
+      storage_group: '', // defaults to "data-table-group"
+      // alternate url to use (set if grouping tables across
+      // multiple pages)
+      storage_fixedUrl: '',
+      // table attribute to get the fixedUrl, if storage_fixedUrl
+      // is undefined
+      storage_page: '',
+
+      // *** ZEBRA WIDGET ***
+      // class names to add to alternating rows
+      // [ "even", "odd" ]
+      zebra: [
+        "even",
+        "odd"
+      ]
+
+    }
+
+  });
+});
+
Example
Utility Options
String"checked"Used by the "checkbox" parser in the parser-input-select.js file (v2.22.2; v2.25.0). +
+

When using the checkbox parser, this class name is added to the row along with this class name plus the column index when the targeted checkbox is checked.

+

For example, if the named parser file has been loaded & "sorter-checkbox" class is added to the first column header, then any checked checkbox in the first column will have "checked checked-0" class names added to the row.

+ Checkboxes in any other column, not targeted by the parser, will be ignored and no extra row class names will be added. +
+
Example
BooleantrueUsed by the "checkbox" parser in the parser-input-select.js file (v2.24.6). +
+

When using the checkbox parser, this setting is used when a checkbox inside a header cell is changed; if true, only same column checkboxes within visible rows are modified to match the header checkbox.

+

If false, same column checkboxes in all rows are modified to match the status of the header checkbox.

+
+
Example
dataObject, ArrayundefinedStorage for processed table build widget (widget-build-table.js) data (array, object, string) (v2.11).Example
dateRangeNumeric50 + Used by the two digit year parser (parser-date-two-digit-year.js) to set the date range (v2.14).Example
objectnull + This option is used by multiple parsers to localize the language (v2.24.0; v2.24.1). +

+ This option is meant to be used with the jQuery Globalize library along with CLDR data. +

See the Globalization section in the group widget demo for details on how to set it up.

+

Currently, the globalize (parser-globalize.js), month (parser-date-month.js) & weekday (parser-date-weekday.js) parsers utilize this option.

+
$(function() {
+  $('table').tablesorter({
+    // globalize : { lang: 'en' } // for ALL columns
+    // or, per column by using the column index
+    globalize : {
+      0 : { lang: 'fr', Globalize : Globalize('fr'), raw: 'MMM d, y G' },
+      2 : { lang: 'fr' }
+    }
+  });
+});
+
+
2 2
Booleanfalse + Used by the input-select parser indicate if changes to child row content is to be ignored (v2.28.10) +

+ Change this setting to ignore input, textarea and select changes inside of child rows: +
$(function() {
+  $('table').tablesorter({
+    ignoreChildRow : true
+  });
+});
+ See pull request #1399 for more information. +
+
String"alt" + Used by the image parser to grab the image attribute content (v2.17.5; moved to tablesorter core in v2.18.0; see config.parsers). +

+ Change this setting to grab a different image attribute to be used for sorting: +
$(function() {
+  $('table').tablesorter({
+    // parse image title (value to be used while sorting & filtering)
+    imgAttr : 'title',
+    headers : {
+      0 : { sorter: 'image' } // this parser is auto-detected, but will only work on the first image
+    }
+  });
+});
+
+
Deprecated/Removed Options
Stringundefined + This option is being deprecated in v2.21.3! + It has been replaced by widgetOptions.storage_fixedUrl; but is still available for backwards compatibility. +

+ This option was added to set a specific page when storing data using the $.tablesorter.storage code (v2.12). +
+ More specifically, when the storage function is used, it attempts to give every table a unique identifier using both the page url and table ID (or index on the page if no id exists). This option allows you to override the current page url (it doesn't need to be a url, just some constant value) and save data for multiple tables across a domain.
+
+ The table url & id can also be overridden by setting table data attributes data-table-page (url) and data-table-group (id)
(e.g. <table class="tablesorter" data-table-page="mydomain" data-table-group="financial">...</table>)
+
+ For a bit more detail, specifically on how to use the new storage function options for a custom widget, please refer to issue #389. +
+
StringThis option was removed! + It has been replaced by cssNoSort which does the opposite of what this class name was supposed to do. +
+

This option was not working as intended, so it was completely removed - sorry for the lack of notice.

+

Previous default was "tablesorter-allowClicks"

+ Class name added to table header which allows clicks to bubble up. (added v2.18.1; removed in v2.20.0). +
+
+ This option was removed in v2.21.2! + It has been replaced by widgetOptions.columns. +
+
+ Default value: { css: [ "primary", "secondary", "tertiary" ] } (Object with Array) +

+ When the column styling widget is initialized, it automatically applied the default class names of "primary" for the primary sort, "secondary" for the next sort, "tertiary" for the next sort, and so on (add more as needed)... (v2.0.17). + Use the widgetColumns option to change the css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["columns"], // initialize column styling of the table
+    widgetColumns: { css: ["primary", "secondary", "tertiary" ] }
+  });
+});
+
+ This option was removed in v2.4! + It has been replaced by widgetOptions.uitheme. +
+
+ Default value: { css: [ "ui-icon-arrowthick-2-n-s", "ui-icon-arrowthick-1-s", "ui-icon-arrowthick-1-n" ] } (Object with Array) +

+ Used when the ui theme styling widget is initialized. It automatically applies the default class names of "ui-icon-arrowthick-2-n-s" for the unsorted column, "ui-icon-arrowthick-1-s" for the descending sort and "ui-icon-arrowthick-1-n" for the ascending sort. (v2.0.9). + Find more jQuery UI class names by hovering over the Framework icons on this page: http://jqueryui.com/themeroller/
+
+ Use the widgetUitheme option to change the css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["uitheme"], // initialize ui theme styling widget of the table
+    widgetUitheme: {
+      css: [
+        "ui-icon-carat-2-n-s", // Unsorted icon
+        "ui-icon-carat-1-s",   // Sort up (down arrow)
+        "ui-icon-carat-1-n"    // Sort down (up arrow)
+      ]
+    }
+  });
+});
+
+ This option was removed in v2.4! + It has been replaced by widgetOptions.zebra. +
+
+ Default value: { css: [ "even", "odd" ] } (Object with Array) +

+ When the zebra striping widget is initialized, it automatically applied the default class names of "even" and "odd". + Use the widgetZebra option to change the css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["zebra"], // initialize zebra striping of the table
+    widgetZebra: { css: [ "normal-row", "alt-row" ] }
+  });
+});
+
+ + +

Widget & Pager Options

+ + + + + + + + + + + + + + + + +
Widget PriorityNameRequires jQueryLimiting function
30columnsv1.2.6
50filterv1.4.31.4.3 (nextUntil & delegate)
Lastpager pluginv1.2.6
55pager widgetv1.71.7 (on)
40resizablev1.4.1*1.4.1 (parseJSON)*
20saveSortv1.4.11.4.1 (parseJSON)*
60stickyHeadersv1.4.31.4.3 (isWindow)**
10uithemev1.2.6
90zebrav1.2.6
+ +
+

+ tablesorter widgets have many options, and to better organize them, they now are grouped together inside of the widgetOptions. Thanks to thezoggy for putting together this jQuery-widget compatibility table, but please note: +
    +
  • The applied order of widget is dependent of the widget priority, from low to high.
  • +
  • Widget priority values do not need to be unique. Any new widget without a defined priority will automatically have a priority of 10.
  • +
  • The pager, being a plugin, is actually initialized after tablesorter has initialized and all selected widgets applied.
  • +
  • * The saveSort and resizable widgets use the $.tablesorter.storage function by default and thus need the parseJSON function which is available in jQuery 1.4.1+.
  • +
  • ** The stickyHeaders widget was updated in v2.18.0 to use $.isWindow for the xScroll & yScroll options; and therefore now requires jQuery 1.4.3+.
  • +
+ +
+ TIP! Click on the link in the property column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PropertyTypeDefaultDescriptionLink
PropertyTypeDefaultDescriptionLink
Array[ "primary", "secondary", "tertiary" ] + Columns widget: When the column styling widget is initialized, it automatically applied the default class names of "primary" for the primary sort, "secondary" for the next sort, "tertiary" for the next sort, and so on (add more as needed)... (Modified v2.1). +
+
+ Use the "columns" option to change the css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["columns"], // initialize column styling of the table
+    widgetOptions : {
+      columns: [ "primary", "secondary", "tertiary" ]
+    }
+  });
+});
+
Example
Booleantrue + Columns widget: If true, the class names from the columns option will also be added to the table thead (v2.4). +
+
+ Use the "columns_thead" option to add the column class names to the thead as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["columns"], // initialize column styling of the table
+    widgetOptions : {
+      columns_thead: true
+    }
+  });
+});
+
Example
Booleantrue + Columns widget: If true, the class names from the columns option will also be added to the table tfoot (v2.4). +
+
+ Use the "columns_tfoot" option to add the column class names to the tfoot as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["columns"], // initialize column styling of the table
+    widgetOptions : {
+      columns_tfoot: true
+    }
+  });
+});
+
Example
Booleanfalse + Filter widget: If there are child rows in the table (rows with class name from "cssChildRow" option) and this option is true and a match is found anywhere in the child row, then it will make that row visible. + (Modified v2.1). +
+

*NOTE* When using this option, please be aware that all child row content will be obtained from each table cell using textContent, so none of the markup will be preserved. Also, carriage returns (<br>) will not be included. To account for the loss of white space, especially after carriage returns, please add an extra space to the end of the line. Using innerText, could have been an option for preserving the white space, but it is not standardized across all browsers (ref).

+ Use the filter_childRows option to include child row text as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_childRows : true
+    }
+  });
+});
+
Booleanfalse + Filter widget: If true, queries will search child row content by column (v2.22.0). +
+

The filter_childRows option must also be true for this option to work.

+

If false, and the filter_childRows option is true, then queries in any column will search all child content, as before this option was added.

+

+ Use the filter_childByColumn option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_childRows : true,
+      filter_childByColumn : true
+    }
+  });
+});
+
+
Example
Booleantrue + Filter widget: include all sibling rows of the matching child row (v2.23.4). +
+

Both filter_childRows & filter_childByColumn options must be set to true for this option to work.

+

If false, this option will only show the child row that matches the filter; and its parent row.

+ Use the filter_childWithSibs option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: [ "filter" ],
+    widgetOptions : {
+      filter_childRows     : true,
+      filter_childByColumn : true,
+      // only show matching child row & parent
+      filter_childWithSibs : false
+    }
+  });
+});
+
+
Example
Booleantrue + Filter widget: If true, allows using "#:{query}" in anyMatch searches (v2.20.0). +
+
+ Users can use the anymatch input to target a specific column, using a one-based index. +

+ For example: In the table below, searching for 2:aa in an anymatch filter will result in "Phillip Aaron Wong" and "Aaron" showing in the First Name column. +

+ See live examples in the Filter Widget External Search demo. +
+
Booleantrue + Filter widget: If true, a filter will be added to the top of each table column (v2.4). +
+
+ Use the filter_columnFilters option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_columnFilters : true
+    }
+  });
+});
+
String or Array""Filter widget: Additional CSS class applied to each filter cell (v2.18.0). +

+ When the filter row is built, each table cell (<td>) will get the class name from this option. +
    +
  • If this option is a plain string, all filter row cells will get the text applied as a class name. +
    // "table-filter-cell" class added to all filter row td's
    +filter_cellFilter : 'table-filter-cell'
    +
  • +
  • If this option is an array, then each filter row cell will get the text from the associate array element applied as a class name. +
    .hidden { display: none; }
    +
    // hiding second & fourth columns using associated css
    +filter_cellFilter : [ '', 'hidden', '', 'hidden' ]
    +
  • +
+

Use the filter_cellFilter option to add an extra css class name as follows:

+
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      // css class applied to the table row containing the filters & the inputs within that row
+      // or [ "filter-cell0", "filter-cell1", "filter-cell2", etc... ]
+      filter_cellFilter : "tablesorter-filter-cell"
+    }
+  });
+});
Note The cells from this option are also contained within the config.$filters variable. +
+
Example
String or Array""Filter widget: Additional CSS class applied to each filter (v2.15). +

+ As of v2.15, this option can also contain an array of class names that are to be applied to input filters.
+
+ Changed default to an empty string in v2.11, as the "tablesorter-filter" class will always be added to the filter; this option now contains any additional class names to add. +

Use the filter_cssFilter option to add an extra css class name as follows:

+
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      // css class applied to the table row containing the filters & the inputs within that row
+      // or [ "filter0", "filter1", "filter2", etc... ]
+      filter_cssFilter : "tablesorter-filter"
+    }
+  });
+});
+
+
Example
Object{ } + Filter widget: Add a default filter type to a column (v2.17.8). +
+
+ Warning If a column has a default filter set, the user will not be able to use other filters.
+
+ Use the filter_defaultFilter option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+
+      filter_defaultFilter : {
+        // target a column by class name or column index (zero-based)
+
+        '.fuzzy' : '~{q}',
+        2 : '{q}=',
+
+        // for default "anyMatches" use the column length as the index
+        // e.g. if your table has 5 columns (0,1,2,3,4), then set the anyMatch column as 5.
+        5 : '{q} or {q}' // any match column
+      }
+
+    }
+  });
+});
+ Set up the column string as follows: +
    +
  • Add the desired filter type symbol along with {query} or {q} to maintain positioning
  • +
  • Symbols can be added to the beginning "~{query}" (default fuzzy search) or end "{q}=" (default exact search)
  • +
  • For symbols that separate queries, like "AND", "OR" or "-" (range): +
      +
    • Add one additional {q} tag.
    • +
    • For example, to add a default "OR" search, use "{q} OR {q}".
    • +
    • If the user enters "a b c d" the column will be filtered using "a OR b OR c OR d", so there is no need to add more {q} tags within the string; adding more will likely mess up the results.
    • +
    • If the user only enters "a", then the column will be filtered using "a OR a" so as not to cause other issues.
    • +
    +
  • +
  • Note It is not possible to set up a default filter search within a query. So the "wild" filter search will not work as intended (e.g. {q}* and {q}? are essentially the same as {q}.
  • +
+
+
Example
Object{ }Filter widget: Additional CSS class applied to each filter (v2.17.8). + Exclude a filter type(s) for a column. +
+
+ Use the filter_excludeFilter option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+
+      filter_excludeFilter : {
+        // target a column by class name or column index (zero-based)
+        '.title' : 'range',
+
+        // separate multiple filter types using spaces
+        2 : 'range notMatch exact'
+      }
+
+    }
+  });
+});
+ Exclusion names must be separated by a comma. Here is a full list of filter type names: +
    +
  • and - logical AND type filter (using foo AND bar or foo && bar).
  • +
  • or - logical OR type filter (using foo OR bar or foo|bar).
  • +
  • exact - exact match (using " or =).
  • +
  • fuzzy - fuzzy match (~)
  • +
  • notMatch - not match (! or !=)
  • +
  • operators - comparison filters (< <= >= >)
  • +
  • range - range ( - or to )
  • +
  • regex - regex (/\d/)
  • +
  • wild - wild card matching (? for single characters, * for multiple characters not including spaces, or | or OR for a logical OR (the "or" filter type was separated from "wild" in v2.22.2).
  • +
+
+
String"" + Filter widget: jQuery selector string of inputs, outside of the table, to be used for searching table content (v2.15). +
+
+ Set this option to be a jQuery selector string, or jQuery object, pointing to any external inputs that are to be used for searching the table.
+
+ Use the filter_external option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_external : '.search'
+    }
+  });
+});
+ These external inputs have one requirement, they must have a data-column="#", where the # targets the column (zero-based index), pointing to a specific column to search.
+
<input class="search" type="search" data-column="0" placeholder="Search first column">
+ If you want to search all columns, using the updated "any match" method, set the data column value to "all":
+
<input class="search" type="search" data-column="all" placeholder="Search entire table">
+ The updated any matching code will now automatically update all associated inputs with the latest search. +
+ This option replaces filter_anyMatch. +
+
Example
String"filtered" + Filter widget: This is the class name applied to all rows that do not match the filter (hidden rows); used by pager plugin (v2.10). +
+
+ *NOTE* If creating a custom theme, make sure to include this definition (set to display:none or the filter widget will appear to be broken! +
+
String'Filter "{{label}}" column by...' + This option contains the ARIA-label value to be added to the filter input or select (v2.29.4). +
+

+ By default, a {{label}} is included in the setting to insert a column label. The content is obtained from the header cell's data-label attribute (do not include the data- prefix), if it exists, or the column header text. See the "First Name" column on the example page. +

+

+ More complex labels may be included by adding multiple "data-attributes". For example, the following option: +

filter_filterLabel : 'Filter the {{column-name}}, the {{ordinal-number}} column{{label-extra}}{{default-filter}}'
+ with this header cell HTML +
<th data-column-name="last name" data-ordinal-number="second" data-default-filter=", using a fuzzy search" data-label-extra="">
+  Last
+<th>
+ would result in an ARIA-label of 'Filter the last name, the second column, using a fuzzy search' +

+ Note: +
    +
  • The data- prefix must not be added in the placeholders.
  • +
  • The empty data-label-extra attribute did add any content to the resulting label.
  • +
  • If an included data-attribute does not exist on the header, it will be replaced by the header text.
  • +
+
+
Example
Objectnull + Filter widget: This option allows you to add custom controls within the filter widget row (v2.7.7; v2.17.0).
+
+
+ In v2.17.0, the filter_formatter column can also be referenced by using a jQuery selector (e.g. class name or ID) that points to a table header cell.
+
filter_formatter : {
+    ".col-value" : function($cell, indx) {
+      return $.tablesorter.filterFormatter.uiSpinner( $cell, indx, {
+        ...
+      });
+    }
+}
+ Warning What won't work is if you try to target the header using a filtering selector that uses an index, e.g. "th:eq()", ":gt()", ":lt()", ":first", ":last", ":even" or ":odd", ":first-child", ":last-child", ":nth-child()", ":nth-last-child()", etc.
+
+ A new file has been included named "widget-filter-formatter-jui.js" & "widget-filter-formatter-html5.js". The files include code to add jQuery UI and HTML5 controls via the filter_formatter option.
+
+ Most of the formatter functions have an option named valueToHeader which, when true adds a span to the header cell above the filter row and updates it with the current control's value (see example 2). If the option exists and is set to false, then the current value is added to the control's handle and css can be used to create a popup to show the current value (see example 1).
+
+ Another custom option named addToggle is included with the "uiSpinner", "html5Color" and "html5Number" code. This allows the user to disable the control to show all table rows. For the single sliders, "uiSlider" and "html5Range", the minimum value is used to clear the filter (show all rows).
+
+ The options included for each jQuery UI control only show basic options, but any or all of the jQuery UI options for that control can be included.
+
    +
  • + To add the jQuery UI slider, follow this example: +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions : {
    +      filter_formatter : {
    +        // column index `0` or use a jQuery selector `"th:contains('Discount')"`
    +        0 : function($cell, indx) {
    +          return $.tablesorter.filterFormatter.uiSpinner( $cell, indx, {
    +            value : 0,  // starting value
    +            min   : 0,  // minimum value
    +            max   : 50, // maximum value
    +            step  : 1,  // increment value by
    +            addToggle: true, // Add a toggle to enable/disable the control
    +            valueToHeader: false // add current slider value to the header cell
    +          });
    +        }
    +      }
    +    }
    +  });
    +});
    Any of the other jQuery UI spinner widget options can also be included.
    +
    +
  • +
  • Filter formatter functions include: "uiSpinner", "uiSlider", "uiRange" (uiSlider ranged), "uiDatepicker" (range only), "html5Number", "html5Range" and "html5Color".
  • +
  • + For other examples, please refer to the example pages. Formatter part 1 (example 1) adds jQuery UI controls to the filter row, while formatter part 2 (example 2) adds HTML5 controls, if supported, to the filter row. +
  • +
+
+
+ 1 + 2 +
Objectnull + Filter widget: Customize the filter widget by adding a select dropdown with content, custom options or custom filter functions (v2.3.6; v2.17.0). +
+
+ In v2.22.0, a data parameter was added to that long list of parameters.

+ *WARNING* In a future update, the filter function parameters will be cleaned up and changed as follows! +
filter_functions : {
+  // function(e, n, f, i, $r, c, data) {} <- current parameters
+  0 : function(c, data) {} // planned change (version undetermined)
+}
The e (exact table cell text), n (normalized table cell text), f (filter input value), i (column index) and $r (current row; jQuery object) are all already included in the data object. +

+ In v2.17.0, the filter_functions column can also be referenced by using a jQuery selector (e.g. class name or ID) that points to a table header cell.
+
filter_functions : {
+    ".col-date" : {
+        "< 2004" : function (e, n, f, i, $r, c, data) {
+            return n < Date.UTC(2004, 0, 1); // < Jan 1 2004
+        },
+        ...
+    }
+}
+ Warning What won't work is if you try to target the header using a filtering selector that uses an index, e.g. "th:eq()", ":gt()", ":lt()", ":first", ":last", ":even" or ":odd", ":first-child", ":last-child", ":nth-child()", ":nth-last-child()", etc.
+
+ Use the "filter_functions" option in three different ways: +
+
    +
  • + Make a sorted select dropdown list of all column contents. Repeated content will be combined. +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions: {
    +      filter_functions: {
    +        // Add select menu to this column
    +        // set the column value to true, and/or add "filter-select" class name to header
    +        0 : true
    +      }
    +    }
    +  });
    +});
    + Alternately, instead of setting the column filter funtion to true, give the column header a class name of "filter-select". See the demo.

    +
  • +
  • + Make a select dropdown list with custom option settings. Each option must have a corresponding function which returns a boolean value; return true if there is a match, or false with no match. + +

    Regex example

    +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions: {
    +      // function variables:
    +      // e = exact text from cell
    +      // n = normalized value returned by the column parser
    +      // f = search filter input value
    +      // i = column index
    +      // $r = jQuery element of current row
    +      // c = table.config
    +      // data = combined filter data - see filter widget "custom searches" demo, look under "How to add Custom filter types"
    +      filter_functions: {
    +        // Add these options to the select dropdown (regex example)
    +        2 : {
    +          "A - D" : function(e, n, f, i, $r, c, data) { return /^[A-D]/.test(e); },
    +          "E - H" : function(e, n, f, i, $r, c, data) { return /^[E-H]/.test(e); },
    +          "I - L" : function(e, n, f, i, $r, c, data) { return /^[I-L]/.test(e); },
    +          "M - P" : function(e, n, f, i, $r, c, data) { return /^[M-P]/.test(e); },
    +          "Q - T" : function(e, n, f, i, $r, c, data) { return /^[Q-T]/.test(e); },
    +          "U - X" : function(e, n, f, i, $r, c, data) { return /^[U-X]/.test(e); },
    +          "Y - Z" : function(e, n, f, i, $r, c, data) { return /^[Y-Z]/.test(e); }
    +        }
    +      }
    +    }
    +  });
    +});
    +

    Comparison example

    +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions: {
    +      // function variables:
    +      // e = exact text from cell
    +      // n = normalized value returned by the column parser
    +      // f = search filter input value
    +      // i = column index
    +      // $r = jQuery element of current row
    +      // c = table.config
    +      // data = combined filter data - see filter widget "custom searches" demo, look under "How to add Custom filter types"
    +      filter_functions: {
    +        // Add these options to the select dropdown (numerical comparison example)
    +        // Note that only the normalized (n) value will contain numerical data
    +        // If you use the exact text, you'll need to parse it (parseFloat or parseInt)
    +        4 : {
    +          "< $10"      : function(e, n, f, i, $r, c, data) { return n < 10; },
    +          "$10 - $100" : function(e, n, f, i, $r, c, data) { return n >= 10 && n <=100; },
    +          "> $100"     : function(e, n, f, i, $r, c, data) { return n > 100; }
    +        }
    +      }
    +    }
    +  });
    +});
    + Note: if the filter_ignoreCase option is true, it DOES alter the normalized value (n) by making it all lower case.

    +
  • +
  • + Make a custom filter for the column. +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions: {
    +      // function variables:
    +      // e = exact text from cell
    +      // n = normalized value returned by the column parser
    +      // f = search filter input value
    +      // i = column index
    +      // $r = jQuery element of current row
    +      // c = table.config
    +      // data = combined filter data - see filter widget "custom searches" demo, look under "How to add Custom filter types"
    +      filter_functions: {
    +        // Exact match only
    +        1 : function(e, n, f, i, $r, c, data) {
    +          return e === f;
    +        }
    +      }
    +    }
    +  });
    +});
    + Note: if the filter_ignoreCase option is true, it DOES alter the normalized value (n) by making it all lower case.

    +
  • +
+
+
Example
Booleantrue + Filter widget: Set this option to false to always show the filter row; by default, the filter row is completely hidden when no rows exist within the tbody (v2.15). +
+
+ Use the filter_hideEmpty option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_hideEmpty : false
+    }
+  });
+});
+
Boolean, or Functionfalse + Filter widget: Set this option to true to hide the filter row initially. The row is revealed by hovering over the visible portion of the filter row or by giving any filter input/select focus (tab key) (v2.4; v2.26.6). +
+
+ Use the filter_hideFilters option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_hideFilters : true
+    }
+  });
+});
You can change the style (thickness) of the hidden filter row in the tablesorter theme css. Look for .tablesorter-filter-row (revealed row) and .tablesorter-filter-row.hideme (for the hidden row) css definitions. + In v2.26.6, this setting can also contain a function. +
    +
  • When this function returns a boolean value, it signifies whether the filter row should or should not be hidden (see the details in this issue.
  • +
  • If this function does not return a boolean value, the value of the filters are checked and if no search queries are found, the filter row will be hidden.
  • +
+ Example of function setting: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_hideFilters : function(config) {
+        // get an array of filter queries (don't use the value from
+        // `config.lastSearch` because this code will modify it;
+        // unless you extend it)
+        var search = $.tablesorter.getFilters(config.$table);
+        // ignore any query is column 2 (zero-based index)
+        search.splice(2,1);
+        // return true to hide the filter row, false to show it
+        return search.join("") === "";
+      }
+    }
+  });
+});
+
Example
Booleantrue + Filter widget: Set this option to false to make the column content search case-sensitive, so typing in "a" will not find "Albert". (v2.3.4) +
+
+ Use the filter_ignoreCase option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_ignoreCase : false
+    }
+  });
+});
+
Example
Boolean, Number or Objecttrue + Filter widget: If true, a search of the column content will occur as the user types, with the delay set in the filter_searchDelay option (v2.9; v2.27.3). +
+
+ This option, when false allows you to disable the live search behavior, so that a filter is only applied when the user presses Enter (or uses Esc to clear and cancel the search).
+
+ If this option is set to a number, e.g. 4, a search of the column content will not initiate until this minimum number of characters are entered into the input. +

In v2.27.3, this option can be set as an object containing specific column zero-based indexes, or class names. For undefined columns, include a "fallback" value otherwise undefined columns will be set as false.

+
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_liveSearch : {
+        // when false, the user must press enter to blur the input to trigger the search
+        3 : false,
+        // the query will initiate when 5 or more characters are entered into the filter
+        4 : 5,
+        // no live search on the last three columns (using a header class name)
+        '.last-3-columns' : false,
+        // for columns that aren't defined; this will set the fallback value
+        // otherwise the fallback defaults to false.
+        'fallback' : true
+      }
+    }
+  });
+});
+
+
Object{ 'input': 'exact', 'select': 'exact' } + Filter widget: This option sets the global setting that applied to either input or select filters (v2.25.5) +
+

A basic input filter will match the filter query until a "and" and "or" type search is performed.

+

By default, select filters and "and" and "or type searchs will look for an exact match, and to override this behavior you *had* to add a "filter-match" class to the columns to allow partial matches.

+

This option allows you to set a global "match" setting and save you the trouble of adding a bunch of classes.

+

There are only two options: "exact" and "match"; and it defaults to "exact" if anything else is entered.

+ In the demo, try searching for super or man in the first column. +
+
Example
String"filter-onlyAvail" + Filter widget: If a header contains a select dropdown and this class name, only the available (visible) options in the column will show (v2.10.1; v2.17.6). +
+
+ In v2.17.6, columns with the only available class name set, that are not currently being filtered will only show the available options. Conversely, the column(s) with a selected option will show all options.
+
+ This option is useful after one or more columns have been filtered, then the column select filter with this class applied will only show the contents of the column within the dropdown that are currently visible. See the custom filter widget demo "Discount" column for an example (sort the "First Name" column first).
+
+ Caution: The main issue with this functionality is with keyboard accessibility. If the keyboard is used to select an option, only the first and default options will be available for chosing. The only way to select other options is with the mouse. +
+
Example
object{ search : '', select : '' } + Filter widget: Set global filter input placeholder text for search inputs and selects (v2.16). +
+
+ Any search type input added to the filter row will automatically get a placeholder attribute added, the source of this placeholder text is from the following sources, in order of priority: +
    +
  • Header cell data (table.config.$headers.eq(0).data('placeholder', 'search for...');
  • +
  • Header cell data-attribute (<th data-placeholder="Find Rank...">Rank</th>)
  • +
  • Global filter_placeholder.search setting.
  • +
+ The filter_placeholder.select setting adds the text to the first select option (default option to show all rows).
+
+ Note: The widget-filter-formatter-jui.js jQuery UI Datepicker Range Selector creates two inputs, so this option then includes two additional settings:
+
+
filter_placeholder : {
+	search : '',
+	select : '',
+	from   : '', // datepicker range "from" placeholder
+	to     : ''  // datepicker range "to" placeholder
+}
+
Note: The browser must support the placeholder attribute before it will be visible. +
+
Example
String, jQuery objectnull + Filter widget: jQuery selector string of an element used to reset the filters (v2.4; v2.16). +
+

+ When this option points to a reset element using a jQuery selector string, it is bound using event delegation. So if any additional reset elements, with the same class name, are added to the page dynamically, they will be associated with the same table.
+
+ For example, add this button (<button class="reset">Reset</button>) to the table header, or anywhere else on the page. That element will be used as a reset for all column and quick search filters (clears all fields): +

+ Use the filter_reset option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_reset : '.reset'
+    }
+  });
+});
+ If this option contains a jQuery object (v2.16), clicking on any of the elements within that jQuery object will trigger a filter reset. If any additional elements with the same selector are added to the page, they will not be dynamically functional.
+
+ If either of these methods do not work as desired, simply trigger a filterReset event on the table. +
+
Example
Booleantrue + Filter widget: when true, normalize pressing escape to clear filter input across browsers (v2.25.2). +
+

Webkit browsers automatically clear the filter search input when pressing escape, but IE and Firefox do not. Setting this option to true, clears the filter input when the user presses escape.

+ When this option is set to false, pressing escape inside the search field is ignored. +
+
Booleanfalse + Filter widget: If the storage utility is available (included with jquery.tablesorter.widgets.js file, the last applied filter is saved to storage (v2.14). +
+
+ Filters saved to local storage (or cookies) will override any default filters within the header data-attribute (set by the filter_defaultAttrib option) and be available to the pager before any ajax calls are made.
+
+ To bypass this behavior, clear out the saved filters as follows: +
$.tablesorter.storage( $('table'), 'tablesorter-filters', '' );
+
+
Example
Numeric300 + Filter widget: Set this option to the number of milliseconds to delay the search. (v2.3.4). +
+
+ Use the filter_searchDelay option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_searchDelay : 500
+    }
+  });
+});
+ + If you want to want to initialize the filter without user input, target any one of the filters and trigger a "search". + +
// target the first filter input
+// this method will begin the search after the searchDelay time
+$('input.tablesorter-filter:eq(0)').trigger('search');
+
+// this method will begin the search immediately
+$('input.tablesorter-filter:eq(0)').trigger('search', false);
+ + In tablesorter v2.4+, the trigger can be applied directly to the table: +
// refresh the widget filter; no delay
+$('table').trigger('search', false);
+
Booleantrue + Filter widget: Set this option to allow searching through already filtered rows (in special conditions); this will speed up searching in large tables (v2.17.4). +
+
+ To better explain this, lets do it by example. Lets say you have a column of color names and you enter "light" and results like "light blue" and "light grey" show up.
+
+ When you press the space bar, a space is added, and instead of searching though all of the colors again, the filter widget only searches through the already filtered results that started with "light". This can substantially speed up searching, especially in large tables.
+
+ But, there are some special circumstances which would make this method return incorrect results. So, this option was added to allow you to disable this feature in case one of these following conditions doesn't cover your need; but still, please report any issues!
+
+ The search through filtered results only occurs if the following conditions are met: +
    +
  • The last search (for all columns) was not completely empty - all rows will be searched anyway.
  • +
  • If there were no changes to the search from the beginning of the search string (changing the above search to "bright" will force a new search of all rows).
  • +
  • If the search does not contain a logical or ( or or |), or a range delimiter ( - or to ) within the search query.
  • +
  • If the search is not looking for an exact match (" or =) or a logical not search (!).
  • +
  • Or, if the search is using a select dropdown without a "filter-match" class name (looking for an exact match).
  • +
  • If the search does not contain an operator greater than or equal to a negative number (>=-10) or less than or equal to a positive number (<=10).
  • +
  • And lastly, only search filtered rows if all rows are not hidden.
  • +
+ If the debug option is set to true, then a message will appear while filtering stating the specific number of rows, or "all" rows, that are being searched.
+
+ Use the filter_searchFiltered option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_searchFiltered : false
+    }
+  });
+});
+
Example
Functionnull + Filter widget: Include a function to return an array of values to be added to the column filter select (v2.16.0; v2.28.12). +
+
+ In v2.24.4, this option will now accept an array of objects which can contain extra information for each option. Here is an example modified from the filter selectmenu demo: +
filter_selectSource  : {
+  0 : [
+    { value : '/\\.js$/',            'data-class' : 'ui-icon-script',   text : 'javascript' },
+    { value : '/\\.(jpg|png|gif)$/', 'data-class' : 'ui-icon-image',    text : 'Image' },
+    // plain strings are also acceptable - the string is added to both the text & value attribute
+    'foobar',
+    { value : '.css',                'data-class' : 'ui-icon-note',     text : 'CSS' },
+    { value : '.html',               'data-class' : 'ui-icon-document', text : 'HTML page' },
+    { value : '/\\.(json|txt|md)$/', 'data-class' : 'ui-icon-help',     text : 'Misc' },
+  ]
+}
+
    +
  • Each object entry must include a text property, or it will get ignored.
  • +
  • If no value property is set, the option value will be equal to the text (IE requires a value defined for every option).
  • +
  • Plain strings are still allowed within the array. If included, both the option text & value attribute will contain the string.
  • +
  • *NOTE* when returning an array containing objects, the options will not be reduced to obtain unique selections.
  • +
  • *NOTE* because this example is providing a fixed select option source, it can not support "filter-onlyAvail" (only show available options after filtering).
  • +
+ +

+ In v2.28.12, return null from a filter_selectSource function to prevent updates to the select options. Useful if you are updating the select options from outside of tablesorter. +

filter_selectSource : {
+  ".filter-select" : function() { return null; }
+}
+

+

+ In v2.27.7, an option to have a descending sort applied to this data can be done by adding a "filter-select-sort-desc" to the header cell. Adding a "filter-select-nosort" class name to a header to prevent sorting has been available since v2.16.3. +

+

+ In v2.21.5, this option will now override the filter_function options (so you need to add them back!), allowing the addition of custom select options and still maintain basic filtering action - see this demo (ref). +

+ In v2.17.0, the filter_selectSource column can also be referenced by using a jQuery selector (e.g. class name or ID) that points to a table header cell.
+
filter_selectSource : {
+  ".model-number" : [ "abc", "def", "ghi", "xyz" ]
+}
+ Warning What won't work is if you try to target the header using a filtering selector that uses an index, e.g. "th:eq()", ":gt()", ":lt()", ":first", ":last", ":even" or ":odd", ":first-child", ":last-child", ":nth-child()", ":nth-last-child()", etc.
+
+ A column will have a filter select dropdown when a "filter-select" class name is added to the header cell, or if the filter_functions column value is set to true
+
+ This option allows using an alternate source, or customizing options of the filter select dropdown. This option can be set as follows: +
    +
  • null - this value will set the default behavior and return all table cell values from the current column.
  • +
  • An overall function - when this option is a function, it will be used for all filter selects in the table. +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions : {
    +      filter_selectSource : function(table, column, onlyAvail) {
    +        // get an array of all table cell contents for a table column
    +        var array = $.tablesorter.filter.getOptions(table, column, onlyAvail);
    +        // manipulate the array as desired, then return it
    +        return array;
    +      }
    +    }
    +  });
    +});
    +
  • +
  • An object containing column keys set with a function - when the option is set in this manner, a function can be applied to a specific column. +

    This example was updated in v2.23.4 to use the buildSelect function directly (ref)

    +
    $(function() {
    +  $("table").tablesorter({
    +    widgets: ["filter"],
    +    widgetOptions : {
    +      filter_selectSource : {
    +        0 : function(table, column, onlyAvail) {
    +          // call ajax after tablesorter has initialized; this prevents
    +          // multiple ajax calls during initialization
    +          if (table.hasInitialized) {
    +            $.getJSON('ajax/options.json', function(data) {
    +              // return false if there is a problem and the select
    +              // will display the original defaults
    +              var result = data.hasOwnProperty('options') ? data.options : false;
    +              // if not already done on the server-side & you want to sort & remove duplicates
    +              // from the results, use the processOptions function (added 2.23.4)
    +              result = $.tablesorter.filter.processOptions( table, column, result );
    +              // call the buildSelect function; pass `true` to replace the contents of the select
    +              $.tablesorter.filter.buildSelect( table, column, result, true, onlyAvail );
    +            });
    +          }
    +          return false;
    +        }
    +      }
    +    }
    +  });
    +});
    +
  • +
+ The function uses the following parameters: +
    +
  • table - table DOM element
  • +
  • column - zero-based index of the column with a filter select
  • +
  • onlyAvail - boolean indicating if the returned options should only be from available (non-filtered) rows.
  • +
+ Important Return an array of values which will be added to the filter select dropdown. This array will automatically be stripped of any duplicate values and sorted alphanumerically before being added to the select. If for some reason the custom filter_selectSource function does not obtain the desired array of values, return false and the original method of obtaining column cell content will be used. +
+
1 2
String'|' + Filter widget: Set this option to be any separator (v2.17.6) that is to be used within the filter_selectSource returned array. +
+
+ Use the filter_selectSourceSeparator option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_selectSource : {
+        0 : [ 'en|English', 'fr|French', 'de|German' ]
+      },
+      // filter_selectSource array text left of the separator is added to the option value, right into the option text
+      filter_selectSourceSeparator : '|'
+    }
+  });
+});
Results in this HTML: +
<select>
+	<option value=""></option>
+	<option value="en">English</option>
+	<option value="fr">French</option>
+	<option value="de">German</option>
+</select>
This also works for the filter_functions +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions: {
+      filter_functions: {
+        // Add these options to the select dropdown (regex example)
+        1 : {
+          "<10|Less than 10" : function(e, n, f, i, $r, c, data) { return n < 10; },
+          "10 - 100|Between 10 & 100" : function(e, n, f, i, $r, c, data) { return n >= 10 && n <=100; },
+          ">100|Greater than 100" : function(e, n, f, i, $r, c, data) { return n > 100; }
+        }
+      },
+      // filter_selectSource array text left of the separator is added to the option value, right into the option text
+      filter_selectSourceSeparator : '|'
+    }
+  });
+});
+ Results in this HTML (this adds a data-function-name attribute to keep a reference to the associated filter_functions. +
<select>
+  <option value=""></option>
+  <option data-function-name="&lt;10|Less than 10" value="&lt;10">Less than 10</option>
+  <option data-function-name="10 - 100|Between 10 &amp; 100" value="10 - 100">Between 10 & 100</option>
+  <option data-function-name="&gt;100|Greater than 100" value="&gt;100">Greater than 100</option>
+</select>
+
+
Booleanfalse + Filter widget: Set this option to true if filtering is performed on the server-side (v2.5.3). +
+
+ Use the filter_serversideFiltering option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_serversideFiltering : true
+    }
+  });
+});
+
Booleanfalse + Filter widget: Set this option to true to use the filter to find text from the start of the column, so typing in "a" will find "albert" but not "frank", both have a's. (v2.1). +
+
+ Use the filter_startsWith option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_startsWith : true
+    }
+  });
+});
+
Example
Booleanfalse + Filter widget: If true, ALL filter searches will only use parsed data (v2.4). +
+
+ Use the filter_useParsedData option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_useParsedData : false
+    }
+  });
+});
+
    +
  • To only use parsed data in specific columns, set this option to false and use any of the following (they all do the same thing), set in order of priority: +
      +
    • jQuery data data-filter="parsed".
    • +
    • metadata class="{ filter: 'parsed'}". This requires the metadata plugin.
    • +
    • headers option headers : { 0 : { filter : 'parsed' } }.
    • +
    • header class name class="filter-parsed".
    • +
    +
  • +
  • Remember that parsed data most likely doesn't match the actual table cell text, 20% becomes 20 and Jan 1, 2013 12:01 AM becomes 1357020060000.
  • +
+
+
String"data-value" + Filter widget: This option contains the name of the data-attribute which contains the default (starting) filter value (v2.10.8). +
+
+ Use the filter_defaultAttrib option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_defaultAttrib : 'data-value'
+    }
+  });
+});
Then add the default filter value to the table header as follows:
<th data-value="<30">Age</th>
+
Example
String""Sticky Headers widget: This additional CSS class applied to the sticky header row (v2.11). +

+ Changed to empty string in v2.11, as the "tablesorter-stickyHeader" class will always be added to the sticky header row; this option now contains any additional class names to add. +

Previously, this option contained the class name to be applied to the sticky header row (tr) (v2.1).

+ Use the "stickyHeaders" option to add an extra css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // css class name applied to the sticky header
+      stickyHeaders : "tablesorter-stickyHeader"
+    }
+  });
+});
+
Example
String"-sticky" + Sticky Headers widget: If the table has an ID defined, the suffix from this option will be added to the ID in the cloned sticky table (v2.9). +
+
+ So if your table ID is "gummy", then the cloned sticky table id becomes "gummy-sticky"
+
+ Use the "stickyHeaders_cloneId" option to change the cloned table id as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // cloned table id suffix
+      stickyHeaders_cloneId : "-clone"
+    }
+  });
+});
+
Booleantrue + Sticky Headers widget: If this option is false and a caption exists, it will not be included in the sticky header (v2.10.8). +
+
+ Use the stickyHeaders_includeCaption option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // cloned table id suffix
+      stickyHeaders_includeCaption : false
+    }
+  });
+});
+
Example
String, jQuery Objectnull + Sticky Headers widget: jQuery selector or object to physically attach the sticky headers (v2.27.0). +
+

+ Note Appending the sticky headers to an element that does not have the same dimensions (width) as the table wrapper will produce a sticky header that does not match the table size! +

+ Use the stickyHeaders_appendTo option as follows:
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // Where to attach the stickyHeaders
+      stickyHeaders_appendTo : '.wrapper' // $('.wrapper') jQuery object can also be used
+    }
+  });
+});
+
String, jQuery Objectnull + Sticky Headers widget: points to the table wrapper to stick the headers to while scrolling. Use this option to point to their needs (v2.14.4). +
+
+ Use the stickyHeaders_attachTo option as follows:
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // Where to attach the stickyHeaders
+      stickyHeaders_attachTo : '.wrapper' // $('.wrapper') jQuery object can also be used
+    }
+  });
+});
+
Example
String, jQuery Objectnull + Sticky Headers widget: points to the element in which to monitor for horizontal scrolling (v2.18.0). +
+
+ If undefined (or null), the window element will be monitored.
+
+ Use the stickyHeaders_xScroll option as follows:
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window)
+      stickyHeaders_xScroll : '.wrapper' // $('.wrapper') jQuery object can also be used
+    }
+  });
+});
+
Example
String, jQuery Objectnull + Sticky Headers widget: points to the element in which to monitor for vertical scrolling (v2.18.0). +
+
+ If undefined (or null), the window element will be monitored.
+
+ Use the stickyHeaders_yScroll option as follows:
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window)
+      stickyHeaders_yScroll : '.wrapper' // $('.wrapper') jQuery object can also be used
+    }
+  });
+});
+
Example
Multiple0 + Sticky Headers widget: Set the sticky header offset from the top as a Number or jQuery selector string or object (v2.10). +
+
+ If the page includes a fixed navigation bar at the top, like Bootstrap, set "stickyHeaders_offset" option to offset the sticky table header to be below the fixed navigation by setting this option using any of the following examples:
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // apply sticky header top 30px below the top of the browser window
+      stickyHeaders_offset : 30
+    }
+  });
+});
or +
stickyHeaders_offset : '.navbar-fixed-top' // jQuery selector string
or +
stickyHeaders_offset : $('.navbar-fixed-top') // jQuery object
+
Booleantrue + Sticky Headers widget: Scroll table top into view after filtering (v2.16.2). +
+
+ When a user searches the table using the sticky header filter row the results may reduce the number of rows so that the table would scroll up out of the viewport. So, this option scrolls the table top into view and moves the filter focus to the same input in the original header, instead of the sticky header input.
+
+ Set this option to false to prevent the page scroll after filtering the table. +
+
Booleantrue + Sticky Headers widget: If true, sticky table headers will resize automatically when content is added to or removed from the table headers (v2.10). +
+
+ While this option is true, a timer is initialized to check the width of every header cell every 1/4 second. If this causes lag, or any other problems, set this option to false. + When this option is false, sticky table headers are unable to detect and match the width of the original table headers when content is added or removed.
+
+ Use the "stickyHeaders_addResizeEvent" option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["resizable"],
+    widgetOptions : {
+      // add header resize detection
+      stickyHeaders_addResizeEvent : true
+    }
+  });
+});
When the browser window is resized, the headers (original and sticky) will resize automatically no matter the value of this option. +
+
Numeric2 + Sticky Headers widget: The zIndex added to the stickyHeaders. This option allows the user to adjust the value to their needs (v2.11). +
+
+ Use the stickyHeaders_zIndex option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["stickyHeaders"],
+    widgetOptions : {
+      // The zIndex of the stickyHeaders, allows the user to adjust this to their needs
+      stickyHeaders_zIndex : 100
+    }
+  });
+});
+
Booleantrue + Resizable widget: If this option is set to false, resized column widths will not be saved. Previous saved values will be restored on page reload (v2.4). +
+
+ Use the "resizable" option to not save the resized widths: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["resizable"],
+    widgetOptions : {
+      // css class name applied to the sticky header
+      resizable : false
+    }
+  });
+});
+
Example
Booleanfalse + Resizable widget: If this option is set to true, a resizing anchor will be included in the last column of the table (v2.8.3). +
+
+ If an anchor was included and the table is full width, the column would resize in the opposite direction which my not be intuitive to the user. So set this option as desired, but please be mindful of the user experience.
+
+ Use the "resizable_addLastColumn" option to include the last column resizer as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["resizable"],
+    widgetOptions : {
+      // css class name applied to the sticky header
+      resizable_addlastcolumn : true
+    }
+  });
+});
+
Example
BooleantrueResizable widget: If this option is set to true, the resizable handle will extend into the table footer (v2.28.8). +
+

When true, this option includes the entire height of the table footer. If the table does not include a footer, the resize handle will stop at the last row.

+

If false, the resizable handle will not extend into the table footer.

+
+
Array[] + Resizable widget: Set this option to the starting & reset header widths (v2.15.12). +
+
+ Use the "resizable_widths" option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["resizable"],
+    widgetOptions : {
+      // headers widths applied at initialization & resizable reset
+      // this setting includes any non-resizable cells (resizable-false)
+      resizable_widths : [ '10%', '10%', '50px' ]
+    }
+  });
+});
+
Example
Booleanfalse + Resizable widget: Set this option to throttle the resizable events (v2.17.4). +
+
+ When false throttling of the mousemove (resizing) event is not applied.
+
+ Set this option to either true for a default 5 millisecond delay, or set it to any number less than 10 to adjust the throttling delay that is applied to the mousemove/resizing event.
+
+ Use the "resizable_throttle" option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["resizable"],
+    widgetOptions : {
+      // set to true for a default 5ms throttling delay
+      // or set to a number < 10 (more than that makes the resizing adjustment unusable
+      resizable_throttle : true
+    }
+  });
+});
+
Booleanfalse + Resizable widget: When true, the last column will be targeted for resizing (v2.21.3). +
+

When true, resizing a column will change the size of the selected column, and the last column, not the selected column's neighbor.

+

When false, resizing a column will move the column border between it's neighbors.

+

Also, in a full width table, if this option is false, the same behavior as when this option is true can be seen when resizing a column while holding down the Shift key on the keyboard - the last column is resized.

+
+
Booleantrue + SaveSort widget: If this option is set to false, new sorts will not be saved. Any previous saved sort will be restored on page reload (v2.4). +
+
+ Use the "saveSort" option to not save the current sort: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["saveSort"],
+    widgetOptions : {
+      // if false, the sort will not be saved for next page reload
+      saveSort : false
+    }
+  });
+});
+
Example
String"" + Storage widget: Set this option to the first letter of the desired storage type (v2.28.8). +
+

If any value is set in this option, the deprecated "storage_useSessionStorage" is completely ignored.

+
    +
  • When set to "s" (session), all saved variables for the table use sessionStorage. This means once the user closes the browser, all saved variables are lost.
  • +
  • When set to "c" (cookie), all saved variables for the table will use cookies.
  • +
  • Any other setting will switch to using the default localStorage type.
  • +
+ Use the "storage_storageType" option as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["saveSort"],
+    widgetOptions : {
+      // This sets session storage; only the first letter is required
+      storage_storageType : "session"
+    }
+  });
+});
+
Booleanfalse + Storage widget: This option was deprecated in v2.28.8. +
+

In v2.28.8, this option is replaced by the "storage_storageType" option and will be completely ignored if that option has any setting.

+

If this option is set to false, all saved variables for the table will be within local storage (v2.21.3).

+

If true, all saved variables for the table will be within the session storage. This means once the user closes the browser, all saved variables are lost.

+ Use the "storage_useSessionStorage" option to switch to session storage as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["saveSort"],
+    widgetOptions : {
+      // if false, saved variables will be saved in local storage
+      storage_useSessionStorage : true
+    }
+  });
+});
+
Example
String"" + Storage widget: This option allows setting an alternate table id so multiple tables on a page have the settings grouped together +
+

When grouping together multiple tables, setting the variable on one table will not cause the setting to modify a second table, the change will only be apparent on page reload.

+

If this option is not defined, the actual table id will be used (which isn't conducive to applying to multiple tables!)

+

A more detailed list of the table id priority settings can be found in the $.tablesorter.storage function description.

+
+
String"" + Storage widget: Set a table (data) attribute to use to obtain a table id, which allows grouping multiple tables on one page together - they share a common saved setting. +
+

If a value is set in this option, the storage widget looks in that defined table data-attribute for a table id.

+

If this option is not defined, then the default data-attribute for the table becomes "data-table-group".

+

The value in the data-attribute sets a table id, if not found, the storage widget then looks for an id in the storage_tableId option.

+
+
String"" + Storage widget: This option allows setting an alternate table url so that tables on multiple pages will have their settings grouped together +
+

This option replaces and will override the now deprecated config.fixedUrl option.

+

If there is a value within the table's "data-table-page" (set by the storage_page option), it will override this setting.

+

A more detailed list of the table url priority settings can be found in the $.tablesorter.storage function description.

+
+
String"" + Storage widget: Set a table (data) attribute to use to obtain a table url, which allows grouping tables across multiple pages together - they share a common saved setting. +
+

If a value is set in this option, the storage widget looks in that defined table data-attribute for a table url.

+

If this option is not defined, then the default data-attribute for the table becomes "data-table-page".

+

The value in the data-attribute sets a table url, if not found, the storage widget then looks for a url in the storage_fixedUrl option.

+
+
Array[ "even", "odd" ] + Zebra widget: When the zebra striping widget is initialized, it automatically applied the default class names of "even" and "odd". (Modified v2.1). +
+
+ Use the "zebra" option to change the theme as follows: +
$(function() {
+  $("table").tablesorter({
+    widgets: ["zebra"], // initialize zebra striping of the table
+    widgetOptions: {
+      zebra: [ "normal-row", "alt-row" ]
+    }
+  });
+});
+
Example
Pager
Objectnull + Target your one or more pager markup blocks by setting this option with a jQuery selector. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.container; additionally you can change the class name that is applied to the pager (used within the jquery.tablesorter.pager.css file) by modifying the widgetOption.pager_css.container class name (default is "tablesorter-pager")
+
+ This is some example pager markup. It should contain all of the controls set by the multiple css-named options:
+<div class="pager">
+  <form>
+    <img src="first.png" class="first"/>
+    <img src="prev.png" class="prev"/>
+    <span class="pagedisplay"></span> <!-- this can be any element, including an input -->
+    <img src="next.png" class="next"/>
+    <img src="last.png" class="last"/>
+    <select class="pagesize">
+      <option value="10">10</option>
+      <option value="20">20</option>
+      <option value="30">30</option>
+      <option value="40">40</option>
+    </select>
+    <select class="gotoPage" title="Select page number"></select>
+  </form>
+</div>
Caution If you use buttons in your pager container, make sure the buttons include a button type (<button type="button">Next</button>) to prevent form submission and page reloading every time the button is clicked.
+
Use this option as follows:
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      container: $(".pager")
+    });
+});
+
Example
Stringnull + Set this option to include a url template to use so that the pager plugin can interact with your database (v2.1; v2.9). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_ajaxUrl
+
+ The tags within the ajaxUrl string are optional. If do not want the user to change the page size, then you only need to include the page in this string: +
ajaxUrl: "http://mydatabase.com?start={page}"
+ If you need to send your server a page offset (actual starting record number), then you'll need to use the customAjaxUrl option.
+
+ Here is an example of how to include the option, it should always be paired with an ajaxProcessing function: +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxUrl: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}",
+      ajaxProcessing: function(data, table, xhr) {
+        // do something with the ajax data
+        return [ formatted_data, total_rows ];
+      }
+    });
+});
The ajaxUrl template replaces the following tags with values from the tablesorter plugin and pager addon: +
+ + + + + + + + + + + + + + + + + +
TagReplaced with
{page}Zero-based index of the current pager page
{page+1}One-based index of the current pager page (replace "+1" with any number) (e.g. {page+3}) (v2.9).
{size}Number of rows showing, or number of rows to get from the server
{sortList:col} or {sort:col}Adds the current sort to the ajax url string into a "col" array, so your server-side code knows how to sort the data (v2.4.5).
+ The col portion of the {sortList:col} tag can be any name string (no spaces) to indicate the name of the variable to apply. So if your current sortList is [[2,0],[3,0]], it becomes "&sort[2]=0&sort[3]=0" in the url. {sort:col} shortened tag also works (v2.9). +
{filterList:fcol} or {filter:fcol}Adds the value of the current filters to the ajax url string into a "fcol" array, so your server-side code knows how to filter the data (v2.6).
+ The fcol portion of the {filterList:fcol} tag can be any name string (no spaces) to indicate the name of the variable to apply. So if your current filters are ['','Blue',13], it becomes "&fcol[2]=Blue&fcol[3]=13" in the url. {filter:col} shortened tag also works (v2.9). +
+
Example
Functionfunction(table, url) { return url; } + This callback function allows you to modify the processed URL as desired (v2.8.1). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_customAjaxUrl
+
+ The customAjaxUrl function has two parameters, the table DOM element and the processed url string (all tags within the ajaxUrl have been replaced with their appropriate values). +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxUrl: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}",
+      ajaxProcessing: function(data, table, xhr) {
+        // do something with the ajax data
+        return [ formatted_data, total_rows ];
+      },
+      // modify the url after all processing has been applied
+      customAjaxUrl: function(table, url) {
+        // trigger my custom event
+        $(table).trigger('changingUrl');
+        // send the server the current page
+        return url += '&currntUrl=' + window.location.href;
+      }
+    });
+});
+ In the following example, lets say your server needs a starting and ending record number instead of a page & size parameter. Use this option as follows: +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxUrl: "http://mydatabase.com?{sortList:col}",
+      customAjaxUrl: function(table, url) {
+        var pager = table.config.pager,
+          start = pager.page * pager.size,
+          end = start + pager.size;
+        return url += '&start=' + start + '&end=' + end;
+      },
+      ajaxProcessing: function(data, table, xhr) {
+        // do something with the ajax data
+        return [ total_rows, data ];
+      }
+    });
+});
+
+
Example
Object{ dataType: 'json' } + This option contains the ajax settings for the pager interaction with your database (v2.10). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_ajaxObject
+
+ The ajaxObject is completely customizable, except for the url setting which is processed using the pager's ajaxUrl and customAjaxUrl options. This means you can also add a success callback function which is called after the ajax has rendered.
+
+ Your server does not need to return a JSON format, if you want to return pure HTML, set the dataType to "html" and modify the ajaxProcessing function to instead work with HTML; then return a jQuery object or apply the HTML to the table yourself.
+
+ See all possible settings in the jQuery.ajax documentation +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxUrl: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}",
+      ajaxObject: {
+        // add more ajax settings here
+        // see http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings
+        dataType: 'json',
+        type: 'GET'
+      },
+      ajaxProcessing: function(data, table, xhr) {
+        // do something with the ajax data;
+        return [ total_rows ];
+      }
+    });
+});
+
Example
Functionnull + This callback allows you to customize the error message displayed in the thead (v2.23.0; v2.23.1). +
+
+ In v2.23.1 +
    +
  • A settings parameter was added before the exception parameter to exactly match the parameters returned by the jQuery .ajaxError() method.
  • +
  • This function is now always called, even if the $.tablesorter.showError function is called by an external function with only a string.
  • +
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_ajaxError
+
+ When there is an ajax error, the $.tablesorter.showError function is called. In v2.23.0, that function now checks this callback to allow adding a custom error message.
+
+ Use it as follows: +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxError: function( config, xhr, settings, exception ) {
+        // returning false will abort the error message
+        // the code below is the default behavior when this callback is set to `null`
+        return
+          xhr.status === 0 ? 'Not connected, verify Network' :
+          xhr.status === 404 ? 'Requested page not found [404]' :
+          xhr.status === 500 ? 'Internal Server Error [500]' :
+          exception === 'parsererror' ? 'Requested JSON parse failed' :
+          exception === 'timeout' ? 'Time out error' :
+          exception === 'abort' ? 'Ajax Request aborted' :
+          'Uncaught error: ' + xhr.statusText + ' [' + xhr.status + ']' );
+      }
+    });
+});
+
Objectundefined + When processAjaxOnInit is set to false, set this option to contain the total number of rows and filtered rows to prevent an initial ajax call (v2.25.4). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_initialRows +

Use this option as follows:

+
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      processAjaxOnInit: false,
+      initialRows: {
+        total: 100,
+        filtered: 100
+      },
+      // other ajax settings...
+    });
+});
+
+
Booleantrue + Set this option to false if your table data is preloaded into the table, but you are still using ajax (v2.14.5). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_processAjaxOnInit +
+
Functionfunction (data) { return data; } + This function is required to return the ajax data obtained from your server into a useable format for tablesorter to apply to the table (v2.1, v2.30.8). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_ajaxProcessing
+
+ This function was created and modified to allow you a great deal of flexibility. The only required information that this function needs to return is an array containing the total number of rows, which is needed to calculate total pages.
+
+ There are numerous examples below. Choosing which one to use is left to you. For more information, please check out related questions on Stackoverflow, in particular see this thread about how to use the different ajax options together.
+
+ In v2.10, the returned rows is now optional. And it can either be an array of arrays or a jQuery object (not attached to the table)
+
+ Process your ajax data so that the following information is returned:
+
+ +
+ +

Object

+
+ After tablesorter v2.17.3, this function can include a filteredRows property which will update the internal value and thus show a proper filtered row count, and update the "goto" page drop down selector appropriately.
+
+ After tablesorter v2.11, the ajaxProcessing function can return an object containing these properties, along with any extra properties. These extra properties will be available for use within the pager output string (see more details in issue #326);
+
+ So, lets say the data sent by the server looks like this: +
{
+    "total_rows"    : 100,
+    "filtered_rows" : 75,
+    "new_headers"   : [ "ID", "Name", "Data", "Value" ],
+    "data"          : '<tr><td>a123</td><td>abc</td><td>xyz</td><td>999</td></tr>',
+    "subject"       : "cheese",
+    "tasty"         : "It's delicious!"
+}
+ This ajaxProcessing function must return an object with "total", "headers" and "rows" properties! As before, "total" is the only required property; if the headers don't need to be changed, don't return a headers array, and if you append the rows to the table yourself within the ajaxProcessing function, you don't need to return a "rows" property. +
ajaxProcessing: function(result, table, xhr) {
+    if (result && result.hasOwnProperty('data')) {
+
+        // "total" is a required property!
+        result.total = result["total_rows"];
+
+        // "filteredRows" is optional - in tablesorter v2.17.3, this updates the filter row count internally
+        // and updates the "goto" page dropdown selector appropriately
+        result.filteredRows = result["filtered_rows"];
+
+        // "headers" is optional. This is not needed if the table headers don't change
+        result.headers = result["new_headers"];
+
+        // "rows" is optional. No need to return this if you process and add the rows to the table yourself
+        // otherwise, return an array of arrays or jQuery object (shown in this example)
+        result.rows = $(result.data);
+
+        return result;
+    }
+}
+ Now in the output string, you can also reference the extra ajax data: +
output : '{startRow} to {endRow} of {filteredRows} ({totalRows}) rows about {subject} ({tasty})'
+
+ +

Array (total only)

+
+ After tablesorter v2.10, just build the table yourself and return the total number of rows: +
ajaxProcessing: function(data, table, xhr) {
+  if (data && data.hasOwnProperty('rows')) {
+    var r, row, c, d = data.rows,
+    // total number of rows (required)
+    total = data.total_rows,
+    // all rows: array of arrays; each internal array has the table cell data for that row
+    rows = '',
+    // len should match pager set size (c.size)
+    len = d.length;
+    // this will depend on how the json is set up - see City0.json
+    // rows
+    for ( r=0; r < len; r++ ) {
+      rows += '<tr class="ajax-row">'; // new row
+      // cells
+      for ( c in d[r] ) {
+        if (typeof(c) === "string") {
+          rows += '<td>' + d[r][c] + '</td>'; // add each table cell data to row
+        }
+      }
+      rows += '</tr>'; // end new row
+    }
+    // find first sortable tbody, then add new rows
+    table.config.$tbodies.eq(0).html(rows);
+    // no need to trigger an update method, it's done internally
+    return [ total ];
+  }
+}
+
+ +

Array (rows as jQuery object)

+
+ After tablesorter v2.10, return a jQuery object +
ajaxProcessing: function(data, table, xhr) {
+  if (data && data.hasOwnProperty('rows')) {
+    var r, row, c, d = data.rows,
+    // total number of rows (required)
+    total = data.total_rows,
+    // array of header names (optional)
+    headers = data.headers,
+    // all rows: array of arrays; each internal array has the table cell data for that row
+    rows = '',
+    // len should match pager set size (c.size)
+    len = d.length;
+    // this will depend on how the json is set up - see City0.json
+    // rows
+    for ( r=0; r < len; r++ ) {
+      rows += '<tr class="ajax-row">'; // new row
+      // cells
+      for ( c in d[r] ) {
+        if (typeof(c) === "string") {
+          rows += '<td>' + d[r][c] + '</td>'; // add each table cell data to row
+        }
+      }
+      rows += '</tr>'; // end new row
+    }
+    // don't attach the $(rows) because it's difficult to tell old from new data
+    // and no need to trigger an update method, it's done internally
+    return [ total, $(rows), headers ];
+  }
+}
+ Or, if your JSON contains all the rows within a string this method will work: +
ajaxProcessing: function(data, table, xhr) {
+  if (data && data.hasOwnProperty('rows')) {
+    // data.rows would look something like this
+    // '<tr><td>r0c0</td><td>r0c1</td></tr><tr><td>r1c0</td><td>r1c1</td></tr>'
+    return [ data.total, $(data.rows), data.headers ];
+  }
+}
+
+ +

Array (rows as an array of arrays)

+
+ After tablesorter v2.1, this function must return an array with values in any of the following orders: +
// [ total_rows (number), rows (array of arrays), headers (array; optional) ]
+// or [ rows, total_rows, headers ]
+// or [ total_rows, $(rows) ]
+// or [ total_rows ]
+[
+  100, // total rows
+  [
+    [ "row1cell1", "row1cell2", ... "row1cellN" ],
+    [ "row2cell1", "row2cell2", ... "row2cellN" ],
+    ...
+    [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
+  ],
+  [ "header1", "header2", ... "headerN" ] // optional
+]
Note: In v2.14.3, the contents of the array can also contain table cell markup (i.e. "<td class='green'>+ 10%</td>").
+
+ Here is some example JSON (comments added, but not allowed in JSON) which is contained in the City0.json file: +
{
+  // total rows
+  "total_rows": 80,
+  // headers
+  "cols" : [
+    "ID", "Name", "Country Code", "District", "Population"
+  ],
+  // row data...
+  "rows" : [{
+    "ID": 1,
+    "Name": "Kabul",
+    "CountryCode": "AFG",
+    "District": "Kabol",
+    "Population": 1780000
+  }, {
+    // row 2, etc...
+  }]
+}
+ The above JSON is processed by the following code (this returns an array of array of table rows): +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      ajaxUrl: "http://mydatabase.com?page={page}&size={size}",
+      ajaxProcessing: function(data, table, xhr) {
+        if (data && data.hasOwnProperty('rows')) {
+          var r, row, c, d = data.rows,
+          // total number of rows (required)
+          total = data.total_rows,
+          // array of header names (optional)
+          headers = data.cols,
+          // all rows: array of arrays; each internal array has the table cell data for that row
+          rows = [],
+          // len should match pager set size (c.size)
+          len = d.length;
+          // this will depend on how the json is set up - see City0.json
+          // rows
+          for ( r = 0; r < len; r++ ) {
+            row = []; // new row array
+            // cells
+            for ( c in d[r] ) {
+              if (typeof(c) === "string") {
+                row.push(d[r][c]); // add each table cell data to row array
+              }
+            }
+            rows.push(row); // add new row array to rows array
+          }
+          return [ total, rows, headers ]; // or return [ rows, total, headers ] in v2.9+
+        }
+      }
+    });
+});
+
+ +
+
+
Example
String"{startRow} to {endRow} of {totalRows} rows"This option allows you to format the output display which can show the current page, total pages, filtered pages, current start and end rows, total rows and filtered rows (v2.0.9; v2.28.4). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_output
+

+ In v2.28.4 +

The output element can now include two data-attributes, data-pager-output and data-pager-output-filtered which override this setting string when set.

+
<span class="pagedisplay" data-pager-output="{startRow:input} – {endRow} / {totalRows} total rows" data-pager-output-filtered="{startRow:input} – {endRow} / {filteredRows} of {totalRows} total rows"></span>
+

It will not override this option if set as a function, or if a specific output is returned from the server through ajax.

+ In v2.27.7, this option can also be set as a function +
output: function(table, pager) {
+	return 'page ' + pager.startRow + ' - ' + pager.endRow;
+}
+ This option replaced the original separator option, which only separated the page number from the total number of pages. The formatted output from this option is placed inside the information block targeted by the cssPageDisplay option.
+
+ Use it as follows: +
$(function() {
+  $("table")
+    .tablesorter()
+    .tablesorterPager({
+      output: '{startRow} to {endRow} of {totalRows} rows'
+    });
+});
The following tags are replaced within the output string: + + + + + + + + + + + + + + + + +
TagReplaced with
{size}The current pager size
{page}The current pager page
{page:input}The current pager page within an input (v2.17.6)
{totalPages}Total number of pager pages
{filteredPages}Total number of pages left after being filtered
{startRow}Starting row number currently displayed
{startRow:input}Starting row number currently displayed within an input (v2.17.6)
{endRow}Ending row number currently displayed
{filteredRows}Total number of rows left after being filtered
{totalRows}Total number of rows
+
+ 1 + 2 + 3 + 4 +
Booleantrue + If true, the addon hides the left pager arrow on the first page and right pager arrow on the last page. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_updateArrows
+
+ If true the classname from the cssDisabled option is applied to the arrows when at either page extreme.
+
Example
Numeric0 + Set the starting page of the pager (zero-based index). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_startPage +
+
Example
Numeric, Boolean0 + Reset pager to this page after filtering; set to desired page number (zero-based index), or false to not change page at filter start (v2.16). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_pageReset +
+
Numeric10 + Set initial number of visible rows. This value is changed by the dropdown selector targeted by the cssPageSize option. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_size +

Set this option to 'all' to show all rows (added v2.26.0).

+
+
Example
Booleantrue + Saves the current pager page size and number. This option requires the $.tablesorter.storage script in the jquery.tablesorter.widgets.js file (v2.11). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_savePages +
+
Example
String"tablesorter-pager" + Saves tablesorter paging to custom key if defined. Key parameter name used by the $.tablesorter.storage function. Useful if you have multiple tables defined (v2.15) +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_storageKey +
+
Example
BooleanfalseMaintain the height of the table even when fewer than the set number of records is shown (v2.1; updated 2.7.1). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_fixedHeight
+
+ This option replaced the original positionFixed and offset options which set the absolute position of the pager block.
+
+ If true, it should maintain the height of the table, even when viewing fewer than the set number of records (go to the last page of any demo to see this in action). It works by adding an empty row to make up the differences in height. +
+
Example
Booleanfalse + If true, child rows will be counted towards the pager set size (v2.13). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_countChildRows
+
+ *CAUTION* When true, child row(s) may not appear to be attached to its parent row, may be split across pages or + may distort the table if rowspan or cellspans are included within the child row.
+
+ If this option is false, child row(s) will always appear on the same page as its parent. +
+
Booleanfalse + If true, rows are removed from the table to speed up the sort of large tables (v2.0.21). +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_removeRows
+
+ The original tablesorter plugin (v2.0.5) removed rows automatically, without providing an option. It really does speed up sorting of very large tables, but also breaks updating and modifying table content dynamically.
+
+ If this option is false, the addon only hides the non-visible rows; this is useful if you plan to add/remove rows with the pager enabled. +
+
Example
String".first" + This option contains a jQuery selector string pointing to the go to first page arrow. See container for full HTML. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.first +
+
Example
String".prev" + This option contains a jQuery selector string pointing to the go to previous page arrow. See container for full HTML. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.prev +
+
Example
String".next" + This option contains a jQuery selector string pointing to the go to next page arrow. See container for full HTML. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.next +
+
Example
String".last" + This option contains a jQuery selector string pointing to the go to last page arrow. See container for full HTML. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.last +
+
Example
String".gotoPage"This option contains a jQuery selector string pointing to the page select dropdown. See container for full HTML (v2.4; v2.17.3) +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.gotoPage (changed from goto in v2.17.3)
+
+ Please note that this select dropdown is initially empty and automatically updated by the plugin with the correct number of pages, which depends on the size setting. +
+
Example
String".pagedisplay"This option contains a jQuery selector string pointing to the output element (v2.0.9) +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.pageDisplay
+
+ In the original tablesorter (v2.0.5) this option could only target an input, it was updated (v2.0.9) to display the formatted output from the output option inside of any element (span, div or input). +
+
Example
String".pagesize" + This option contains a jQuery selector string pointing to the page size selector. See container for full HTML. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_selectors.pageSize +
+
Example
String"tablesorter-errorRow"This option contains the class name that is applied to the error information row that is added inside the pager with any ajax exceptions. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_css.errorRow
+
+ Note there is no period "." in front of this class name (it is not a selector). +
String"disabled"This option contains the class name that is applied to disabled pager controls. +
+
+ Note The pager widget equivalent option is within the widgetOptions and accessed via widgetOptions.pager_css.disabled
+
+ More explicitly, this class is applied to the pager arrows when they are at either extreme of pages and the updateArrows option is true. When the pager has been disabled, this class is applied to all controls.
+
+ Note there is no period "." in front of this class name (it is not a selector). +
Removed Options
Booleanfalse + Filter: This option was removed in v2.15... sorry for the sudden notice. +
+
+ This option has been replaced by the filter_external option.
+
+ Show any rows that match a search query. If this option is true any column match will show that row; but there are limitations (v2.13.3).
+
+ It is best if this filter_anyMatch option is used with a single search input as follows: +
<input class="search" type="search">
+<button type="button" class="reset">Reset Search</button>
+
$(function() {
+  $("table").tablesorter({
+    widgets: ["filter"],
+    widgetOptions : {
+      filter_anyMatch : true,
+      filter_columnFilters: false,
+      filter_reset: '.reset'
+    }
+  });
+
+  // Target the $('.search') input using built in functioning
+  // this binds to the search using "search" and "keyup"
+  // Allows using filter_liveSearch or delayed search &
+  // pressing escape to cancel the search
+  $.tablesorter.filter.bindSearch( $table, $('.search') );
+
+});
+
String"jui"UiTheme: This option was removed in v2.19.0! + It has been replaced by theme. +

+ This option contains the name of the theme. Currently jQuery UI ("jui") and Bootstrap ("bootstrap") themes are supported (updated v2.4) +

+ * NOTE * only the uitheme widget option was removed. All of the information below is still pertinent and portions have been copied to the core theme option. +

+

To modify the class names used, extend from the $.tablesorter.themes variable as follows:

+
// Extend the themes to change any of the default class names ** NEW **
+$.extend($.tablesorter.themes.jui, {
+  // change default jQuery uitheme icons - find the full list of icons
+  // here: http://jqueryui.com/themeroller/ (hover over them for their name)
+  table        : 'ui-widget ui-widget-content ui-corner-all', // table classes
+  caption      : 'ui-widget-content',
+  // header class names
+  header       : 'ui-widget-header ui-corner-all ui-state-default', // header classes
+  sortNone     : '',
+  sortAsc      : '',
+  sortDesc     : '',
+  active       : 'ui-state-active', // applied when column is sorted
+  hover        : 'ui-state-hover',  // hover class
+  // icon class names
+  icons        : 'ui-icon', // icon class added to the <i> in the header
+  iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // "caret" class renamed in jQuery v1.12.0
+  iconSortAsc  : 'ui-icon-carat-1-n ui-icon-caret-1-n',
+  iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s',
+  // other
+  filterRow    : '',
+  footerRow    : '',
+  footerCells  : '',
+  even         : 'ui-widget-content', // even row zebra striping
+  odd          : 'ui-state-default'   // odd row zebra striping
+});
+ + This widget option replaces the previous widgetUitheme. All theme css names are now contained within the $.tablesorter.themes variable. Extend the default theme as seen above.
+
+ + The class names from the $.tablesorter.themes.{name} variable are applied to the table as indicated.
+
+ + As before the jQuery UI theme applies the default class names of "ui-icon-arrowthick-2-n-s" for the unsorted column, "ui-icon-arrowthick-1-s" for the descending sort and "ui-icon-arrowthick-1-n" for the ascending sort. (Modified v2.1; Updated in v2.4). Find more jQuery UI class names by hovering over the Framework icons on this page: http://jqueryui.com/themeroller/
+
+ Use the "uitheme" option to change the css class name as follows: +
$(function() {
+  $("table").tablesorter({
+    theme     : 'jui',       // set theme name from $.tablesorter.themes here
+    widgets   : ["uitheme"], // initialize ui theme styling widget of the table
+    widgetOptions: {
+      uitheme : "jui"        // this is now optional in v2.7, it is overridden by the theme option
+    }
+  });
+});
+ + To add a new theme, define it as follows; replace "custom" with the name of your theme: +
$.tablesorter.themes.custom = {
+  table      : 'table',       // table classes
+  header     : 'header',      // header classes
+  footerRow  : '',
+  footerCells: '',
+  icons      : 'icon',        // icon class added to the <i> in the header
+  sortNone   : 'sort-none',   // unsorted header
+  sortAsc    : 'sort-asc',    // ascending sorted header
+  sortDesc   : 'sort-desc',   // descending sorted header
+  active     : 'sort-active', // applied when column is sorted
+  hover      : 'hover',       // hover class
+  filterRow  : 'filters',     // class added to the filter row
+  even       : 'even',        // even row zebra striping
+  odd        : 'odd'          // odd row zebra striping
+}
+
Example
Numeric0 + Pager: This option was removed! +
+
+ The original tablesorter pager plugin absolutely positioned the pager controls at the bottom of the table. It appears that this option was intended to tweak the position of the pager container. The option exists, but no code was found.
+
Booleantrue + Pager: This option was removed! +
+
+ If this option were true, the original tablesorter pager plugin would absolutely position the pager controls at the bottom of the table.
+
String"/" + Pager: This option was removed! Use the output option to allow for more control over the formatting. +
+
+ The original tablesorter pager plugin would combine the current page with the calculated total number of pages within the cssPageDisplay with this separator string inbetween.
+
+ + +

Methods

+ +
+

+ tablesorter has some methods available to allow updating, resorting or applying widgets to a table after it has been initialized. +
+ TIP! Click on the link in the method column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescriptionLink
Use this method to add table rows (v2.0.16; v2.23.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+  $rows = $( 'tr.addedRows' ), // jQuery selector of newly appended row(s)
+  // applies or reapplies a sort to the table; use false to not update the sort
+  resort = true, // or [ [0,0], [1,0] ] etc
+  callback = function( table ) {
+    // do something
+  };
+$.tablesorter.addRows( config, $rows, resort, callback );
+

Triggered event method:

+ It does not work the same as "update" in that it only adds rows, it does not remove them.
+
+ Also, use this method to add table rows while using the pager plugin. If the "update" method is used, only the visible table rows continue to exist. +
// Add multiple rows to the table
+var row = '<tr><td>Inigo</td><td>Montoya</td><td>34</td>' +
+  '<td>$19.99</td><td>15%</td><td>Sep 25, 1987 12:00PM</td></tr>',
+  $row = $( row ),
+  // resort table using true to reapply the current sort; set to false to prevent resort
+  // if undefined, the resort value will be obtained from config.resort (added v2.19.0)
+  // As of v2.19.0, the resort variable can contain a new sortList to be applied
+  // A callback method was added in 2.3.9.
+  resort = true, // or [ [0,0], [1,0] ] etc
+  callback = function( table ) {
+    alert( 'rows have been added!' );
+  };
+$( 'table' )
+  .find( 'tbody' ).append( $row )
+  .trigger( 'addRows', [ $row, resort, callback ] );
+
+
    +
  • In v2.23.0, +
      +
    • If a table contains only one tbody (not counting information only tbodies; which have a class name from cssInfoBlock), then a row string can be used in this method. +
      var $table = $( 'table' ),
      +config = $table[ 0 ].config;
      +// triggered event method
      +$table.trigger( 'addRows', [ '<tr>...</tr>', true ] );
      +
      +// direct method
      +$.tablesorter.addRows( config, '<tr>...</tr>', true );
    • +
    • So instead of making a jQuery object, appending it to the table, then passing the reference to the method, you can just pass a string. This method doesn't work if a table has multiple tbodies, because the plugin doesn't know where you want to add the rows.
    • +
    +
  • +
  • In v2.16.1, the $row parameter can be a row DOM element or jQuery object.
  • +
+
+
Example
Use this method reset the table to it's original settings. (v2.17.0). +
+ Using this method will clear out any settings that have changed since the table was initialized (refreshes the entire table); so any sorting or modified widget options will be cleared.
+ However, it will not clear any values that were saved to storage. This method is basically like reloading the page. +
$('table').trigger('resetToLoadState');
+
+
Use this method to initialize a sort while targeting a specific column header (v2.9). +
+
// Target a specific header
+$('table').find('th:eq(2)').trigger('sort');
Using this method will maintain the sorting order; so, if the column is already sorted in ascending order, this method will act as if you manually clicked on the header. Whatever sort order is applied is dependent on other option settings such as initialSortOrder, lockedOrder (set within the headers), sortReset option, sortRestart and will be ignored if the column sort is disabled (sorter: false). +
+
Use this method to sort an initialized table in the desired order (v2.23.0) +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	sort = [ [ 0,0 ], [ 1,0 ] ],
+	callback = function( table ) {
+		// do something
+	};
+	// NOTE: the triggered 'sorton' method requires the sort & callback methods to be
+	// passed inside an array, this direct method does not.
+	// Also, notice the "O" of "sortOn" is capitalized in this direct method
+$.tablesorter.sortOn( config, sort, callback );
+

Triggered event method:

+
// Choose a new sort order
+var sort = [ [0,0], [2,0] ],
+    callback = function( table ) {
+        alert( 'new sort applied to ' + table.id );
+    };
+// Note that the sort value below is inside of another array (inside another set of square brackets)
+// without a callback it could look like this:
+$( 'table' ).trigger( 'sorton', [ [[0,0],[2,0]] ] );
+
+// when including a callback method the outer square bracket wrap both parameters (added in 2.3.9).
+$( 'table' ).trigger( 'sorton', [ sort, callback ] );
+ *NOTE* using this method to sort ignores the additions from the sortForce and sortAppend options.
+
+ In v2.17.0, the sort direction can be set using "a" (ascending), "d" (descending), "n" (next), "s" (same) & "o" (opposite). +
$('table').trigger('sorton', [ [[0,"a"],[2,"n"]] ]);
+ Please try out the demo (example link) to better understand how these values work. +
+
Example
Use this method to set the table into an unsorted state (v2.4.7; v2.23.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.sortReset( config, callback );
+

Triggered event method:

+ This method immediately resets the entire table sort, while the option only resets the column sort after a third click.
+
+ In v2.16.0, a callback function was added to this method.
+
+
// Reset the table (make it unsorted)
+var callback = function( table ) {
+	console.log( 'sort has been reset' );
+};
+$( 'table' ).trigger( 'sortReset', [ callback ] );
+ *NOTE* Don't confuse this method with the sortReset option. +
+
Example
/ Update the tbody's stored data (update & updateRows do exactly the same thing; v2.23.0) +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	// applies or reapplies a sort to the table; use false to not update the sort
+	resort = true, // or [ [0,0], [1,0] ] etc
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.update( config, resort, callback );
+

Triggered event method:

+
// Add new content
+$( 'table tbody' ).append( html );
+
+// let the plugin know that we made a update
+// the resort flag set to true will trigger an automatic resort using the current sort
+// if set to false, no new sort will be applied; or set it to any sortList value (e.g. [[0,0]]; new v2.19.0)
+// A callback method was added in 2.3.9.
+var resort = true,
+    callback = function( table ) {
+        alert( 'new sort applied' );
+    };
+$( 'table' ).trigger( 'update', [ resort, callback ] );
+
+// As of version 2.19.0, if the resort parameter is undefined, the setting from the config.resort will be used
+
+// As of version 2.0.14, the table will automatically resort after the update (if the "resort" flag is true
+// & will use the current sort selection), so include the following if you want to specify a different sort
+
+// set sorting column and direction, this will sort on the first and third column
+var sorting = [ [2,1], [0,0] ];
+// method to use prior to v2.19.0
+// $( 'table' )
+//  .trigger( 'update', [ false ] )
+//  .trigger( 'sorton', [ sorting ] );
+// After v2.19.0; do the following to apply a new sort after updating
+// if sorting is an empty array [], then the sort will be reset
+$( 'table' ).trigger( 'update', [ sorting ] );
+ NOTE updateRows was added to work around the issue of using jQuery with the Prototype library. Triggering an "update" would make Prototype clear the tbody; Please see issue #217 for more details. +
+
Example
Update a column of cells (thead and tbody) (v2.8; v2.23.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	// applies or reapplies a sort to the table; use false to not update the sort
+	resort = true, // or [ [0,0], [1,0] ] etc
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.updateAll( config, resort, callback );
+

Triggered event method:

+
// Change thead & tbody column of cells
+// remember, "eq()" is zero based & "nth-child()" is 1 based
+$("table thead th:eq(2)").html("Number");
+// add some random numbers to the table cell
+$("table tbody").find('td:nth-child(3)').html(function(i,h) {
+  return Math.floor(Math.random()*10) + 1; // random number from 0 to 10
+});
+
+// reapply the current sort if resort = true
+// do not reapply the current sort if resort = false
+// if undefined, resort will be obtained from config.resort (added v2.19.0)
+// as of v2.19.0, apply a new sort if resort = [[0,0]] (or whatever)
+// or the sort is reset if resort = []
+var resort = true,
+  // add a callback, as desired
+  callback = function(table) {
+    alert('table updated!');
+  };
+
+// let the plugin know that we made a update, then the plugin will
+// automatically sort the table based on the header settings
+$("table").trigger("updateAll", [ resort, callback ]);
+
Example
Update the parsers, only if not defined, then update the internal cache (v2.15.4; v2.27.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	// optional parameter to target tbodies
+	$tbodies = $( 'table' ).children( 'tbody.totals' ),
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.updateCache( config, callback, $tbodies );
+

Triggered event method:

+

*NOTE* In v2.22.2, a new parameter was added to allow passing a jQuery object containing tbodies to be added to the table (for the tbody sorting widget).

+

This method is used by the pager (addon & widget) to update the data stored within the cache after the content has been updated using ajax.

+
// optional callback function
+var callback = function( table ) { /* do something */ },
+	// optional in v2.22.2; defaults to table.config.$tbodies if undefined
+	$tbodies = $( 'table' ).children( 'tbody' );
+$("table").trigger("updateCache", [ callback, $tbodies ] );
+
Example
Adds all of the cached table rows back into the table (v2.23.0).
+

+ This method was originally designed to be used with the pager. It should be used under these conditions: +
    +
  • When the pager removeRows option is true.
  • +
  • When not using the "updateCell" or "addRows" methods.
  • +
  • Before manually adding or removing table rows.
  • +
  • And, before triggering an "update".
  • +
+ Note: The entire table is stored in the cache, but when using the pager with the removeRows option set to true, only the visible portion is actually exists within the table. So, use this option to add all stored rows back into the table before manually changing the contents. Otherwise, if any update method is triggered, only the visible rows will be added back to the cache. +

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config;
+// there is no callback available when using this direct method because
+// it may divert to pager injected functions, if the pager is being used
+$.tablesorter.appendCache( config );
+

Triggered event method:

+
// how to update the table contents
+$("table")
+  .trigger("appendCache")
+  .append('<tr>...</tr>') // add new row(s), or delete rows
+  .trigger("update"); // update the cache
+
+
Refresh table headers only (v2.23.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.updateHeaders( config, callback );
+

Triggered event method:

+
$(function() {
+  $( 'table' ).tablesorter();
+
+  // click on a button somewhere on the page to update header index
+  $( 'button' ).click( function() {
+    // Do something after the cell update in this callback function
+    var $headerCell = $('thead th:eq(1)'),
+      index = $headerCell.data( 'counter' ) || 0,
+      callback = function( table ) {
+          /* do something */
+        };
+
+    // change header text
+    $headerCell
+      .html( 'header click #' + index )
+      // update header data
+      .data( 'counter', index++ );
+
+    // update the header, includes rebinding events & using the header template
+    $( 'table' ).trigger( 'updateHeaders', callback );
+    return false;
+  });
+});
+
Update a table cell in the tablesorter data (v2.23.0). +
+

Direct method:

+

In v2.23.0, this method can be called directly as follows:

+
var config = $( 'table' )[ 0 ].config,
+	$cell = $( 'td.random' ), // jQuery selector or DOM element of newly updated cell
+	// applies or reapplies a sort to the table; use false to not update the sort
+	resort = true, // or [ [0,0], [1,0] ] etc
+	callback = function( table ) {
+		// do something
+	};
+$.tablesorter.updateCell( config, $cell, resort, callback );
+

Triggered event method:

+
$(function() {
+  $( 'table' ).tablesorter();
+
+  $( 'td.discount' ).click( function() {
+
+    // Do we want to reapply the current sort on the column?
+    // see updateRow for other resort settings as of v2.19.0
+    // if resort is undefined, the value from config.resort (added v2.19.0) will be used
+    var resort = false,
+        // Do something after the cell update in this callback function
+        callback = function( table ) {
+          /* do something */
+        },
+        // randomize a number & add it to the cell
+        discount = '$' + Math.round( Math.random() * Math.random() * 100 ) + '.' +
+          ( '0' + Math.round( Math.random() * Math.random() * 100 ) ).slice( -2 );
+
+    // add new table cell text
+    $( this ).text( discount );
+
+    // update the table, so the tablesorter plugin can update its value
+    // set resort flag to false to prevent automatic resort (since we're using a different sort below)
+    // prior to v2.19.0, leave the resort flag as undefined, or with any other value, to automatically resort the table
+    // new resort values can be set as of v2.19.0 - please see the "updateRow" documentation for more details
+    // $( 'table' ).trigger( 'updateCell', [ this ] ); < - resort is undefined so the table WILL resort
+    $( 'table' ).trigger( 'updateCell', [ this, resort, callback ] );
+
+    // As of version 2.0.14, the table will automatically resort (using the current sort selection)
+    // after the update, so include the following if you want to specify a different sort
+
+    // prior to v2.19.0, set sorting column and direction, this will sort on the first and third column
+    // after v2.19.0, add any new sort to the "resort" variable above
+    var sorting = [ [ 3,1 ] ];
+    $( 'table' ).trigger( 'sorton', [ sorting ] );
+
+    return false;
+  });
+});
+
Example
Apply the selected widget to the table, but the widget will not continue to be applied after each sort. See the example, it's easier than describing it (v2.25.4). +
+

In v2.25.4, the direct method will now correctly accept $('table'). Previously, only $('table')[0] would work.

+

In v2.25.0, this method ensures that the widget initialization code is executed as well as the format function. So now a widget that was not previously initialized or had been previously removed, can be reapplied.

+

Only one widget can be applied at a time using this method.

+

Direct method:

+
$.tablesorter.applyWidgetId( $('table'), 'zebra' );
+

Triggered event method:

+
$(function() {
+  // initialize tablesorter without the widget
+  $('table').tablesorter();
+
+  // click a button to apply the zebra striping
+  $('button').click(function() {
+    $('table').trigger('applyWidgetId', 'zebra');
+    return false;
+  });
+
+});
+
Example
Apply the set widgets to the table (v2.29.0). +
+

In v2.29.0, access to the callback method was added.

+

This method only updates the widgets listed in the table.config.widgets option.

+
// This method applies the widgets already added to tablesorter
+$('table').trigger('applyWidgets', callback);
+
+ Use this method can be used to add multiple widgets to the table. +
var $table = $( 'table' );
+$table[0].config.widgets = [ 'zebra', 'columns' ];
+$table.trigger('applyWidgets', function() {
+	console.log('new widgets applied');
+});
+
+
Example
Remove the named widget from the table (v2.25.0). +
+

This method will remove the named widget(s) from the table.

+

Direct method:

+
$.tablesorter.removeWidget( $( 'table' ), widget );
+

Triggered event method:

+
$( 'table' ).trigger( 'removeWidget', widget );
+

The widget parameter can be used as follows:

+
    +
  • Single widget name - string +
    // only remove zebra widget
    +$( 'table' ).trigger( 'removeWidget', 'zebra' );
    +
  • +
  • Multiple widgets names - array or string (space or comma separated) +
    // remove multiple widgets (the below methods are all equivalent)
    +$( 'table' ).trigger( 'removeWidget', 'zebra columns' );
    +$( 'table' ).trigger( 'removeWidget', 'zebra, columns' );
    +$( 'table' ).trigger( 'removeWidget', [ 'zebra', 'columns' ] );
    +
  • +
  • true - boolean (removes all widgets; passing false does nothing) +
    // remove all widgets
    +$( 'table' ).trigger( 'removeWidget', true );
    +
  • +
+
+
Example
Use this method to remove tablesorter from the table (v2.3.2; v2.16). +
+
// Remove tablesorter and all classes
+$("table").trigger("destroy");
+
+// Remove tablesorter and all classes but the "tablesorter" class on the table
+// callback is a function
+$("table").trigger("destroy", [false, callback]);
+
Refresh the currently applied widgets. Depending on the options, it will completely remove all widgets, then re-initialize the current widgets or just remove all non-current widgets (v2.4; v2.19.0). +

+ Trigger this method using either of the following methods (they are equivalent): +
// trigger a refresh widget event
+$('table').trigger('refreshWidgets', [doAll, dontapply]);
+
+// Use the API directly
+$.tablesorter.refreshWidgets(table, doAll, dontapply)
+
    +
  • If doAll is true it removes all widgets from the table. If false only non-current widgets (from the widgets option) are removed.
  • +
  • When done removing widgets, the widget re-initializes the currently selected widgets, unless the dontapply parameter is true leaving the table widget-less.
  • +
  • Note that if the widgets option has any named widgets, they will be re-applied to the table when it gets resorted. So if you want to completely remove all widgets from the table, also clear out the widgets option $('table')[0].config.widgets = [];
  • +
+
+
Example
Widget Methods
filter: Trigger the filter widget to reset the search criteria (v2.7.7). +

+ If you are using the filter_formatter option to add custom input elements, this function may not work on those columns. Please refer to the filter_formatter section for more details. +
$(function() {
+  // this is the same code that the "filter_reset" element runs to clear out the filters.
+  $('button').click(function() {
+    $('table').trigger('filterReset');
+    return false;
+});
+ This method is used by the filter_reset option when defined. +
+
Example
filter: Trigger the filter widget to reset the sort & reset the search criteria (v2.28.7). +
+

+ This combination reset was added to prevent issues with the widgets not being updated after a combination of filterReset and sortReset due to internal timers preventing multiple widget applications in a row. +

+ This method does not include a callback parameter similiar to the sortReset method. +
$(function() {
+  $('button').click(function() {
+    $('table').trigger('filterAndSortReset');
+    return false;
+});
+
+
filter: Make the filter widget reset any saved searches (v2.25.6). +

+ Use this as follows: +
$(function() {
+  // this is the same code that the "filter_reset" element runs to clear out the filters.
+  $('button').click(function() {
+    $('#myTable').trigger('filterResetSaved');
+    return false;
+});
+ This is the equivalent of executing this function: +
// clear the current table stored filters
+$.tablesorter.storage( $('#myTable'), 'tablesorter-filters', '' );
+
+
Example
Trigger the saveSort widget to clear any saved sorts for that specific table (v2.7.11). +
+
$(function() {
+  $('button').click(function() {
+    $('table').trigger('saveSortReset');
+    return false;
+  });
+});
+
Pager Methods
Trigger the pager to change the page size (v2.7.4; v2.24.0). +
+
$(function() {
+  $('table').trigger('pageSize', 15);
+});
+ In v2.24.0, this option will now accept "all" as a setting, and in turn display all rows. +
+
Trigger the pager to change the current page (v2.7.7). +
+
+ If no value is passed, the pager will reset to page 1; otherwise, pass a "one-based" index of the desired page +
$(function() {
+  $('table').trigger('pageSet', 3); // pass a one-based index
+});
+
Trigger the pager to change the current page & size (v2.19.0; ; v2.24.0). +
+
+ If no value is passed, the pager will reset to page 1 with the original pager size setting; otherwise, pass a "one-based" index of the desired page and the pager size as an array +
$(function() {
+  $('table').trigger('pageAndSize', [ 2, 20 ]); // pass a one-based page index & page size
+});
+ In v2.24.0, this option will now accept "all" as a page size setting, and in turn display all rows; the page number will be force to page one. +
+
Force the pager to update the table with the current settings (v2.19.0). +
+
+ If there is a need, this method will force the (ajax and non-ajax) pager to update. Use it as follows: +
$(function() {
+  $('table').trigger('pagerUpdate');
+});
+ Or, if you need, you can optionally pass a new page number: +
$(function() {
+  $('table').trigger('pagerUpdate', 3); // update and set to page 3
+});
+
+
Calling this method will reveal the entire table, remove the pager functionality, and hide the actual pager (v2.0.16; v2.23.0). +
+

In v2.23.0, this method was changed from destroy.pager to destroyPager because of issues with unique namespacing.

+
$(function() {
+  $('table').trigger('destroyPager');
+});
+ The only way to restore the pager addon is to re-initialize the pager addon: +
+
$(function() {
+  $('table').tablesorterPager(pagerOptions);
+});
+
Example
This method will put the pager into a disabled state (v2.0.21.2; v2.23.0). +
+

In v2.23.0, this method was changed from disable.pager to disablePager because of issues with unique namespacing.

+ The disabled state will reveal all table rows and disable, but not hide, pager controls. +
+
$(function() {
+  $('table').trigger('disablePager');
+});
+
Example
This method will re-enable the pager, but only from the disabled state (v2.0.21.2; v2.23.0). +
+

In v2.23.0, this method was changed from enable.pager to enablePager because of issues with unique namespacing.

+
$(function() {
+  $('table').trigger('enablePager');
+});
+
Example
+ + +

Events

+ +
+

+ tablesorter has some methods available to allow updating, resorting or applying widgets to a table after it has been initialized. +
+ TIP! Click on the link in the event column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EventDescriptionLink
This event fires when tablesorter has completed initialization. (v2.2). +
+
$(function() {
+
+  // bind to initialized event BEFORE initializing tablesorter*
+  $("table")
+    .bind("tablesorter-initialized",function(e, table) {
+      // do something after tablesorter has initialized
+    });
+
+  // initialize the tablesorter plugin
+  $("table").tablesorter({
+    // this is equivalent to the above bind method
+    initialized : function(table) {
+      // do something after tablesorter has initialized
+    }
+  });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
This event fires immediately before tablesorter begins resorting the table. +
+
$(function() {
+
+  // initialize the tablesorter plugin
+  $("table").tablesorter();
+
+  // bind to sort events
+  $("table").bind("sortBegin",function(e, table) {
+    // do something crazy!
+  });
+});
+
This event fires immediately after the tablesorter header has been clicked, initializing a resort. +
+
$(function() {
+
+  // initialize the tablesorter plugin
+  $("table").tablesorter();
+
+  // bind to sort events
+  $("table")
+    .bind("sortStart",function(e, table) {
+      $("#overlay").show();
+    })
+    .bind("sortEnd",function(e, table) {
+      $("#overlay").hide();
+    });
+});
+
Example
This event fires when tablesorter has completed resorting the table. +
+
$(function() {
+
+  // initialize the tablesorter plugin
+  $("table").tablesorter();
+
+  // bind to sort events
+  $("table")
+    .bind("sortStart",function(e, table) {
+      $("#overlay").show();
+    })
+    .bind("sortEnd",function(e, table) {
+      $("#overlay").hide();
+    });
+});
+
Example
This event fires after tablesorter has completed updating (v2.3.9). +
+ This occurs after an "update", "updateAll", "updateCell" or "addRows" method was called, but before any callback functions are executed. +
$(function() {
+
+  // initialize the tablesorter plugin
+  $('table')
+    .tablesorter()
+    // bind to sort events
+
+    .bind('updateComplete', function(e, table) {
+      // do something after the table has been altered;
+    });
+
+});
+
This event fires after tablesorter has complete applying all widgets and is ready for its next action (v2.24.0). +
+ This event occurs after an "updateComplete" and "pagerComplete" event. There may be some other table interaction if a widget includes any setTimeout functions. +
$(function() {
+
+  // initialize the tablesorter plugin
+  $('table')
+    .tablesorter()
+    // bind to sort events
+
+    .bind('tablesorter-ready', function(e, table) {
+      // do something after tablesorter has finished interacting with the table;
+    });
+
+});
+
This event fires after tablesorter has completed executing the refreshWidget method (v2.19.0) +
+
+ Use it as follows: +
$(function() {
+
+  // initialize the tablesorter plugin
+  $('table')
+    .tablesorter()
+    // bind to sort events
+    .bind('refreshComplete', function(e, table) {
+      // do something after the 'refreshWidgets' has refreshed
+    });
+
+});
+
Widget Events
Event triggered when the filter widget has finished initializing (v2.4). +

+ You can use this event to modify the filter elements (row, inputs and/or selects) as desired. Use it as follows:
$(function() {
+  $('table')
+
+    // bind to filter initialized event BEFORE initializing tablesorter*
+    .bind('filterInit', function(event, config) {
+      $(this).find('tr.tablesorter-filter-row').addClass('fred');
+    })
+
+     // initialize the sorter
+    .tablesorter({
+      widgets : ['filter']
+    });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
Example
Event triggered when the filter widget has started processing the search (v2.4). +

+ You can use this event to do something like add a class to the filter row. Use it as follows:
$(function() {
+  $('table').bind('filterStart', function(event, filters) {
+    // filters contains an array of the current filters
+    $(this).find('tr.tablesorter-filter-row').addClass('filtering');
+  });
+});
+
Example
Event triggered when the filter widget has finished processing the search (v2.4). +

+ You can use this event to do something like remove the class added to the filter row when the filtering started. Use it as follows:
$(function() {
+  $('table').bind('filterEnd', function(event, config) {
+    $(this).find('tr.tablesorter-filter-row').removeClass('filtering');
+  });
+});
+
Example
Event triggered when the stickyHeader widget has finished initializing (v2.10.4). +

+ You can use this event to do something like modify content within the sticky header:
$(function() {
+  $('table')
+    // bind to the init event BEFORE initializing tablesorter*
+    .bind('stickyHeadersInit', function() {
+      // this.config.widgetOptions.$sticky contains the entire sticky header table
+      this.config.widgetOptions.$sticky.find('tr.tablesorter-headerRow').addClass('sticky-styling');
+    })
+
+  // initialize the tablesorter plugin
+  .tablesorter({
+    widgets : ['stickyHeaders']
+  });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
Event triggered after any widget has finished being removed (v2.29.0). +

+ You can use this event to do something like remove the class added to the filter row when the filtering started. Use it as follows:
$(function() {
+  $('table').bind('widgetRemoveEnd', function(event, table) {
+    // do something after widget was removed
+  });
+});
+ This method does not include data on which widget was removed. +
+
Example
Pager Events
This event fires when the pager plugin begins to render the table on the currently selected page. (v2.0.7). +
+
$(function() {
+
+  // initialize the sorter
+  $("table")
+    .tablesorter()
+
+    // initialize the pager plugin
+    .tablesorterPager({
+      container: $("#pager")
+    })
+
+    // bind to pager events
+    .bind('pagerChange pagerComplete', function(event, options) {
+      // options = table.config.pager (pager addon)
+      // options = table.config (pager widget) - so use options.pager.page below
+      // this.totalPages contains the total number of pages (this = table)
+      $('#display').html( event.type + " event triggered, now on page " + (options.page + 1) );
+    });
+
+});
+
Example
This event fires when the pager plugin has completed initialization (v2.18.1), and its render of the table on the currently selected page. (v2.0.7). +
+
+ Note In v2.18.1, the "pagerComplete" event also fires off immediately after pager initialization.
+
+
$(function() {
+
+  // initialize the sorter
+  $("table")
+    .tablesorter()
+
+    // initialize the pager plugin
+    .tablesorterPager({
+      container: $("#pager")
+    })
+
+    // bind to pager events
+    .bind('pagerChange pagerComplete', function(event, options) {
+      // options = table.config.pager (pager addon)
+      // options = table.config (pager widget) - so use options.pager.page below
+      // c.totalPages contains the total number of pages
+      $('#display').html( event.type + " event triggered, now on page " + (options.page + 1) );
+    });
+
+});
+
Example
This event fires after all pager controls have been bound and set up but before the pager formats the table or loads any ajax data (v2.4.4). +
+
$(function() {
+
+  $("table")
+
+    // bind to pager initialized event BEFORE calling the ADDON
+    // or BEFORE initializing tablesorter when using the pager WIDGET*
+    .bind('pagerBeforeInitialized', function(event, options) {
+      // options = table.config.pager (pager addon)
+      // options = table.config (pager widget)
+
+      // event = event object; options = pager options
+    })
+
+    // initialize the sorter
+    .tablesorter()
+
+    // initialize the pager plugin
+    .tablesorterPager({
+      container: $("#pager")
+    });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
This event fires when the pager plugin has completed initialization (v2.4.4). +
+
$(function() {
+
+  $("table")
+
+    // bind to pager initialized event BEFORE calling the ADDON
+    // or BEFORE initializing tablesorter when using the pager WIDGET*
+    .bind('pagerInitialized', function(event, options) {
+      // options = table.config.pager (pager addon)
+      // options = table.config (pager widget) - so use options.pager.page below
+      // c.totalPages contains the total number of pages
+      $('#display').html( e.type + " event triggered, now on page " + (options.page + 1) );
+    })
+
+    // initialize the sorter
+    .tablesorter()
+
+    // initialize the pager plugin
+    .tablesorterPager({
+      container: $("#pager")
+    });
+
+});
* Note: bind to all initialization events before initializing tablesorter; because most of the time, if bound after, the events will fire before the binding occurs.
+
Example
This event fires when the pager plugin begins to change to the selected page (v2.4.4). +
+ This event may fire before the pagerComplete event when ajax processing is involved, or after the pagerComplete on normal use. + See issue #153. +
$(function() {
+
+  // initialize the sorter
+  $("table")
+    .tablesorter()
+
+    // initialize the pager plugin
+    .tablesorterPager({
+      container: $("#pager")
+    })
+
+    // bind to pager events
+    .bind('pageMoved', function(event, options) {
+      // options = table.config.pager (pager addon)
+      // options = table.config (pager widget) - so use options.pager.page below
+      // c.totalPages contains the total number of pages
+      $('#display').html( event.type + " event triggered, now on page " + (options.page + 1) );
+    });
+
+});
+
Example
+ + +

Tablesorter API

+ +
+ tablesorter has some useful internal variables & functions available through the API which can be used in custom coding, parsers and/or widgets.
+ +

Variables

+ TIP! Click on the link in the variable column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VariableTypeDescriptionLink
ObjectThis object contains all of the default settings shown in the configuration section. +
+

+ Changing a defaults will cause all tables using the code to alter its default behavior without needing to include the options while initializing. For example, changing the default theme will cause all tables to use the new theme (make sure to include the css!). +

+ Note: If you wish to modify this object, make sure to either target the setting directly:
+// set the default directly
+$.tablesorter.defaults.theme = 'myCustomTheme';
+ Or use jQuery extend (include true parameter for more depth):
+// Extend the object. Use `true` for more than one level of depth
+$.extend( true, $.tablesorter.defaults, {
+  theme: 'myCustomTheme',
+  widgets: ['zebra', 'filter'],
+  widgetOptions: {
+    filter_placeholder: {
+      search: 'Find in column...',
+      select: 'Choose an option'
+    },
+    zebra: [ 'row-even', 'row-odd' ]
+  }
+});
+ If you don't use one of the above methods, the other default settings will be removed and cause unexpected issues. +
+
ArrayThis is an array of all parser objects added using the addParser function. +
+
+ If a specific parser needs to be retreived from this array, use the getParserById function. +
+
$.tablesorter.themesObjectThis is an object containing a list of specific class names to be applied to table elements. Please see the widget uitheme option for more details.
ArrayThis is an array of all widget objects added using the addWidget function. +
+
+ If a specific widget needs to be retreived from this array, use the getWidgetById function. +
+
ObjectThis object contains the phrases (in English by default) added to the aria-label on each header column (v2.24.4). +
+

In v2.24.4, sortDisabled was added to the language settings. It is added to disabled columns which may have a sort applied; so, instead of "nextAsc", "nextDesc" or "nextNone" being appended to the sort message, the "sortDisabled" content is added.

+ This is how the object is set up: +
$.tablesorter.language = {
+  sortAsc      : 'Ascending sort applied, ',
+  sortDesc     : 'Descending sort applied, ',
+  sortNone     : 'No sort applied, ',
+  sortDisabled : 'sorting is disabled', // added v2.24.4
+  nextAsc      : 'activate to apply an ascending sort',
+  nextDesc     : 'activate to apply a descending sort',
+  nextNone     : 'activate to remove the sort'
+};
+ So, as an example, in the following situation: +
    +
  • A table header is named "Account #"
  • +
  • This column has an ascending sort applied
  • +
  • The next click on the header will be a descending sort, which means: +
      +
    • sortInitialOrder option has its default setting; so the sort order will switch from ascending to descending on the second click.
    • +
    • No lockedOrder is set within the headers option
    • +
    +
  • +
+ Then the label will be built as follows: +
// "Header Name" + $.tablesorter.language.sortAsc + $.tablesorter.language.nextDesc
+"Account #: Ascending sort applied, activate to apply a descending sort"
+ If the next click were to reset the sort (sortReset applied), then the message would use $.tablesorter.language.nextNone.
+
+ In v2.24.4, if sorting is disabled but the column has a sort applied, the label will be built as follows: +
// "Header Name" + $.tablesorter.language.sortDesc + $.tablesorter.language.sortDisabled
+"Account #: Descending sort applied, sorting is disabled"
+ Use this variable to change the language as follows: +
$(function() {
+
+  $.tablesorter.language = {
+    sortAsc      : 'sorting from a to z, ',
+    sortDesc     : 'sorting from z to a, ',
+    sortNone     : 'not sorted, but ',
+    sortDisabled : 'sorting is disabled',
+    nextAsc      : 'click to sort from a to z',
+    nextDesc     : 'click to sort from z to a',
+    nextNone     : 'click to clear the sort'
+  }
+  $('table').tablesorter();
+});
+
+
+ 1 + 2 +
ObjectThis variable contains all instance methods of the config object. Added using addInstanceMethods function before table initialization (v2.21.0). +
+
+
$.tablesorter.addInstanceMethods({
+  columnSum: function(colNumber) {
+    var sum = 0, tbodyIndex, normalizedRows, rowIndex;
+    // `this` refers to config object
+    for (tbodyIndex = 0; tbodyIndex < this.$tbodies.length; ++tbodyIndex) {
+      normalizedRows = this.cache[tbodyIndex].normalized;
+      for (rowIndex = 0; rowIndex < normalizedRows.length; ++rowIndex) {
+        sum += normalizedRows[rowIndex][colNumber];
+      }
+    }
+    return sum;
+  },
+  columnMean: function(colNumber) {
+    return this.columnSum(colNumber) / this.totalRows;
+  },
+});
+
+$('table').tablesorter();
+c = $('table')[0].config;
+console.log('sum of third column: ' + c.columnSum(2));
+console.log('mean of third column: ' + c.columnMean(2));
+
+
Access the table configuration variables (config) using any of these methods: +
+
+
// pure js, get the first table (zero index)
+var config = document.getElementsByTagName('table')[0].config;
+// or by table ID
+var config = document.getElementById('mytable').config;
+
+// using jQuery, get first table (zero index)
+var config = $('table')[0].config;
+// or from the jQuery data
+var config = $('#mytable').data('tablesorter');
+
+
+
ObjectInternal list of table contents (v2.0.18; v2.20.0 ) +
+
+ This object contains the following:
+
    +
  • tbody index (non-info block only, indexing now matches the config.$tbodies variable; v2.20.0) +
      +
    • colMax +
        +
      • This contains an array of the absolute value maximum numerical value for each "numerically" parsed column per tbody.
      • +
      • Text columns will have an undefined value in this array.
      • +
      • Used when determining how to sort text within a numeric column.
      • +
      • Access it as follows:
        // $('table')[0].config.cache[tbodyIndex].colMax[column];
        +// try this in the console for this page:
        +$('.tablesorter')[0].config.cache[0].colMax;
        +// result: [undefined × 3, 45, 153.19, 44.7, 100.9, 1169133120000]
      • +
      +
    • +
    • normalized +
        +
      • This contains an array of indexed table rows.
      • +
      • Within each row is an array of extracted, then parsed table contents for each column (plus one extra value; see the next comment).
      • +
      • It is important to note that the last value in the column array is the original row index value. This is used when resetting a column sort to its original unsorted order.
      • +
      • In v2.19.1, the raw unparsed data was added to the row data.
      • +
      • In v2.16.0, the last value in the column array is now an object which contains a jquery object of the row, index of the original unsorted order and a child array which contains raw html from any associated child row +
        // $('table')[0].config.cache[tbodyIndex].normalized[row][column]
        +// try this in the console for this page:
        +$('.tablesorter')[0].config.cache[0].normalized[0];
        +/* result: ["a1", "bruce", "almighty", 45, 153.19, 44.7, 77, 979830720000, {
        +    $row  : jQuery.fn.jQuery.init[1], // row (jQuery object)
        +    child : [], // child row raw html, if any
        +    // raw unparsed data from the table cells - added v2.19.1
        +    raw   : ["A1", "Bruce", "Almighty", "45", "$153.19", "44.7%", "+77", "Jan 18, 2001 9:12 AM"],
        +    order : 3   // original row index (unsorted)
        +}]
        +*/
        + to specifically target the extra row data, use this method: +
        var config = $('.tablesorter')[0].config,
        +    dataIndex = config.columns, // number of columns within the table
        +    rowData = config.cache[tbodyIndex].normalized[row][ dataIndex ];
        +
      • +
      • Note that all text values will be in lower case if the ignoreCase option is true.
      • +
      • This internal normalized content is what is actually sorted for maximum performance.
      • +
      +
    • +
    • row (removed in v2.16.0) +
        +
      • Well not removed, but moved into the row data, within the normalized section of the cache as described above.
      • +
      • This contains an array of jQuery row objects.
      • +
      • These rows are never in sort order, and are used when updating the table after sorting the normalized content. The indexing of these rows is cross-referenced within the normalized values - the extra column value within the row array. Hopefully that makes sense.
      • +
      +
    • +
    +
  • +
+ The table.config.cache variable is useful when writing widgets that need access to the parsed content. +
+
NumericInternal count of the number of table columns in the header (v2.12) +
+
+ This number is stored as a length (one-based), and takes into account any colspan and rowspan within the table head.
+
+ Note that the table.config.columns variable does not always correlate with the indexing of the headers stored within table.config.$headers because of multiple rows and column or row spans. +
+
ArrayInternal list of each header's starting HTML (as text) (v2.8) +

+ This HTML snapshot is taken using the $headers jQuery object, and done before the headerTemplate is applied and before the onRenderTemplate and onRenderHeader callbacks are executed.
+
+ This list is used by the $.tablesorter.restoreHeaders function to restore the table headers when the destroy method is executed. +
+
ArrayInternal list of each header element as selected using jQuery selectors in the selectorHeaders option. +
+
+ This list contains DOM elements (not jQuery objects of each table header cell like the $headers variable) and is how the original version of tablesorter stored these objects. +
+ It is not used in the current version of tablesorter, and is only left in place for backwards compatibility with widgets written for the original tablesorter plugin. +
+
ArrayInternal list of all of the table's currently set parsers (objects copied from $.tablesorter.parsers). Here is a complete list of default parsers: (modified v2.18.0) +
+
+ + + + + + + + + + + + + + + +
parser: falsedisable parsing for this column (this also disables sorting & filtering for the column; v2.17.6).
sorter: falsedisable sort for this column.
sorter: "text"Sort alpha-numerically.
sorter: "digit"Sort numerically.
sorter: "currency"Sort by currency value (supports "£$€¤¥¢").
sorter: "image"Sort by image alt value (see imgAttr option; added in v2.18.0).
sorter: "ipAddress"Sort by IP Address; Warning This parser was moved to the parser-network.js file in v2.18.0.
sorter: "url"Sort by url.
sorter: "isoDate"Sort by ISO date (YYYY-MM-DD or YYYY/MM/DD; these formats can be followed by a time).
sorter: "percent"Sort by percent.
sorter: "usLongDate"Sort by date (U.S. Standard, e.g. Jan 18, 2001 9:12 AM or 18 Jan 2001 9:12 AM (new in v2.7.4)).
sorter: "shortDate"Sort by a shortened date (see dateFormat; these formats can also be followed by a time).
sorter: "time"Sort by time (23:59 or 12:59 pm).
sorter: "metadata"Sort by the sorter value in the metadata - requires the metadata plugin.
+

Check out the headers option to see how to use these parsers in your table (example #1).

+ Or add a header class name using "sorter-" plus the parser name (example #2 & #3), this includes custom parsers (example #4). +
+
+ 1 + 2 + 3 + 4 +
ArrayInternally stored Array of headers that represent each column (v2.21.0) +
+
+ The table.config.$headers variable contains ALL header cells, not all of which contain sorting information (data-attributes, class names or sorting information). +

+ This variable targets the last sortable header cell in a particular column; unless an entire column is completely unsortable (checkbox column), then it just picks the last cell in that column. +

+ To make that description less confusing, look at the HTML in the config.$headers documentation, only the four middle rows (header-index 1-4) will be contained within this variable: +
// the resulting config.$headerIndexed for the HTML example in config.$headers will look like this:
+console.log( table.config.$headerIndexed );
+/* outputs : [
+  $('<th data-column="0">header-index 1</th>'),
+  $('<th data-column="1">header-index 2</th>'),
+  $('<th data-column="2">header-index 3</th>'),
+  $('<th data-column="3">header-index 4</th>')
+] */
+

+ * NOTE * This variable contains an array of jQuery objects, it is not a collection of jQuery objects, i.e. +
var $column = table.config.$headerIndexed[ 0 ]; // jQuery object returned
+console.log( $column.hasClass('foo') ); // how to access information
+
+var $headers = $( table.config.$headerIndexed ); // make a collection of jQuery objects
+// then use collection manipulation functions
+$headers.each(function() {
+  console.log( $(this).text() );
+});
+
jQuery ObjectInternal list of all table header cells (v2.8) +
+

+ *NOTE* The header cells within rows with the cssIgnoreRow class (default is "tablesorter-ignoreRow" will not be included in this variable.

+

Header cells in not-ignored rows are targeted using the jQuery selector from the selectorHeaders option

+ Please note that the headers cells are simply an array of targetted header cells and should not be targeted using a column index. For example, given the following table thead markup, the header-index counts the header th cells and does not actually match the data-column index when extra rows and/or colspan or rowspan are included in any of the header cells: +
<thead>
+	<tr>
+		<th colspan="4" data-column="0" class="sorter-false">header-index 0</th>
+	</tr>
+	<tr>
+		<th data-column="0">header-index 1</th>
+		<th data-column="1">header-index 2</th>
+		<th data-column="2">header-index 3</th>
+		<th data-column="3">header-index 4</th>
+	</tr>
+	<!-- the next row WILL NOT be included in the config.$headers variable -->
+	<tr class="tablesorter-ignoreRow">
+		<th colspan="2" data-column="0">This cell is not included in the config.$headers</th>
+		<th colspan="2" data-column="2">This cell is not included in the config.$headers</th>
+	</tr>
+	<tr>
+		<th colspan="2" data-column="0" class="sorter-false">header-index 5</th>
+		<th colspan="2" data-column="2" class="sorter-false">header-index 6</th>
+	</tr>
+</thead>
+ So, in the above example, to target the header cell in the second table column (data-column index of 1), use the following code: table.config.$headers.filter('[data-column="1"]') or table.config.$headers.eq(2).
+
+ The table.config.$headers variable is useful within callback functions or when writing widgets that target the table header cells. +
+
DOM elementInternally stored DOM table element (v2.17.5) +
+
+ The table.config.table variable is useful when certain callback functions only pass the config object; originally you could use config.$table[0], but this saves that small extra trouble. +
+
jQuery ObjectInternally stored jQuery object of the table (v2.7.1) +
+
+ The table.config.$table variable is useful within callback functions or when writing widgets that target the table. +
+
ObjectInternally stored object of column specific sort variables (v2.24.0; v2.30.7) +
+
+ This object is defined as follows:
+
// $('table')[0].config.sortVars -> result
+[{
+  // column 0
+  count: 0, // click count; used as index for order value
+  order: [ 0, 1, 2 ], // default sort (sortReset: true)
+  lockedOrder: false, // true if column order is locked (read only)
+  sortedBy: 'user'
+},{
+  // ...
+},{
+  // column n
+  count: 0, // click count; used as index for order value
+  order: [ 0, 1, 2 ], // default sort (sortReset: true)
+  lockedOrder: false, // true if column order is locked (read only)
+  sortedBy: 'sorton'
+}]
+
    +
  • count +
      +
    • The initial value is -1.
    • +
    • The value is incremented each time the user clicks on a header cell; but it resets to zero once it reaches the end of the order array.
    • +
    +
  • +
  • order +
      +
    • An array that contains the sort sequence for each column.
    • +
    • 0 indicates an ascending sort.
    • +
    • 1 indicates a descending sort.
    • +
    • 0 indicates a reset sort (initial unsorted rows).
    • +
    • Locked sorts will contain either [0, 0] or [1, 1] determined by the lockedOrder setting.
    • +
    • Setting these values to [2, 2] will lock in an unsorted column, but the unsort arrows will still be visible; use a "sorter-false" class instead.
    • +
    • The values of this order can only be modified after tablesorter has initialized.
    • +
    +
  • +
  • lockedOrder +
      +
    • See the lockedOrder setting.
    • +
    • It's readonly because it is set during initialization and not used again (future use by a widget?)
    • +
    +
  • +
  • sortedBy + These values are added to the header cell in a data-sortedBy attribute and may contain: +
      +
    • 'user' (renamed from 'mouseup' event) when a column is sorted by a user.
    • +
    • 'sort' when a column is sorted by triggering a sort event on the header.
    • +
    • 'sorton' when a column is sorted using the sorton method.
    • +
    • 'sortAppend' when a sorted column is added by the sortAppend option.
    • +
    • 'sortForce' when a sorted column is added by the sortForce option.
    • +
    +
  • +
+
+
jQuery ObjectInternally stored jQuery object of table non-info block tbodies (v2.7.1) +
+
+ jQuery object of sortable tbodies.
+
+ Caution! These tbodies are the ones without a class name from the cssInfoBlock option, so the indexing of this jQuery object does not match the actual table tbody indexes.
+
+ The table.config.$tbodies variable is useful within callback functions or when writing widgets that target the table. +
+
BooleanBoolean value indicating that tablesorter has been initialized on a table +
+
+ This variable is not stored within the config, as the config would not be defined (an alternative method of determining if tablesorter has been initialized).
+
+ Access it as follows: $('table')[0].hasInitialized. It is true while tablesorter is active on a table, and false if tablesorter was destroyed. +
+
NumericThis variable contains the total number of rows within the table, not including rows within info-only tbodies (v2.17.4; v2.17.5) +
+
+ Access this internal value after tablesorter has initialized. It is also included as a variable in the filterEnd event so you can update an external count like this: +
$('table').bind('filterInit filterEnd', function(event, data) {
+    // use data.filteredRows or this.config.filteredRows
+    $('.filter-rows').html( data.filteredRows );
+    $('.total-rows').html( data.totalRows );
+});

+ If using the pager plugin or widget, the value returned from the filterEnd event will not be accurate, so you'll need to bind to the pagerComplete event instead: +
$('table').bind('filterInit filterEnd pagerComplete', function(event, data) {
+    // Note: data = table.config (filterEnd event); and data = table.config.pager (pagerComplete event)
+    // both objects contain data.filteredRows & data.totalRows
+    $('.filter-rows').html( data.filteredRows );
+    $('.total-rows').html( data.totalRows );
+});
+
+
Access the widgetOptions (wo) using any of these methods: +
+
+
// pure js, get the first table (zero index)
+var wo = document.getElementsByTagName('table')[0].config.widgetOptions;
+// or by table ID
+var wo = document.getElementById('mytable').config.widgetOptions;
+
+// using jQuery, get first table (zero index)
+var wo = $('table')[0].config.widgetOptions;
+// or from the jQuery data
+var wo = $('#mytable').data('tablesorter').widgetOptions;
+
+
+
jQuery ObjectOnly available when the filter widget is active. This variable contains all external search inputs with data-column="all", bound using the bindSearch function. +
+
+ The table.config.widgetOptions.filter_$anyMatch variable contains one more more search inputs, and is dynamically updated if the bindSearch function is called; make sure to set the flag to force a new search so that the values of the altered filters is updated appropriately. +
+
jQuery ObjectOnly available when the filter widget is active. This variable contains all table cells within the filter row. +
+
+ Use the table.config.$filters variable when access to filters is needed. Note! This variable contains the table cell and not the actual input because the filter_formatter function allows adding other types of value selectors (e.g. jQuery UI slider). +
+
jQuery ObjectOnly available when the filter widget is active. This variable contains all external search inputs bound using the bindSearch function. +
+
+ The table.config.widgetOptions.filter_$externalFilters variable contains an array of jQuery objects pointing to all external inputs, even if the bindSearch function is used multiple times. +
+
wo.filter_initializedBooleanOnly available when the filter widget is active. This variable is true once the filter widget has initialized; it is undefined otherwise.
NumericOnly available when the filter widget is active. This variable contains the current number of filtered rows (v2.17.4; v2.17.5) +
+
+ This internal value will show an accurate number of filtered rows; which means the count won't include any rows from "information-only" tbodies.
+
+ Access this internal value at any time after the filter widget has initialized, or as a shortcut it is included as a variable in the filterEnd event so you can update an external count like this: +
$('table').bind('filterInit filterEnd', function(event, data) {
+    // use data.filteredRows or this.config.filteredRows
+    $('.filter-rows').html( data.filteredRows );
+    $('.total-rows').html( data.totalRows );
+});

+ If using the pager plugin or widget, the value returned from the filterEnd event will not be accurate, so you'll need to bind to the pagerComplete event instead: +
$('table').bind('filterInit filterEnd pagerComplete', function(event, data) {
+    // Note: data = table.config (filterEnd event); and data = table.config.pager (pagerComplete event)
+    // both objects contain data.filteredRows & data.totalRows
+    $('.filter-rows').html( data.filteredRows );
+    $('.total-rows').html( data.totalRows );
+});
+
+
jQuery ObjectOnly available when the stickyHeaders widget (not the css3 version) is active. +
+
+ The table.config.widgetOptions.$sticky variable contains a jQuery object pointing to a cloned table containing the sticky header. The table contained within this variable has a class name of "containsStickyHeaders" versus the original table with a class name of "hasStickyHeaders". +
+
Access the pager options (p) using any of these methods: +
+
+
// pure js, get the first table (zero index)
+var p = document.getElementsByTagName('table')[0].config.pager;
+// or by table ID
+var p = document.getElementById('mytable').config.pager;
+
+// using jQuery, get first table (zero index)
+var p = $('table')[0].config.pager;
+// or from the jQuery data
+var p = $('#mytable').data('tablesorter').pager;
+
+ These methods are the same for both the pager addon and the pager widget. +

+*NOTE* This pager variable also contains all of the default pager option settings. +

+*NOTE* When using the pager widget, changing a value within the widgetOptions for the pager may not update the pager as expected. The pager widget uses most of the values within config.pager instead of the pager widgetOptions after initialization. +
+
jQuery ObjectContains a jQuery object pointing to a pager block(s). +
+
+ The table.config.pager.$container variable is targeted using the pager container option and will have a class name of "tablesorter-pager" applied.
+
+ The container(s) may include the pager navigation arrows, page size selector, page selector and/or output block; the contents are completely customizable and all components are optional. +
+
jQuery ObjectContains a jQuery object pointing to an empty select within the pager block(s). +
+
+ The table.config.pager.$goto variable is targeted using the pager cssGoto option.
+
+ The contents of this select element are dynamically updated if the page size is changed or if the table gets filtered. +
+
jQuery ObjectContains a jQuery object pointing to a select with the desired page size settings already in place. +
+
+ The table.config.pager.$size variable is targeted using the pager cssPageSize option.
+
+ The contents of this select element must be set up with the desired page size settings before initializing tablesorter.
+
+ Also, because Internet Explorer does not appear to get the select value from the option text, please include a value attribute with all options. For example: +
<select class="pagesize">
+  <option value="10">10</option>
+  <option value="20">20</option>
+  <option value="30">30</option>
+  <option value="40">40</option>
+</select>
+
+
ArrayContains an array of zero-based row indexes of rows that currently displayed within the table. +
+
+ For example, if you want to get the parsed values for the rows currently displayed within the pager, use the table.config.pager.cacheIndex variable as follows: +
var c = $('table')[0].config,
+	p = c.pager,
+	cache = c.cache[0].normalized,
+	cachedValues = [];
+$.each( p.cacheIndex, function(i, v) {
+	// cache[v] will be an array of parsed values for each cell in selected row
+	cachedValues.push( cache[v] );
+});
+ the p.cacheIndex variable get updated whenever the table is sorted, filtered or the pager changes pages or page size. +
+
NumericContains a one-based index of the first row visible in the pager. +
+
+ The {startRow} tag in the pager output option is replaced by this value; and it does not correspond to the row index within the cache. +
+
NumericContains a one-based index of the last row visible in the pager. +
+
+ The {endRow} tag in the pager output option is replaced by this value; and it does not correspond to the row index within the cache. +
+
NumericContains the page count, determined by the page size setting, after the table is filtered. It equals the totalPages if no filters are applied. +
+
+ The {filteredPages} tag in the pager output option is replaced by this value. +
+
NumericContains the number of rows accessible by the pager after the table is filtered. It equals the totalRows if no filters are applied. +
+
+ The {filteredRows} tag in the pager output option is replaced by this value. +
+
NumericContains a zero-based index of the current page visible in the pager. +
+
+ The {page} tag in the pager output option is replaced by this value.
+
+ Initially, this value is set by either the pager page option or from local storage if the savePages option is true. It is then updated by user interaction with the page selector (targeted by the cssGoto option or programmically by the pageSet or pageAndSize method. +
+
NumericContains the currently selected page size. +
+
+ Initially, this value is set by either the pager size option or from local storage if the savePages option is true. It is then updated by user interaction with the size selector (targeted by the cssPageSize option or programmically by the pageSize or pageAndSize method. +
+
NumericContains the total page count as determined by the page size setting. +
+
+ The {totalPages} tag in the pager output option is replaced by this value. +
+
NumericContains the total number of rows within the table +
+
+ The {totalRows} tag in the pager output option is replaced by this value. +
+
Removed Variables
jQuery ObjectInternal list of all extra table header cells (v2.16.2; removed v2.21.3) +
+

This variable was removed due to it causing memory leak issues. To now find extra headers use config.namespace as follows:

+
$( config.namespace + '_extra_headers' )
+

When the bindEvents function is used, the extra (external) header cells are added to this variable, and automatically updated with the table headers (config.$headers).

+

So, when writing a custom widget that clones the table header, there is no longer a need to update the header class names, it's all done automatically.

+
+
jQuery ObjectInternal list of all extra (cloned) table elements (v2.19.0; removed v2.21.3) +
+

This variable was removed due to it causing memory leak issues. To find extra tables use config.namespace as follows:

+
$( config.namespace + '_extra_table' )
+

Some widgets need to duplicate parts of the original table to provide functionality (e.g. stickyHeaders, scroller). This saved variable will either not exist or contain a jQuery object pointing to the cloned table elements.

+

This variable was added for the uitheme widget to allow dynamic updating of themes to the original table as well as all cloned parts.

+
+
+ +
+

Functions

+ TIP! Click on the link in the function column to reveal full details (or toggle|show|hide all) or double click to update the browser location. +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionDescription
Core Functions
This function adds a colgroup element to the table when widthFixed is true. +

+ A new colgroup with col elements is only added if: +
    +
  • widthFixed is true.
  • +
  • A predefined colgroup element does not already exist in the table *.
  • +
+ * Note If a colgroup was added by the plugin, calling this function additional times will refresh the set widths
+
+ Also, the col elements within the colgroup are set with a percentage width to dynamically maintain the fixed column width ratios.
+
+ Use it as follows: +
$.tablesorter.fixColumnWidth( table );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
+
+
+ This function detaches the targeted tbody from the DOM to allow faster manipulation of the tbody contents (v2.4). +
+ Use it as follows: +
$.tablesorter.processTbody( table, $(tbody), getIt );
+
    +
  • table - table DOM element (or jQuery object) of table containing the tbody.
  • +
  • $(tbody) - tbody jQuery object.
  • +
  • getIt - Boolean flag (optional if false).
  • +
+ When calling the function, to get a tbody, set the getIt boolean parameter to true and the removed tbody is returned; setting this option to false or optionally not including it will restore the tbody. +
+ Here is a basic example of how this function is used: +
var tbodyIndex, _tbody,
+	table = $('#my-table')[0],
+	tbodies = table.tBodies, // use table.config.$tbodies for all tbodies EXcluding information only tbodies
+for (tbodyIndex = 0; tbodyIndex < tbodies.length; tbodyIndex++) {
+	// detach tbody from table
+	_tbody = $.tablesorter.processTbody( table, tbodies[tbodyIndex], true );
+	// do something magical to the tbody
+	_tbody.addClass('unicorn');
+	// restore tbody
+	$.tablesorter.processTbody( table, _tbody );
+}
+ Please note that completely detaching the tbody was found to be a much quicker method of manipulating DOM elements than just hiding the tbody; this is especially true in older browsers. +
+
This function adds the processing (indeterminant loading icon) to specific or all header cells while processing table elements (v2.4). +

+ Use it as follows: +
$.tablesorter.isProcessing( table, toggle, $ths );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • toggle - Boolean flag.
  • +
  • $ths - jQuery object of targeted header cells (optional; if excluded all header cells are targeted).
  • +
+ When calling the function, set the toggle option to add (true) or remove (false) process indicators. Include any specific header cells within the $ths variable with which to add the process indicator. When $ths is not defined and a sort is applied, the currently sorted header cells will show process indicators.
+
+ All this function does is add or remove a class name of "tablesorter-processing" and the class name contained within the cssProcessing option.
+
+ Here is a basic example of how this function is used: +
$('table').bind('sortBegin sortEnd', function(event, table) {
+	// this is included with the basic functionality of tablesorter
+	$.tablesorter.isProcessing( this, event === 'sortBegin' );
+});
+ Please note that currently the processing icons do not animate (see issue #158). This is due to javascript being a single-threaded, meaning it only does one task at a time, and maximizing the sorting script. So lots of processing is needed to sort & rebuild the table and thus it has no time for animation. If someone knows of a better solution, please share! +
+
This function empties ALL of the table tbodies (v2.17.2). +

+ Use it as follows: +
$.tablesorter.clearTableBody( table );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
+ Please note that this function uses jQuery empty(). All data & event handlers are removed. No where within the tablesorter script or included widgets is this function used, it is left intact for backwards compatibility. +
+
This function adds header event listeners to the targeted cells (v2.8; v2.16.2). +

+ Use it as follows: +
$.tablesorter.bindEvents( table, $headers );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • $headers - jQuery object of targeted cells.
  • +
+ This function allows you to bind the same header event listeners to external headers cells (usually clones of the original table). This includes the triggered sort event, left click (only) to sort, ignoring long clicks (> 250ms), pressing enter to trigger a sort (must have focus and a tabindex attribute) and cancelling selection of text (if the option is set).
+
+ To ensure the columns match the original table, include data-column attributes pointing to the desired column.
+
+ Here is a basic example of how this function is used: +
var $table = $('table'),
+	// make a copy of the table
+	$clonedTable = $table.clone().addClass('clonedTable').insertAfter($table);
+// remove stuff we don't need in the clone
+$clonedTable.find('tfoot,tbody').remove();
+// bind events to the cloned headers
+$.tablesorter.bindEvents( $table, $clonedTable.find('th') );
+ In v2.16.2, this function now saves all extra (external) headers to the config.$extraHeaders variable which allows the plugin to automatically update the sort status. +
+
This function restores the table headers cells with their original content (v2.8). +

+ The original header cell content is saved, within the headerContent variable array, before the headerTemplate is applied and before the onRenderTemplate and onRenderHeader callbacks are executed.
+
+ Use it as follows: +
$.tablesorter.restoreHeaders( table );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
+ Please note that only header cells that still contain a div with a class name of tablesorter-header-inner will have their contents restored; it assumes that the contents have already been restored. +
+
This function completely removes tablesorter, including all widgets, associated data & event handlers from the table (v2.3.2). +

+ Use it as follows: +
$.tablesorter.destroy( table, removeClasses, callback );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • removeClasses - Boolean flag
  • +
  • callback - Function executed once tablesorter has been removed.
  • +
+ When calling the function, set the removeClasses option to true to include removing of the "tablesorter" class name, tablesorter theme name (e.g. "tablesorter-blue") and the class name applied by the tableClass option. The callback function only provides a table (DOM element only) parameter.
+
+ Here is a basic example of how this function is used: +
$.tablesorter.destroy( table, true, function(table) {
+	alert('tablesorter has been removed! No sort for you!');
+});
+ Please note that only header cells that still contain a div with a class name of tablesorter-header-inner will have their contents restored; it assumes that the contents have already been restored. +
+
This function sorts the a & b parameter using a natural sort (v2.12; v2.28.13). +

+ Access it as follows: +
$.tablesorter.sortNatural(a, b);
+
    +
  • a - string.
  • +
  • b - string to compare.
  • +
+ Here is a basic example of how this function is used: +
var myArray = [ '1a', '10a', '2a', '2b' ];
+// result: ["1a", "2a", "2b", "10a"]
+myArray.sort(function(a,b) { return $.tablesorter.sortNatural(a, b); });
+ Please note that this natural sort function only accepts strings (added v2.0.6; renamed v2.12).
+ As of v2.28.13, the natural sort function will convert all values into strings. +
+
This function sorts the a & b parameter using a basic sort (renamed v2.12). +

+ Access it as follows: +
$.tablesorter.sortText(a, b);
+
    +
  • a - string.
  • +
  • b - string to compare.
  • +
+ Here is a basic example of how this function is used: +
var myArray = [ '1a', '10a', '2a', '2b' ];
+// result: ["10a", "1a", "2a", "2b"]
+myArray.sort(function(a,b) { return $.tablesorter.sortText(a, b); });
+
+
This function replaces basic accented characters to better sorting & filtering of table contents (v2.2). +

+ Use it as follows: +
$.tablesorter.replaceAccents(string);
+
    +
  • string - a string to process & replace accented characters.
  • +
+ Here is a basic example of how this function is used: +
$.tablesorter.replaceAccents("áàâãä"); // result: "aaaaa"
+ This function is used when the sortLocaleCompare option is set to true. Please refer to the option and the demo for more details on the defaults values and how to add more accented characters. +
+
This function returns a zero-based index value of the position of the value within the array, otherwise it returns -1 (Modified v2.15.6). +

+ Use it as follows: +
$.tablesorter.isValueInArray(value, array);
+
    +
  • value - value to find within the array.
  • +
  • array - array (sortList) to search for the value.
  • +
+ Sadly, this function has limited usefulness outside of tablesorter. It is only meant to search a sortList array and determine if a column (value) is already contained within it. Here is a basic example of how this function is used: +
var sortList = [ [1,0], [2,0], [0,0] ];
+// result: 1
+$.tablesorter.isValueInArray(2, sortList);
+ After v2.15.6, this function returns a zero-based index of the position of the value within the array parameter, or -1 if the value is not in the array. Previously, this function returned a boolean value of true if the value was contained within the array, or false if not. +
+
This function allows the adding of custom parser scripts to the tablesorter core. +

+ Access it as follows: +
$.tablesorter.addParser(myParser);
+
    +
  • myParser - object containing parser code.
  • +
+ The myParser object must contain a proper template. The template must include an id, is, format and type blocks. Please refer to the writing custom parsers demo page for more details & an example. +
+
This function allows to add custom methods for config object (v2.21.0). +

+ Access it as follows: +
$.tablesorter.addInstanceMethods(methods);
+
    +
  • methods - an object containing methods to be added, indexed by method names. These methods can use config object by refering this.
  • +
+ Take a look at instanceMethods variable description for more details. +
+
This function returns the named parser object. +

+ Access it as follows: +
$.tablesorter.getParserById(name);
+
    +
  • name - the name (or id) of the parser.
  • +
+ Use this function as follows: +
var parser = $.tablesorter.getParserById("currency"),
+	value = parser.format('100%'); // returns 100 (number type, not a string)
+
+
This function allows the checking to see if a widget is installed (v2.17.4). +

+ Access it as follows: +
$.tablesorter.hasWidget( table, 'mywidget');
+
    +
  • table - table element or jQuery object of the selected table.
  • +
  • 'mywidget' - ID of the widget to check.
  • +
+ This function returns a boolean value, where true means the widget is currently installed and active; and false means the widget is not installed.
+
+ Use this function as follows: +
$.tablesorter.hasWidget( $('table'), 'zebra' ); // true
+
+
This function allows the adding of custom widget scripts to the tablesorter core (v2.19.0). +
+
+ In v2.19.0, a "refreshing" parameter was added to the remove widget function to indicate that the widget will be refreshed so it will only be temporarily removed (see this demo for more details).
+
+ Access it as follows: +
$.tablesorter.addWidget(myWidget);
+
    +
  • myWidget - object containing widget code.
  • +
+ The myWidget object must contain a proper template. The template must include an id and format blocks. The priority, options, init and remove blocks are optional.
+
+ Please refer to the writing custom widgets demo page for more details & an example. +
+
This function returns the named widget object. +

+ Access it as follows: +
$.tablesorter.getWidgetById(name);
+
    +
  • name - the name (or id) of the widget.
  • +
+ Use this function as follows: +
var widget = $.tablesorter.getWidgetById("saveSort"),
+	table = $('table')[0];
+// apply save sort widget to a table; but it will get removed if the refreshWidgets method
+// is triggered, unless the table.config.widgets array contains this widget name/id
+widget.format( table, table.config, table.config.widgetOptions );
+
+
This function applys (refreshes) all currently selected widgets on a table (v2.16.0; v2.19.0). +

+ Use it as follows: +
$.tablesorter.applyWidget( table, init, callback );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • init - optional, boolean initialization flag.
  • +
  • callback - optional, a function executed after all widgets have been applied; the only parameter is table (v2.19.0).
  • +
+ The init flag is only set to true to extend the default option values from the widget options block. If the widget(s) have already been applied to the table, just leave this parameter undefined.
+
+ This function is called when the applyWidgets method is triggered. +
+
This function removes, then reapplies all currently selected widgets on a table (v2.4; v2.19.0). +
+
+ In v2.19.0, this function was modified to internally use the removeWidget function & a "refreshComplete" event is now triggered upon completion.
+
+ Use it as follows: +
$.tablesorter.refreshWidgets( table, doAll, dontapply );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • doAll - optional, boolean flag.
  • +
  • dontapply - optional, boolean flag.
  • +
+ The doAll flag is set to true if all widgets contained with the global $.tablesorter.widgets array are to be removed.
+ When doAll is false, only widgets not contained within the config.widget option are removed; then if the dontapply flag is false, the widgets named in that option are reapplied (without removing them).
+
+ This function is called when the refreshWidgets method is triggered. +
+
This function removes selected widgets (v2.19.0). +

+ Use it as follows: +
$.tablesorter.removeWidget( table, names, refreshing );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • names - string (space or comma separated widget names), an array of widget names, or if true all installed widgets are removed.
  • +
  • refreshing - if true, the widget name will not be removed from the widgets option; any other setting and the name will be removed.
  • +
+ This function is used by the refreshWidgets function (v2.19.0). +
+
This function returns the column data from an object based on a column index or classname/id (v2.21.1). +
+
+ Use it as follows: +
$.tablesorter.getColumnData( table, object, key );
+
    +
  • table - table DOM element (or jQuery object).
  • +
  • object - object containing zero-based column indexes or column class names as a key (e.g. table.config.headers or table.config.widgetOptions.filter_functions; any data found as the value (of the key : value pair) will be returned.
  • +
  • key - key to be Object key; this can be a zero-based column index or header class name/id.
  • +
+ As a full example, say you have a header cell with a class name of "event". And you want to use the textExtraction function for that column, then you would use this function as follows: +
var table = $('table')[0],
+textExtractionFunction = $.tablesorter.getColumnData( table, table.config.textExtraction, ".event" );
+ The ".event" key can be replaced with a zero-based column index, if the textExtraction option is set up using indexes. +

+ This function is sometimes used in conjunction with the getData function. This function is called first because the getData function uses the result in the configHeaders parameter - the config.headers option result from this function would be an object and not a function. +
+
This utility function returns the entire column text (raw and parsed) as well as other data (v2.21.4; v2.24.0). +
+
+ Use it as follows: +
$.tablesorter.getColumnText( table, column, callback, rowFilter );
+
    +
  • table - table DOM element (or jQuery object).
  • +
  • column - zero-based column index or 'all'.
  • +
  • callback - callback function that is called while obtaining data for each cell within the specified column.
  • +
  • rowFilter - can be a jQuery selector, function or element to allow targeting specific rows (e.g. ":visible" or ":not(.child-row)"); this parameter is used in a jQuery .is() function (v2.24.0).
  • +
+ When using the callback, there is only one argument (object) passed to this function; it contains the following: +
    +
  • tbodyIndex - the tbody zero-based index of the current cell.
  • +
  • rowIndex - the row zero-based index of the current cell.
  • +
  • $row - a jQuery object targeting the current row being processed.
  • +
  • $cell - a jQuery object targeting the curretn cell being processed.
  • +
  • parsed - parsed text of the current cell.
  • +
  • raw - raw (unparsed) text of the current cell.
  • +
+ As an example, say you want to make the text of all cells with a value greater than 20 red (assuming the 'red' class name is defined in css). Do it as follows: +
var table = $('table')[0];
+// targeting the 4th column (zero-based index)
+$.tablesorter.getColumnText( table, 3, function( data ) {
+  if ( data.parsed > 20 ) {
+    data.$cell.addClass('red');
+  }
+});
+ In this example, the goal is to get data from rows visible after filtering. When the callback returns false, that row/cell data will not be included in the resulting data: +
var table = $('table')[0],
+  // getting data from ALL columns, we're just skipping filtered out rows (rows with a "filtered" class)
+  result = $.tablesorter.getColumnText( table, 'all', function( data ) {
+    return !data.$row.hasClass('filtered');
+  });
+  // let's just assume this example is only showing filter matching rows (rows without a "filtered" class ;)
+  // result = {
+  //   raw    : [ [ 'r1c1', 'r1c2', ..., 'r1cN' ], [ 'r2c1', 'r2c2', ..., 'r2cN' ] ..., [ 'rNc1', 'rNc2', ..., 'rNcN' ] ],
+  //   parsed : [ [ 'r1c1', 'r1c2', ..., 'r1cN' ], [ 'r2c1', 'r2c2', ..., 'r2cN' ] ..., [ 'rNc1', 'rNc2', ..., 'rNcN' ] ],
+  //   $cell  : [ [ $r1c1,  $r1c2,  ..., $r1cN  ], [ $r2c1,  $r2c2,  ..., $r2cN  ] ..., [ $rNc1,  $rNc2,  ..., $rNcN  ] ]
+  // }
+
+ If the callback isn't used, the function will return the cummulated data for the column (it does not include all of the parameters available within the callback function): +
var table = $('table')[0],
+  // targeting the 4th column (zero-based index)
+  columnText = $.tablesorter.getColumnText( table, 3 );
+  // columnText = {
+  //   raw    : [ 'cell1', 'cell2', ..., 'celln' ],
+  //   parsed : [ 'cell1', 'cell2', ..., 'celln' ],
+  //   $cell  : [ $cell1,  $cell2,  ..., $celln  ]
+  // }
+
+ When using the filter widget, you can ignore filtered rows using the following code: +
var table = $('table')[0],
+  // targeting the 4th column (zero-based index); callback function is an empty string & therefore ignored
+  columnText = $.tablesorter.getColumnText( table, 3, '', ':not(.filtered)' );
+  // columnText = {
+  //   raw    : [ 'cell1', 'cell2', ..., 'celln' ],
+  //   parsed : [ 'cell1', 'cell2', ..., 'celln' ],
+  //   $cell  : [ $cell1,  $cell2,  ..., $celln  ]
+  // }
+
+ All of these methods of gathering column data might be useful for custom widgets, etc. +

+ * NOTE * All currently available widgets that gather data from a column will be updated to use this function in v2.22.0. +
+
This functions gets the sorter, string, empty, etc options for each column from jQuery data, metadata, header option or header class name ("sorter-false") (v2.1.16). +

+ priority = jQuery data > meta > headers option > header class name
+
+ Use it as follows: +
$.tablesorter.getData(headerCell, configHeaders, key);
+
    +
  • headerCell - table DOM element (or jQuery object) of targeted header cell.
  • +
  • configHeaders - table.config.headers option for the current column.
  • +
  • key - get value for this option name, e.g. "sorter".
  • +
+ If the value of the "sorter" option is needed for a column, set the key to "sorter" and this function will return the set value, e.g. false, digit, currency etc.
+ Use this function as follows: +
var column = 2,
+	config = $('table')[0].config,
+	headerCell = config.$headers.filter('[data-column="' + column + '"]');
+// e.g. returns "false" if the header cell has the "sorter-false" class name
+$.tablesorter.getData( headerCell, config.headers[column], "sorter" );
+ The reason there is a priority is because that is the order in which the values are searched, only the first (higher priority) value is returned. +
+
This function converts a number string into a number type. +

+ Use it as follows: +
$.tablesorter.formatFloat(string, table);
+
    +
  • string - a string possibly containing a number.
  • +
  • table - table DOM element (or jQuery object) of table (optional; required for non U.S. formatting).
  • +
+ If string is empty, not a string type, or not a number (after processing), the string itself is returned.
+ If table is not provided, the format float function will default to U.S. number formatting. The reason a table parameter is needed is to check the value of the usNumberFormat option.
+
+ This function uses the usNumberFormat option to determine if either commas or decimals are removed before converting the value within the string parameter into a number type variable. This function does not use the $.tablesorter.isDigit function.
+
+ Any numbers wrapped within parentheses are converted into negative numbers; but any other symbols (e.g. currency) are not removed and will cause this function to determine the string as a non-number (e.g. "$1.25" will be returned as a string).
+
+ Use this function as follows: +
// result: -2345.67 if usNumberFormat option is true
+$.tablesorter.formatFloat( "(2,345.67)", table );
+
+
This function determines if a string contains a number after removing commas, periods, quotes and spaces. +

+ Use it as follows: +
$.tablesorter.isDigit(string);
+
    +
  • string - a string possibly containing a number.
  • +
+ This function will return a boolean value of true if the string parameter contains a number after all commas, decimals, quotes and spaces are removed, and still allow plus or minus signs, or the number wrapped in parenthesis (negative values).
+
+ Use this function as follows: +
// boolean value of true returned
+$.tablesorter.isDigit( "(2,345.67)" );
+
+
Adds the correct data-column indexing to all rows passed to this function (v2.16; v2.25.0). +
+

In v2.25.0, if a config parameter is included, this function will only add a "data-column" attribute to cells where their internal cellIndex doesn't match its actual column index. This does not apply to internal usage where a "data-column" attribute is set on all header & footer cells.

+ Use it as follows: +
// In v2.25.0, if a config parameter is included "data-columns" are not added
+// to cells where their cellIndex and calculated column index match
+$.tablesorter.computeColumnIndex($rows, config);
+
    +
  • $rows - jQuery object of rows in which to add data-column indexes.
  • +
  • config - this is the table.config (table configuration variables) object.
  • +
+ Example result (without including config):
<tr>
+	<td colspan="2" data-column="0">r0c0</td>
+	<td data-column="2">r0c2</td>
+</tr>
+<tr>
+	<td data-column="0">r1c0</td>
+	<td data-column="1">r1c1</td>
+	<td data-column="2">r1c2</td>
+</tr>
+ Example result (including config):
<tr>
+	<td colspan="2">r0c0</td> <!-- data-column="0" is not included because it matches the cellIndex property -->
+	<td data-column="2">r0c2</td>
+</tr>
+<tr>
+	<td>r1c0</td>
+	<td>r1c1</td>
+	<td>r1c2</td>
+</tr>
+
+
This function allows adding/removing a row to the thead, to display any errors (v2.15; v2.23.1). +
+

This function is ONLY included within the widget-pager.js and jquery.tablesorter.pager.js files; in version 3+, I plan to add it as a selectable option in a build.

+

In v2.23.1, a settings parameter was included to make it match the parametered returned by the jQuery .ajaxError() method.

+

In v2.23.0, this function will accept xhr and exception parameters provided by ajax error messages. To maintain backward compatibility, if xhr is a string, it will be treated as previous message parameter and displayed in the error row without modification.

+ Use it as follows: +
$.tablesorter.showError( table, xhr, settings, exception );
+
    +
  • table - table DOM element (or jQuery object) of the table.
  • +
  • xhr - a plain string, string of an HTML row, or the XMLHttpRequest (XHR) object from ajax error message.
  • +
  • settings - a plain object containing the ajax settings, as returned by the ajaxError method.
  • +
  • exception - exception string passed from the ajax error message.
  • +
+ This function will add a table row to the thead, with a class name from either the pager plugin cssErrorRow option setting, or the pager widget pager_css.errorRow option (the default class name is "tablesorter-errorRow"; and styled within each theme css file). +

When passing this function a message string (in the xhr parameter), there are three possibilities:

+
    +
  1. Plain string (with inline HTML is okay) - "<strong>table refuses to cooperate</strong>"
  2. +
  3. HTML row string - '<tr><td colspan="' + table.config.columns + '">yeah, instead of showing your data... I am taking a nap</td></tr>' (the table.config.columns variable contains the number of table columns; use as needed)
  4. +
  5. undefined - completely leave out a message parameter $.tablesorter.showError( table ); to remove all error rows from the table thead.
  6. +
+ As of v2.23.0, +
    +
  • *WARNING* the table parameter no longer accepts multiple tables.
  • +
  • If the xhr parameter is not a string, then the pager ajaxError callback is called to allow modification of the displayed message.
  • +
+
+
Widget Functions
This filter widget function allows binding external search filters to the table (v2.13.3; v2.15). +

+ Access it as follows: +
$.tablesorter.filter.bindSearch(table, $els, false);
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • $els - jQuery object of all input (search) elements to bind.
  • +
  • false - boolean flag to force a new search.
  • +
+

The table element will only process one table. So if you pass in a jQuery object, only the first table will be bound to the elements ($els).

+ The external elements ($els) will allow searching the table using "search" and "keyup" events (enter to start & escape to cancel the search), and uses the filter_liveSearch option, or delayed search.
+
+ Include a data-column="#" attribute (where # is the column number) in the search input, to specify to which column the search should apply ~ see this demo for a full example. Warning!, if no data-column attribute is added to the input, the input will be ignored.
+
+ In v2.15, use a data-column="all" to bind an external any-match search filter; but note that adding an external any-match filter using this method will not override the filter set by the filter_external option.
+
+ The third function parameter, false, is optional. When set to false it forces the inputs to update their values (same as setting the apply flag when using the setFilters function), and reapplies (forces) the current search to be applied again, even if the filter values have not changed; this allows changing the data column attribute dynamically. See the filter external inputs demo for an example.
+
+ Warning! If the third parameter is set to true the saved internal filters (config.$filters) will be replaced - not recommended! +
+
This filter widget function allows the updating or replacing of filter select options from an external source. (v2.17.5): +

+ Access it as follows: +
$.tablesorter.filter.buildSelect( table, column, options, replace, onlyAvail );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • column - zero-based column index.
  • +
  • options - array of options, jQuery object of option elements, or HTML string of option elements to apply to the targeted select.
  • +
  • replace - a boolean value, which if true all options will be replaced; if false the options will be appended to the current list of options.
  • +
  • onlyAvail - an optional boolean flag, which will be ignored if the options parameter is defined or is an empty string; otherwise it is the value passed to the function which finds all table column values; if true, only the available options will be diplayed.
  • +
+ Use this function as follows:
+
// replace filter select options for the first column with 1,2,3,4
+$.tablesorter.filter.buildSelect( $('table'), 0, [ 1, 2, 3, 4 ], true );
+
+// append "Aaron" and "Fred" to the second column's filter select options. These options will remain unsorted.
+$.tablesorter.filter.buildSelect( $('table'), 1, $('<option>Aaron</option><option>Fred</option>'), false );
+ Please be aware that this function will allow you to modify the select options, but as soon as the user filters any column, all of the select options will refresh and obtain options from the table column, or from the filter_selectSource function. +
+
This filter widget function returns all the cached values of the set table column (v2.16.0): +

+ This function respects the parsed data setting for the column, so it uses the filter_useParsedData option, "filter-parsed" class on the header, or the parser parsed flag settinng (ref). +

Use it as follows:

+
$.tablesorter.filter.getOptions( table, column, onlyAvail );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • column - zero-based column index.
  • +
  • onlyAvail - an optional boolean flag, which will be ignored if the options parameter is defined or is an empty string; otherwise it is the value passed to the function which finds all table column values; if true, only the available options will be diplayed.
  • +
+ Use this function as follows:
+
// external filter select
+var index, len,
+	opts = '',
+	$table = $('table'),
+	column = 0, // first column
+	onlyAvail = false, // if true, available rows (visible rows after filtering) are returned
+	// arry is an array of all text content from a table column; duplicate entries are included!
+	array = $.tablesorter.filter.getOptions( $table, column, onlyAvail );
+
+// process array; sort & remove duplicates (added v2.23.4)
+arry = $.tablesorter.filter.processOptions( $table, column, array );
+len = arry.length;
+
+// build options
+for ( index = 0; index < len; index++ ) {
+	opts += '<option value="' + array[ index ] + '">' + array[ index ] + '</option>';
+}
+$('select.external').html( opts );
+

Please be aware that this function does not process the array, so the array may contain duplicates and will not be sorted.

+ The filter processOptions function was added in v2.23.4 which performs those actions. +
+
This filter widget function returns all the cached values of the set table column (v2.16.0): +

+ This function returns both the text and the parsed text for a column. So it ignores the filter_useParsedData option, "filter-parsed" class on the header, and the parser parsed flag settinng (ref). +

Use it as follows:

+
$.tablesorter.filter.getOptionSource( table, column, onlyAvail );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • column - zero-based column index.
  • +
  • onlyAvail - an optional boolean flag, which will be ignored if the options parameter is defined or is an empty string; otherwise it is the value passed to the function which finds all table column values; if true, only the available options will be diplayed.
  • +
+ Use this function as follows:
+
// external filter select
+var index, len,
+	opts = '',
+	$table = $('table'),
+	column = 0, // first column
+	onlyAvail = false, // if true, available rows (visible rows after filtering) are returned
+	// arry is an array of objects [{ text: "Foo", parsed: "foo" }, ...] with duplicates entries removed
+	arry = $.tablesorter.filter.getOptionSource( $table, column, onlyAvail );
+
+// build options
+for ( index = 0; index < len; index++ ) {
+	opts += '<option value="' + array[ index ].parsed + '">' + array[ index ].text + '</option>';
+}
+$('select.external').html( opts );
+

Please be aware that this function is different from the getOptions filter function in that an object is returned.

+
+
This filter widget function returns an array of sorted column data will duplicates removed (v2.23.4). +

+ Use it as follows: +
$.tablesorter.filter.processOptions( table, column, array );
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • column - zero-based column index (optional).
  • +
  • array - An array of items to process.
  • +
+ The column parameter is optional, but if included it is used: +
    +
  • To check the column header for a filter-select-nosort class name to prevent the sorting of options.
  • +
  • To check the column header for a filter-select-sort-desc class name to preform a descending sort of options.
  • +
  • To check tablesorter's textSorter option in case there are column specific sorting algorhithms set.
  • +
+ See the getOptions function description or the filter_selectSource option (under "An object containing column keys"; getJSON) for examples which use this function. +
+
This filter widget function allows getting an array of the currently applied filters (v2.9; v2.15) +

+ Access it as follows: +
$.tablesorter.getFilters(table, flag);
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • flag - boolean flag (optional; false by default; filter values obtained from the cache), added v2.15;
  • +
+ Use this function as follows:
+
// use $('table') or $('table.hasFilters') to make sure the table has a filter row
+// only required if the stickyHeaders or scroller widget is being used (they duplicate the table)
+$.tablesorter.getFilters( $('table') );
+ This function returns an array of filter values (e.g. [ '', '', '', '', '', '2?%' ]), or false an empty array (v2.27.0) if the selected table does not have a filter row or any associated inputs. It will also return an additional parameter if an external input with data-column="all" containing the value used when matching any table column.
+
+ In v2.27.0, this function will always return an array. +
+ In v2.15, this function will also return values from any external filters (set either by the filter_external option or using the bindSearch function) - with or without an internal filter row.
+
+ If the second parameter is set to true, it forces the function to get all of the current filter values directly from the inputs. Otherwise, by default, the function returns the last search as found in this stored data $('table').data('lastSearch');. +
+
This filter widget function allows setting of the filters; include a true boolean to actually apply the search (v2.9; v2.24.3): +

+ Access it as follows: +
$.tablesorter.setFilters(table, filter, apply);
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • filter - array of filter values to apply to the table.
  • +
  • apply - boolean flag, if false (default setting; default is now true as of v2.24.3), the input values are updated, but the search is not applied to the table.
  • +
+ Use this function as follows:
+
// update filters, but don't apply the search
+$.tablesorter.setFilters( $('table'), [ '', '', '', '', '', '2?%' ], false );
+
+// update filters, AND apply the search (apply is undefined; defaults to true in v2.24.3+)
+$.tablesorter.setFilters( $('table'), [ '', '', '', '', '', '2?%' ] ); // this will now use the search method
+ This function returns true if the filters were sucessfully applied, or false if the table does not have a filter row.
+

In v2.24.3, if the apply parameter is left undefined, it will now default to true.

+ As of v2.15, this function will also set values in any external filters (set either by the filter_external option or using the bindSearch function). An additional search parameter can be included to match any column, but please include an external input with data-column="all" using the bindSearch function so your users will know that a search has been applied.
+
+ Also, changed is that when a true value is passed as a third parameter, the search is forced to refresh on the table; otherwise, if the filters set in the search exactly match the previous search, it would be ignored - this was added to prevent numerous ajax calls with exactly the same search or sort parameters (when using the pager). +
+
This resizable widget function allows resetting column width changes and clearing out the saved set widths (v2.4; v2.10.1) +

+ Use it as follows: +
$.tablesorter.resizableReset(table, refreshing);
+
    +
  • table - table DOM element (or jQuery object) of table(s).
  • +
  • refreshing - if this flag is set to true, the stored resize settings will not be cleared; all other settings will clear the storage.
  • +
+ This function is used when the user right-clicks on the table header (the refreshing flag is undefined at that time to allow saving the changes). +
+
Added a resize event to the table headers - used by the stickyHeaders widget, but this is a public function available to any widget (v2.10). +

+ There is no built-in resize event for non-window elements, so when this function is active it triggers a resize event when the header cell changes size. So, if you are writing your own widget and need a header resize event, just include the jquery.tablesorter.widgets.js file, or just the extract the function from that file.
+
+ Access it as follows: +
$.tablesorter.addHeaderResizeEvent(table, disable, { timer : 250 });
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • disable - boolean flag, if false events for the targeted table are disabled.
  • +
  • timer - currently only the timer option is available for modification.
  • +
+ Enable these triggered events as follows: +
var table = $('table')[0],
+    disable = false,
+    options = {
+      timer : 250 // header cell size is checked every 250 milliseconds (1/4 of a second)
+    };
+$.tablesorter.addHeaderResizeEvent( table, disable, options );
Then use it in your custom widget as follows: +
$(table).on('resize', function(event, columns) {
+  // columns contains an array of header cells that were resized
+  // this seemed like a better idea than firing off a resize event for every
+  // column when the table adjusts itself to fit within its container
+  event.stopPropagation(); // optional
+  // do something
+  console.log( columns );
+});
To disable these resize events, use this code:
// Disable resize event triggering:
+var table = $('table')[0];
+$.tablesorter.addHeaderResizeEvent( table, true );
+
+
This function allows saving specific table data (especially widgets) to local storage (cookie fallback for no browser support) (v2.1) +

+ Access it as follows: +
$.tablesorter.storage(table, key, value, options);
+
    +
  • table - table DOM element (or jQuery object) of table.
  • +
  • key - name of the variable to save
  • +
  • value - value of the variable that is saved (for saving only); set as undefined if wanting to get a value with options.
  • +
  • options - storage options; see below
  • +
+ This function attempts to give every table a unique identifier using both the page url and table id (or index on the page if no id exists), by default. Here is a usage example and a look at what is stored within the local storage: +
$.tablesorter.storage( $('#tablesorter-demo')[0], 'test', '123');
+// stored under the "test" value as {"/tablesorter/docs/":{"tablesorter-demo":"123"}}
+
+
+ The saved table url & id can be overridden by setting table data attributes data-table-page (url) and data-table-group (id), as in this sample markup: +
<table class="tablesorter" data-table-page="mydomain" data-table-group="financial">...</table>)
+ To change the default data attributes, use the options to modify them as follows:
$.tablesorter.storage( table, key, value, {
+  // table id/group id
+  id: 'group1',
+  // this group option sets name of the table attribute with the ID;
+  // but the value within this data attribute is overridden by the above id option
+  group: 'data-table-group',
+
+  // table pages
+  url: 'page1',
+  // this page option sets name of the table attribute with the page/url;
+  // but the value within the data attribute is overridden by the above url option
+  page: 'data-table-page',
+
+  // Option added v2.28.8; use the first letter of (l)ocal, (s)ession or (c)ookie
+  // to set the desired storage type. Any setting of this option will override
+  // any setting in the `useSessionStorage` option.
+  storageType:  'l', // local
+
+  // DEPRECATED in v2.28.8; use the `storageType` option.
+  // Option added v2.21.3; if `true` the storage function will use sessionStorage
+  // if not defined, the `config.widgetOptions.storage_useSessionStorage` setting is checked
+  // if no settings are found, it will default to `false`, and use localStorage
+  // useSessionStorage: false
+
+});
+ The priority of table ID settings is as follows: +
    +
  1. options.id setting
  2. +
  3. Table data attribute ("data-table-group" by default) value
  4. +
  5. widgetOptions.storage_tableId option
  6. +
  7. Table id attribute
  8. +
  9. Index of the table (compared to other tables with a "tablesorter" class name) on the page
  10. +
+ The priority of table url (group) settings is as follows: +
    +
  1. options.url setting
  2. +
  3. Table data attribute ("data-table-page" by default) value
  4. +
  5. widgetOptions.storage_fixedUrl option value
  6. +
  7. config.fixedUrl option value
  8. +
  9. window.location.pathname
  10. +
+
+ The storage_fixedUrl widget option allows you to override the current page url (it doesn't need to be a url, just some constant value) and save data for multiple tables across a domain. The value from this option has a lower priority than the options id or group settings (see priority list above).
+
+ When using the storage utility to get a value and use custom table options, set the value parameter as undefined.
+
+ Lastly, this storage utility function needs the parseJSON function available in jQuery v1.4.1+. +
+
+ + +

Download

+ + File Downloads + + + Required: + + + Optional / Add-Ons: + + + Themes: +

Theme zip files have been removed. There are now numerous themes available which can be seen here.

+ + +

Browser Compatibility

+ +

tablesorter has been tested successfully in the following browsers with Javascript enabled:

+
    +
  • Firefox 2+
  • +
  • Internet Explorer 6+
  • +
  • Safari 2+
  • +
  • Opera 9+
  • +
  • Konqueror
  • +
+ +

jQuery Browser Compatibility

+ + +

Support

+ +

First, please review the FAQ page to see if it will help you resolve the problem.

+ +

If you are having a problem with the plugin or you want to submit a feature request, please submit an issue.

+ +

Support is also available from stackoverflow.

+ +

If you would like to contribute, fork a copy on github.

+ +

Some basic unit testing has been added (v2.6). If you would like to add more or report a problem, please use the appropriate link above.

+ +

For questions about jQuery, try irc, stackoverflow, or the jQuery forums.

+ + +

Credits

+

Written by Christian Bach.

+

+ Documentation written by Brian Ghidinelli, + based on Mike Alsup's great documention. +

+

+ Additional & Missing documentation, alphanumeric sort, numerous widgets, unit testing and other changes added by Mottie. +

+

+ Thanks to all that have contributed code, comments, feedback and everything else. A special thanks goes out to: +

+ +

+ John Resig for the fantastic jQuery +
+ + + + + + + + + + + + + + + + diff --git a/compare/thirdparty/tablesorter/docs/js/bootstrap.min.js b/compare/thirdparty/tablesorter/docs/js/bootstrap.min.js new file mode 100644 index 00000000..e5a24299 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e((t=t||self).bootstrap={},t.jQuery,t.Popper)}(this,function(t,g,u){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)g(this._element).one(Y.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",sanitize:!0,sanitizeFn:null,whiteList:Se,popperConfig:null},Fe="show",Ue="out",We={HIDE:"hide"+Oe,HIDDEN:"hidden"+Oe,SHOW:"show"+Oe,SHOWN:"shown"+Oe,INSERTED:"inserted"+Oe,CLICK:"click"+Oe,FOCUSIN:"focusin"+Oe,FOCUSOUT:"focusout"+Oe,MOUSEENTER:"mouseenter"+Oe,MOUSELEAVE:"mouseleave"+Oe},qe="fade",Me="show",Ke=".tooltip-inner",Qe=".arrow",Be="hover",Ve="focus",Ye="click",ze="manual",Xe=function(){function i(t,e){if("undefined"==typeof u)throw new TypeError("Bootstrap's tooltips require Popper.js (https://popper.js.org/)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=g(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(g(this.getTipElement()).hasClass(Me))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),g.removeData(this.element,this.constructor.DATA_KEY),g(this.element).off(this.constructor.EVENT_KEY),g(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&g(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===g(this.element).css("display"))throw new Error("Please use show on visible elements");var t=g.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){g(this.element).trigger(t);var n=_.findShadowRoot(this.element),i=g.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!i)return;var o=this.getTipElement(),r=_.getUID(this.constructor.NAME);o.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&g(o).addClass(qe);var s="function"==typeof this.config.placement?this.config.placement.call(this,o,this.element):this.config.placement,a=this._getAttachment(s);this.addAttachmentClass(a);var l=this._getContainer();g(o).data(this.constructor.DATA_KEY,this),g.contains(this.element.ownerDocument.documentElement,this.tip)||g(o).appendTo(l),g(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new u(this.element,o,this._getPopperConfig(a)),g(o).addClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().on("mouseover",null,g.noop);var c=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,g(e.element).trigger(e.constructor.Event.SHOWN),t===Ue&&e._leave(null,e)};if(g(this.tip).hasClass(qe)){var h=_.getTransitionDurationFromElement(this.tip);g(this.tip).one(_.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},t.hide=function(t){function e(){n._hoverState!==Fe&&i.parentNode&&i.parentNode.removeChild(i),n._cleanTipClass(),n.element.removeAttribute("aria-describedby"),g(n.element).trigger(n.constructor.Event.HIDDEN),null!==n._popper&&n._popper.destroy(),t&&t()}var n=this,i=this.getTipElement(),o=g.Event(this.constructor.Event.HIDE);if(g(this.element).trigger(o),!o.isDefaultPrevented()){if(g(i).removeClass(Me),"ontouchstart"in document.documentElement&&g(document.body).children().off("mouseover",null,g.noop),this._activeTrigger[Ye]=!1,this._activeTrigger[Ve]=!1,this._activeTrigger[Be]=!1,g(this.tip).hasClass(qe)){var r=_.getTransitionDurationFromElement(i);g(i).one(_.TRANSITION_END,e).emulateTransitionEnd(r)}else e();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){g(this.getTipElement()).addClass(Pe+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(g(t.querySelectorAll(Ke)),this.getTitle()),g(t).removeClass(qe+" "+Me)},t.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=we(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?g(e).parent().is(t)||t.empty().append(e):t.text(g(e).text())},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t=t||("function"==typeof this.config.title?this.config.title.call(this.element):this.config.title)},t._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:Qe},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},{},this.config.popperConfig)},t._getOffset=function(){var e=this,t={};return"function"==typeof this.config.offset?t.fn=function(t){return t.offsets=l({},t.offsets,{},e.config.offset(t.offsets,e.element)||{}),t}:t.offset=this.config.offset,t},t._getContainer=function(){return!1===this.config.container?document.body:_.isElement(this.config.container)?g(this.config.container):g(document).find(this.config.container)},t._getAttachment=function(t){return Re[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)g(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==ze){var e=t===Be?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===Be?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;g(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}}),this._hideModalHandler=function(){i.element&&i.hide()},g(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");!this.element.getAttribute("title")&&"string"==t||(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Ve:Be]=!0),g(e.getTipElement()).hasClass(Me)||e._hoverState===Fe?e._hoverState=Fe:(clearTimeout(e._timeout),e._hoverState=Fe,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===Fe&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||g(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),g(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Ve:Be]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=Ue,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===Ue&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){var e=g(this.element).data();return Object.keys(e).forEach(function(t){-1!==je.indexOf(t)&&delete e[t]}),"number"==typeof(t=l({},this.constructor.Default,{},e,{},"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),_.typeCheckConfig(Ae,t,this.constructor.DefaultType),t.sanitize&&(t.template=we(t.template,t.whiteList,t.sanitizeFn)),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(Le);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(g(t).removeClass(qe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=g(this).data(Ne),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),g(this).data(Ne,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.4.1"}},{key:"Default",get:function(){return xe}},{key:"NAME",get:function(){return Ae}},{key:"DATA_KEY",get:function(){return Ne}},{key:"Event",get:function(){return We}},{key:"EVENT_KEY",get:function(){return Oe}},{key:"DefaultType",get:function(){return He}}]),i}();g.fn[Ae]=Xe._jQueryInterface,g.fn[Ae].Constructor=Xe,g.fn[Ae].noConflict=function(){return g.fn[Ae]=ke,Xe._jQueryInterface};var $e="popover",Ge="bs.popover",Je="."+Ge,Ze=g.fn[$e],tn="bs-popover",en=new RegExp("(^|\\s)"+tn+"\\S+","g"),nn=l({},Xe.Default,{placement:"right",trigger:"click",content:"",template:''}),on=l({},Xe.DefaultType,{content:"(string|element|function)"}),rn="fade",sn="show",an=".popover-header",ln=".popover-body",cn={HIDE:"hide"+Je,HIDDEN:"hidden"+Je,SHOW:"show"+Je,SHOWN:"shown"+Je,INSERTED:"inserted"+Je,CLICK:"click"+Je,FOCUSIN:"focusin"+Je,FOCUSOUT:"focusout"+Je,MOUSEENTER:"mouseenter"+Je,MOUSELEAVE:"mouseleave"+Je},hn=function(t){function i(){return t.apply(this,arguments)||this}!function(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}(i,t);var e=i.prototype;return e.isWithContent=function(){return this.getTitle()||this._getContent()},e.addAttachmentClass=function(t){g(this.getTipElement()).addClass(tn+"-"+t)},e.getTipElement=function(){return this.tip=this.tip||g(this.config.template)[0],this.tip},e.setContent=function(){var t=g(this.getTipElement());this.setElementContent(t.find(an),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(ln),e),t.removeClass(rn+" "+sn)},e._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},e._cleanTipClass=function(){var t=g(this.getTipElement()),e=t.attr("class").match(en);null!==e&&0=this._offsets[o]&&("undefined"==typeof this._offsets[o+1]||tInfo Row', colSpan: 6 } ] }, // row 4 + + // TBODY 3 + { newTbody: true }, + [ 'Bruce', 'Evans', 22, '$13.19', '11%', 'Jan 18, 2007 9:12 AM' ], // row 5 + [ 'Brice', 'Almighty', 45, '$153.19', '44%', 'Jan 18, 2001 9:12 AM' ], // row 6 + + { class: 'specialRow', // row 7 + cells: [ + // each object/string is a cell + { text: 'Fred', class: 'fname' }, + { text: 'Smith', class: 'lname' }, + { text: 18, class: 'age', 'data-info': 'fake ID!, he is really 16' }, + { text: '$22.44', class: 'total' }, + '8%', + { text: 'Aug 20, 2012 10:15 AM', class: 'date' } + ], + 'data-info' : 'This row likes turtles' + } + ] + }; + + $('#object2Table').tablesorter({ + theme: 'blue', + data : dataObject, + widgets: ['zebra'], + widgetOptions : { + // *** build object options *** + build_objectRowKey : 'rows', // object key containing table rows + build_objectCellKey : 'cells', // object key containing table cells (within the rows object) + build_objectHeaderKey : 'headers', // object key containing table headers + build_objectFooterKey : 'footers' // object key containing table footers + } + }); + + // *************************** + // OBJECT (JSON via Ajax) + // *************************** + $('#object2Table2').tablesorter({ + theme: 'blue', + widgets: ['zebra'], + widgetOptions: { + build_type : 'json', + build_source : { url: 'assets/build.json', dataType: 'json' } + } + }); + +}); \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/js/docs.js b/compare/thirdparty/tablesorter/docs/js/docs.js new file mode 100644 index 00000000..f920bba0 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/docs.js @@ -0,0 +1,269 @@ +/*jshint browser:true, jquery:true, unused:false */ +/*global prettyPrint:false */ +(function($) { + $(function() { + var $t, t, v, animating, clicked, + + cleanupCode = function(code) { + return code.replace(/([<>\"\'\t\n]|…)/g, function(m) { return { + '<' : '<', + '>' : '>', + "'" : ''', + '"' : '"', + '\t': ' ', + '\n': '
', // needed for IE + '…' : '&#133;' // pager custom controls ellipsis + }[m];}); + }; + + $('a.external').each(function() {this.target = '_new';}); + + // get javascript source + if ($('#js').length) { + $('#javascript pre').addClass('mod').html( cleanupCode( $('#js').html() ) ); + } + if ($('#js2').length) { + $('#javascript2 pre').addClass('mod').html( cleanupCode( $('#js2').html() ) ); + } + if ($('#css').length) { + $('pre.lang-css').not('.locked').addClass('mod').html( cleanupCode( $('#css').html() ) ); + } + if ($('#demo').length && $('#html pre').length) { + $('#html pre').addClass('mod').html( cleanupCode( $('#demo').html() ) ); + } + + // apply to already pre-formatted blocks to add
for IE + $('pre.prettyprint:not(.mod)').each(function() { + $t = $(this); + $t.html( cleanupCode( $t.html() ) ); + }); + + // apply prettyprint in several settimeouts + window['PR_SHOULD_USE_CONTINUATION'] = true; + if (typeof prettyPrint !== 'undefined') { prettyPrint(); } + + // hide child rows + $('#root .tablesorter-childRow').hide(); + // toggle child row content, not hiding the row since we are using rowspan + $('#root .toggle').click(function() { + $(this).closest('tr').nextUntil('tr:not(.tablesorter-childRow)').toggle(); + return false; + }); + + animating = false; + clicked = false; + $('.collapsible').hide(); + + $('a.permalink').click(function() { + var $el = $(this); + setTimeout(function() { + if (!animating && !clicked) { + animating = true; + $el.closest('tr').find('.collapsible').slideToggle(); + setTimeout(function() { animating = false; }, 200); + } + }, 200); + return false; + }); + $('#root .permalink').dblclick(function() { + clicked = true; + window.location.hash = '#' + $(this).closest('tr')[0].id; + showProperty(); + setTimeout(function() { clicked = false; }, 500); + return false; + }); + + $('.toggleAll, .showAll, .hideAll').click(function() { + t = $.trim($(this).text()); + // use nextAll to ignore any
or other elements between this link and the table + $(this).parent().nextAll('table:first').find('.collapsible')[t](); + return false; + }); + + // update version number + $t = $('.current-version'); + if ($t.length) { + $t.html($.tablesorter.version); + } + + // add high visibility tags for newest versions + t = $.tablesorter.version.replace(/(v|version|\+)/g, '').split('.'); + v = [ parseInt(t[0], 10) || 1, parseInt(t[1], 10) || 0, parseInt(t[2], 10) || 0 ]; + $('.version').each(function() { + var i; + $t = $(this); + i = $t.text().replace(/(v|version|\+)/g, '').split('.'); + t = [ parseInt(i[0], 10) || 1, parseInt(i[1], 10) || 0, parseInt(i[2], 10) || 0 ]; + if (t[0] === v[0] && t[1] >= v[1] - 1 ) { + $t.prepend(''+ ($t.hasClass('updated') ? 'Updated' : 'New') + ' '); + // updates for current version are brighter + if (t[2] <= v[2] - 1) { + // new labels for revisions > 1 back are lighter + $t.addClass('older'); + } + } + }); + + $t = $('.accordion'); + if ($t.length) { + var hashId, + hash = window.location.hash; + // add accodion ids + $t.each(function() { + $(this).children('h3').each(function(i) { + var txt = $(this).find('a').text().toLowerCase().replace(/[-:()\"]/g,'').replace(/[\s+\/]/g,'_'); + this.id = txt; + if (hash && txt === hash.slice(1)) { + hashId = i; + } + }); + }); + // set up accordions + $t.each(function() { + var $this = $(this); + $this.accordion({ + active: $this.hasClass('start-closed') ? false : hashId, + animate: false, + heightStyle: 'content', + collapsible: true, + create: function() { + $this.children('h3').each(function(i) { + this.id = $(this).find('a').text().toLowerCase().replace(/[-:()\"]/g,'').replace(/[\s+\/]/g,'_'); + $(this).before(''); + }); + $this.find('.accordion-link').click(function() { + $this.accordion( 'option', 'active', $(this).data('index') ); + }); + }, + activate: function(e, ui) { + // refresh zebra widget when rows are visible + ui.newPanel.find('table').trigger('applyWidgets'); + var $opt = ui.newPanel.find('table.options'); + // options tables are hidden, so colgroup won't find any visible columns to get widths + if ( $opt.length && $opt[0].config ) { + $.tablesorter.fixColumnWidth( $opt ); + } + } + }); + openAccordion( hash, $this ); + }); + + $t.find('table.options').not('.tablesorter-jui').tablesorter({ + widthFixed: true, + widgets: ['stickyHeaders'] + }); + + $('.intlink').click(function() { + openAccordion( $(this).attr('href') ); + }); + + } + + }); + + function openAccordion( hash, $accordion ) { + // hash is not a jQuery selector + if ( /[=,]/.test(hash) ) { + return false; + } + var t, id, $hash; + if ( !$accordion ) { + $accordion = $(hash).closest('.accordion'); + } + $hash = $accordion.find(hash); + + if ( $hash.length ) { + if ( $hash.hasClass('ui-accordion-header') && !$hash.hasClass('ui-accordion-header-active') ) { + $hash.click(); + + // open parent accordion of nested accordion + } else if ( !$accordion.children(hash).length ) { + // div should have an id of ui-accordion-#-panel-# + id = $(hash).closest('.ui-accordion-content').attr('id').match(/\d+$/); + if (id && id.length) { + $accordion.accordion('option', 'active', Number(id[0]) - 1); + } + } + + // open table row of nested accordion + if ( ($accordion.find(hash).closest('tr').attr('id') || '') !== '') { + t = $accordion.find(hash).closest('tr'); + t.find('.collapsible').show(); + if (t.closest('table').hasClass('hasStickyHeaders')) { + setTimeout(function() { + window.scrollTo( 0, t.offset().top - t.parents('table')[0].config.widgetOptions.$sticky.outerHeight() ); + }, 200); + } + } + + } + } + + function showProperty() { + var prop, $t, wo, stickyHt, + h = window.location.hash; + if (h && !/[=,]/.test(h)) { + prop = $(h); + if (prop.length && !/h3|a|table/i.test(prop[0].nodeName)) { + prop.find('.collapsible').show(); + if (h === '#csschildrow') { + $('#root .tablesorter-childRow').show(); + } + // move below sticky header; added delay as there could be some lag + setTimeout(function() { + $t = prop.closest('table'); + if ($t.length && $t[0].config) { + wo = $t[0].config.widgetOptions; + if ( wo.$sticky ) { + stickyHt = wo.$sticky.outerHeight(); + h = ( wo.$sticky ? wo.$sticky.height() : '' ) || $t.hasClass('hasStickHeaders') ? stickyHt : 0; + if ($t.hasClass('options') || $t.hasClass('api')) { + window.scrollTo( 0, prop.offset().top - h ); + } + } + } + }, 200); + } + } + } + + // update stickyHeader when menu closes + $('#main-nav-check').bind('change', function() { + setTimeout(function() { + $(window).scroll(); + }, 350); // transition animation 300ms + }); + + $(window).bind('load', function() { + if ($('#root').length) { + $(window).bind('hashchange', function() { + showProperty(); + }); + showProperty(); + } + }); + + // append hidden parsed value to cell + // used by feet-inch-fraction & metric parser demos + window.addParsedValues = function($t, cols, format) { + var r, val, + $r = $t.find('tbody tr'), + c = $t[0].config.cache[0].normalized; + $r.each(function(i) { + r = this; + $.each(cols, function(v,j) { + val = format ? format(c[i][j]) : c[i][j]; + if (val !== '') { + r.cells[j].innerHTML += ' '; + } + }); + }); + + $('.toggleparsedvalue').bind('click', function() { + $('.val').toggleClass('hidden'); + return false; + }); + }; + +})(jQuery); diff --git a/compare/thirdparty/tablesorter/docs/js/jquery-1.2.6.min.js b/compare/thirdparty/tablesorter/docs/js/jquery-1.2.6.min.js new file mode 100644 index 00000000..6289c994 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/jquery-1.2.6.min.js @@ -0,0 +1,32 @@ +/* + * jQuery 1.2.6 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008/05/26 $ + * $Rev: 5685 $ + */ +(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else +return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else +return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else +selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else +this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else +return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else +jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else +jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("",""]||!tags.indexOf("",""]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!tags.indexOf("",""]||(!tags.indexOf("",""]||!tags.indexOf("",""]||jQuery.browser.msie&&[1,"div
","
"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf(""&&tags.indexOf("=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else +ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&¬xml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&¬xml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&¬xml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else +while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return im[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else +for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("
").append(res.responseText.replace(//g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else +xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else +jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else +for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else +s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else +e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;ithis.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})(); \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/js/jquery-1.4.4.min.js b/compare/thirdparty/tablesorter/docs/js/jquery-1.4.4.min.js new file mode 100644 index 00000000..8f3ca2e2 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/jquery-1.4.4.min.js @@ -0,0 +1,167 @@ +/*! + * jQuery JavaScript Library v1.4.4 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Nov 11 19:04:53 2010 -0500 + */ +(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h= +h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La, +"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this, +e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a, +"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+ +a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/, +C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j, +s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this, +j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length}, +toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j=== +-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false; +if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload", +b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&& +!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&& +l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), +k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, +scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= +false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= +1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
t
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= +"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= +c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); +else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; +if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, +attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& +b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; +c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, +arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= +d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ +c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== +8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== +"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ +d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= +B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== +0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; +break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, +q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= +l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, +m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- +0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; +if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, +g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); +n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& +function(){var g=k,i=t.createElement("div");i.innerHTML="

";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| +p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= +t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? +function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= +h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): +c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, +2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, +b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& +e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, +""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; +else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", +prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/compare/thirdparty/tablesorter/docs/js/jquery-2.2.4.min.js b/compare/thirdparty/tablesorter/docs/js/jquery-2.2.4.min.js new file mode 100644 index 00000000..4024b662 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/jquery-2.2.4.min.js @@ -0,0 +1,4 @@ +/*! jQuery v2.2.4 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="2.2.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isPlainObject:function(a){var b;if("object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype||{},"isPrototypeOf"))return!1;for(b in a);return void 0===b||k.call(a,b)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=d.createElement("script"),b.text=a,d.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:h.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(d=e.call(arguments,2),f=function(){return a.apply(b||this,d.concat(e.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return h.call(b,a)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&f.parentNode&&(this.length=1,this[0]=f),this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?void 0!==c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?h.call(n(a),this[0]):h.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||n.uniqueSort(e),D.test(a)&&e.reverse()),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.removeEventListener("DOMContentLoaded",J),a.removeEventListener("load",J),n.ready()}n.ready.promise=function(b){return I||(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(n.ready):(d.addEventListener("DOMContentLoaded",J),a.addEventListener("load",J))),I.promise(b)},n.ready.promise();var K=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)K(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},L=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function M(){this.expando=n.expando+M.uid++}M.uid=1,M.prototype={register:function(a,b){var c=b||{};return a.nodeType?a[this.expando]=c:Object.defineProperty(a,this.expando,{value:c,writable:!0,configurable:!0}),a[this.expando]},cache:function(a){if(!L(a))return{};var b=a[this.expando];return b||(b={},L(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[b]=c;else for(d in b)e[d]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=a[this.expando];if(void 0!==f){if(void 0===b)this.register(a);else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in f?d=[b,e]:(d=e,d=d in f?[d]:d.match(G)||[])),c=d.length;while(c--)delete f[d[c]]}(void 0===b||n.isEmptyObject(f))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!n.isEmptyObject(b)}};var N=new M,O=new M,P=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Q=/[A-Z]/g;function R(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Q,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:P.test(c)?n.parseJSON(c):c; +}catch(e){}O.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return O.hasData(a)||N.hasData(a)},data:function(a,b,c){return O.access(a,b,c)},removeData:function(a,b){O.remove(a,b)},_data:function(a,b,c){return N.access(a,b,c)},_removeData:function(a,b){N.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=O.get(f),1===f.nodeType&&!N.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),R(f,d,e[d])));N.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){O.set(this,a)}):K(this,function(b){var c,d;if(f&&void 0===b){if(c=O.get(f,a)||O.get(f,a.replace(Q,"-$&").toLowerCase()),void 0!==c)return c;if(d=n.camelCase(a),c=O.get(f,d),void 0!==c)return c;if(c=R(f,d,void 0),void 0!==c)return c}else d=n.camelCase(a),this.each(function(){var c=O.get(this,d);O.set(this,d,b),a.indexOf("-")>-1&&void 0!==c&&O.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){O.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=N.get(a,b),c&&(!d||n.isArray(c)?d=N.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return N.get(a,c)||N.access(a,c,{empty:n.Callbacks("once memory").add(function(){N.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length",""],thead:[1,"
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};$.optgroup=$.option,$.tbody=$.tfoot=$.colgroup=$.caption=$.thead,$.th=$.td;function _(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function aa(a,b){for(var c=0,d=a.length;d>c;c++)N.set(a[c],"globalEval",!b||N.get(b[c],"globalEval"))}var ba=/<|&#?\w+;/;function ca(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],o=0,p=a.length;p>o;o++)if(f=a[o],f||0===f)if("object"===n.type(f))n.merge(m,f.nodeType?[f]:f);else if(ba.test(f)){g=g||l.appendChild(b.createElement("div")),h=(Y.exec(f)||["",""])[1].toLowerCase(),i=$[h]||$._default,g.innerHTML=i[1]+n.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;n.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",o=0;while(f=m[o++])if(d&&n.inArray(f,d)>-1)e&&e.push(f);else if(j=n.contains(f.ownerDocument,f),g=_(l.appendChild(f),"script"),j&&aa(g),c){k=0;while(f=g[k++])Z.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),l.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",l.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var da=/^key/,ea=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,fa=/^([^.]*)(?:\.(.+)|)/;function ga(){return!0}function ha(){return!1}function ia(){try{return d.activeElement}catch(a){}}function ja(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ja(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ha;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return"undefined"!=typeof n&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(G)||[""],j=b.length;while(j--)h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=N.hasData(a)&&N.get(a);if(r&&(i=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=fa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&N.remove(a,"handle events")}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(N.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,la=/\s*$/g;function pa(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function qa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function ra(a){var b=na.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function sa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(N.hasData(a)&&(f=N.access(a),g=N.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}O.hasData(a)&&(h=O.access(a),i=n.extend({},h),O.set(b,i))}}function ta(a,b){var c=b.nodeName.toLowerCase();"input"===c&&X.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function ua(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&ma.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),ua(f,b,c,d)});if(o&&(e=ca(b,a[0].ownerDocument,!1,a,d),g=e.firstChild,1===e.childNodes.length&&(e=g),g||d)){for(h=n.map(_(e,"script"),qa),i=h.length;o>m;m++)j=e,m!==p&&(j=n.clone(j,!0,!0),i&&n.merge(h,_(j,"script"))),c.call(a[m],j,m);if(i)for(k=h[h.length-1].ownerDocument,n.map(h,ra),m=0;i>m;m++)j=h[m],Z.test(j.type||"")&&!N.access(j,"globalEval")&&n.contains(k,j)&&(j.src?n._evalUrl&&n._evalUrl(j.src):n.globalEval(j.textContent.replace(oa,"")))}return a}function va(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(_(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&aa(_(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(ka,"<$1>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=_(h),f=_(a),d=0,e=f.length;e>d;d++)ta(f[d],g[d]);if(b)if(c)for(f=f||_(a),g=g||_(h),d=0,e=f.length;e>d;d++)sa(f[d],g[d]);else sa(a,h);return g=_(h,"script"),g.length>0&&aa(g,!i&&_(a,"script")),h},cleanData:function(a){for(var b,c,d,e=n.event.special,f=0;void 0!==(c=a[f]);f++)if(L(c)){if(b=c[N.expando]){if(b.events)for(d in b.events)e[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);c[N.expando]=void 0}c[O.expando]&&(c[O.expando]=void 0)}}}),n.fn.extend({domManip:ua,detach:function(a){return va(this,a,!0)},remove:function(a){return va(this,a)},text:function(a){return K(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.appendChild(a)}})},prepend:function(){return ua(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=pa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return ua(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(_(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return K(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!la.test(a)&&!$[(Y.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(_(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return ua(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(_(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),f=e.length-1,h=0;f>=h;h++)c=h===f?this:this.clone(!0),n(e[h])[b](c),g.apply(d,c.get());return this.pushStack(d)}});var wa,xa={HTML:"block",BODY:"block"};function ya(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function za(a){var b=d,c=xa[a];return c||(c=ya(a,b),"none"!==c&&c||(wa=(wa||n("
',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){t.types.push(Z),x("BeforeChange",function(e,t,n){t!==n&&(t===Z?D():n===Z&&D(!0))}),x(l+"."+Z,function(){D()})},getIframe:function(n,i){var o=n.src,r=t.st.iframe;e.each(r.patterns,function(){return o.indexOf(this.index)>-1?(this.id&&(o="string"==typeof this.id?o.substr(o.lastIndexOf(this.id)+this.id.length,o.length):this.id.call(this,o)),o=this.src.replace("%id%",o),!1):void 0});var a={};return r.srcAction&&(a[r.srcAction]=o),t._parseMarkup(i,a,n),t.updateStatus("ready"),i}}});var K=function(e){var n=t.items.length;return e>n-1?e-n:0>e?n+e:e},Y=function(e,t,n){return e.replace(/%curr%/gi,t+1).replace(/%total%/gi,n)};e.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var n=t.st.gallery,i=".mfp-gallery",r=Boolean(e.fn.mfpFastClick);return t.direction=!0,n&&n.enabled?(a+=" mfp-gallery",x(f+i,function(){n.navigateByImgClick&&t.wrap.on("click"+i,".mfp-img",function(){return t.items.length>1?(t.next(),!1):void 0}),o.on("keydown"+i,function(e){37===e.keyCode?t.prev():39===e.keyCode&&t.next()})}),x("UpdateStatus"+i,function(e,n){n.text&&(n.text=Y(n.text,t.currItem.index,t.items.length))}),x(p+i,function(e,i,o,r){var a=t.items.length;o.counter=a>1?Y(n.tCounter,r.index,a):""}),x("BuildControls"+i,function(){if(t.items.length>1&&n.arrows&&!t.arrowLeft){var i=n.arrowMarkup,o=t.arrowLeft=e(i.replace(/%title%/gi,n.tPrev).replace(/%dir%/gi,"left")).addClass(y),a=t.arrowRight=e(i.replace(/%title%/gi,n.tNext).replace(/%dir%/gi,"right")).addClass(y),s=r?"mfpFastClick":"click";o[s](function(){t.prev()}),a[s](function(){t.next()}),t.isIE7&&(k("b",o[0],!1,!0),k("a",o[0],!1,!0),k("b",a[0],!1,!0),k("a",a[0],!1,!0)),t.container.append(o.add(a))}}),x(m+i,function(){t._preloadTimeout&&clearTimeout(t._preloadTimeout),t._preloadTimeout=setTimeout(function(){t.preloadNearbyImages(),t._preloadTimeout=null},16)}),x(l+i,function(){o.off(i),t.wrap.off("click"+i),t.arrowLeft&&r&&t.arrowLeft.add(t.arrowRight).destroyMfpFastClick(),t.arrowRight=t.arrowLeft=null}),void 0):!1},next:function(){t.direction=!0,t.index=K(t.index+1),t.updateItemHTML()},prev:function(){t.direction=!1,t.index=K(t.index-1),t.updateItemHTML()},goTo:function(e){t.direction=e>=t.index,t.index=e,t.updateItemHTML()},preloadNearbyImages:function(){var e,n=t.st.gallery.preload,i=Math.min(n[0],t.items.length),o=Math.min(n[1],t.items.length);for(e=1;(t.direction?o:i)>=e;e++)t._preloadItem(t.index+e);for(e=1;(t.direction?i:o)>=e;e++)t._preloadItem(t.index-e)},_preloadItem:function(n){if(n=K(n),!t.items[n].preloaded){var i=t.items[n];i.parsed||(i=t.parseEl(n)),T("LazyLoad",i),"image"===i.type&&(i.img=e('').on("load.mfploader",function(){i.hasSize=!0}).on("error.mfploader",function(){i.hasSize=!0,i.loadError=!0,T("LazyLoadError",i)}).attr("src",i.src)),i.preloaded=!0}}}});var U="retina";e.magnificPopup.registerModule(U,{options:{replaceSrc:function(e){return e.src.replace(/\.\w+$/,function(e){return"@2x"+e})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var e=t.st.retina,n=e.ratio;n=isNaN(n)?n():n,n>1&&(x("ImageHasSize."+U,function(e,t){t.img.css({"max-width":t.img[0].naturalWidth/n,width:"100%"})}),x("ElementParse."+U,function(t,i){i.src=e.replaceSrc(i,n)}))}}}}),function(){var t=1e3,n="ontouchstart"in window,i=function(){I.off("touchmove"+r+" touchend"+r)},o="mfpFastClick",r="."+o;e.fn.mfpFastClick=function(o){return e(this).each(function(){var a,s=e(this);if(n){var l,c,d,u,p,f;s.on("touchstart"+r,function(e){u=!1,f=1,p=e.originalEvent?e.originalEvent.touches[0]:e.touches[0],c=p.clientX,d=p.clientY,I.on("touchmove"+r,function(e){p=e.originalEvent?e.originalEvent.touches:e.touches,f=p.length,p=p[0],(Math.abs(p.clientX-c)>10||Math.abs(p.clientY-d)>10)&&(u=!0,i())}).on("touchend"+r,function(e){i(),u||f>1||(a=!0,e.preventDefault(),clearTimeout(l),l=setTimeout(function(){a=!1},t),o())})})}s.on("click"+r,function(){a||o()})})},e.fn.destroyMfpFastClick=function(){e(this).off("touchstart"+r+" click"+r),n&&I.off("touchmove"+r+" touchend"+r)}}(),_()})(window.jQuery||window.Zepto); diff --git a/compare/thirdparty/tablesorter/docs/js/jquery.tipsy.min.js b/compare/thirdparty/tablesorter/docs/js/jquery.tipsy.min.js new file mode 100644 index 00000000..9b730b6a --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/jquery.tipsy.min.js @@ -0,0 +1,7 @@ +/* tipsy, facebook style tooltips for jquery + * version 1.0.0a + * (c) 2008-2010 jason frame [jason@onehackoranother.com] + * released under the MIT license + * Modified to use themes: https://github.com/Mottie/tipsy + */ +(function(b){function l(a,c){this.$element=b(a);this.options=c;this.enabled=!0;this.fixTitle()}l.prototype={show:function(){var a=this.getTitle();if(a&&this.enabled){var c=this.tip(),d=this.options.theme[0]||"black",f=this.options.theme[1]||"white",g;c.find(".tipsy-inner").css({background:d,color:f})[this.options.html?"html":"text"](a);c[0].className="tipsy";c.remove().css({top:0,left:0,visibility:"hidden",display:"block"}).prependTo(document.body);var a=b.extend({},this.$element.offset(),{width:this.$element[0].offsetWidth, height:this.$element[0].offsetHeight}),f=c[0].offsetWidth,h=c[0].offsetHeight,e="function"==typeof this.options.gravity?this.options.gravity.call(this.$element[0]):this.options.gravity,k;switch(e.charAt(0)){case "n":k={top:a.top+a.height+this.options.offset,left:a.left+a.width/2-f/2};g={"border-bottom-color":d};break;case "s":k={top:a.top-h-this.options.offset,left:a.left+a.width/2-f/2};g={"border-top-color":d};break;case "e":k={top:a.top+a.height/2-h/2,left:a.left-f-this.options.offset};g={"border-left-color":d}; break;case "w":k={top:a.top+a.height/2-h/2,left:a.left+a.width+this.options.offset},g={"border-right-color":d}}2==e.length&&("w"==e.charAt(1)?k.left=a.left+a.width/2-15:k.left=a.left+a.width/2-f+15);c.css(k).addClass("tipsy-"+e);c.find(".tipsy-arrow").css(g)[0].className="tipsy-arrow tipsy-arrow-"+e.charAt(0);this.options.className&&c.addClass("function"==typeof this.options.className?this.options.className.call(this.$element[0]):this.options.className);this.options.fade?c.stop().css({opacity:0,display:"block", visibility:"visible"}).animate({opacity:this.options.opacity}):c.css({visibility:"visible",opacity:this.options.opacity})}},hide:function(){this.options.fade?this.tip().stop().fadeOut(function(){b(this).remove()}):this.tip().remove()},fixTitle:function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("original-title"))&&a.attr("original-title",a.attr("title")||"").removeAttr("title")},getTitle:function(){var a,b=this.$element,d=this.options;this.fixTitle();d=this.options;"string"== typeof d.title?a=b.attr("title"==d.title?"original-title":d.title):"function"==typeof d.title&&(a=d.title.call(b[0]));return(a=(""+a).replace(/(^\s*|\s*$)/,""))||d.fallback},tip:function(){this.$tip||(this.$tip=b('
').html('
'));return this.$tip},validate:function(){this.$element[0].parentNode||(this.hide(),this.options=this.$element=null)},enable:function(){this.enabled=!0},disable:function(){this.enabled=!1},toggleEnabled:function(){this.enabled= !this.enabled}};b.fn.tipsy=function(a){function c(e){var c=b.data(e,"tipsy");c||(c=new l(e,b.fn.tipsy.elementOptions(e,a)),b.data(e,"tipsy",c));return c}function d(){var b=c(this);b.hoverState="in";0==a.delayIn?b.show():(b.fixTitle(),setTimeout(function(){"in"==b.hoverState&&b.show()},a.delayIn))}function f(){var b=c(this);b.hoverState="out";0==a.delayOut?b.hide():setTimeout(function(){"out"==b.hoverState&&b.hide()},a.delayOut)}if(!0===a)return this.data("tipsy");if("string"==typeof a){var g=this.data("tipsy"); if(g)g[a]();return this}a=b.extend({},b.fn.tipsy.defaults,a);a.live||this.each(function(){c(this)});if("manual"!=a.trigger){var g=a.live?"live":"bind",h="hover"==a.trigger?"mouseleave":"blur";this[g]("hover"==a.trigger?"mouseenter":"focus",d)[g](h,f)}return this};b.fn.tipsy.defaults={className:null,delayIn:0,delayOut:0,fade:!1,fallback:"",gravity:"n",html:!1,live:!1,offset:0,opacity:0.8,title:"title",theme:["black","white"],trigger:"hover"};b.fn.tipsy.elementOptions=function(a,c){return b.metadata? b.extend({},c,b(a).metadata()):c};b.fn.tipsy.autoNS=function(){return b(this).offset().top>b(document).scrollTop()+b(window).height()/2?"s":"n"};b.fn.tipsy.autoWE=function(){return b(this).offset().left>b(document).scrollLeft()+b(window).width()/2?"e":"w"};b.fn.tipsy.autoBounds=function(a,c){return function(){var d=c[0],f=1122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}ph;h++)e[h]=c[h].style.display,c[h].style.display="block",c[h].style.height="0",c[h].style.overflow="hidden",c[h].style.visibility="hidden";g=a[b];for(var i=0;d>i;i++)c[i].style.display=e[i],c[i].style.height="",c[i].style.overflow="",c[i].style.visibility=""}return g}function h(b,e){if(this.$window=a(window),this.$document=a(document),this.$element=a(b),this.options=a.extend({},m,e),this._defaults=m,this._name=i,this.startEvent=this.options.startEvent.join("."+i+" ")+"."+i,this.moveEvent=this.options.moveEvent.join("."+i+" ")+"."+i,this.endEvent=this.options.endEvent.join("."+i+" ")+"."+i,this.polyfill=this.options.polyfill,this.onInit=this.options.onInit,this.onSlide=this.options.onSlide,this.onSlideEnd=this.options.onSlideEnd,this.polyfill&&l)return!1;this.identifier="js-"+i+"-"+k++,this.min=parseFloat(this.$element[0].getAttribute("min")||0),this.max=parseFloat(this.$element[0].getAttribute("max")||100),this.value=parseFloat(this.$element[0].value||this.min+(this.max-this.min)/2),this.step=parseFloat(this.$element[0].getAttribute("step")||1),this.$fill=a('
'),this.$handle=a('
'),this.$range=a('
').insertAfter(this.$element).prepend(this.$fill,this.$handle),this.$element.css({position:"absolute",width:"1px",height:"1px",overflow:"hidden",opacity:"0"}),this.handleDown=a.proxy(this.handleDown,this),this.handleMove=a.proxy(this.handleMove,this),this.handleEnd=a.proxy(this.handleEnd,this),this.init();var f=this;this.$window.on("resize."+i,d(function(){c(function(){f.update()},300)},20)),this.$document.on(this.startEvent,"#"+this.identifier+":not(."+this.options.disabledClass+")",this.handleDown),this.$element.on("change."+i,function(a,b){if(!b||b.origin!==i){var c=a.target.value,d=f.getPositionFromValue(c);f.setPosition(d)}})}var i="rangeslider",j=[],k=0,l=b(),m={polyfill:!0,rangeClass:"rangeslider",disabledClass:"rangeslider--disabled",fillClass:"rangeslider__fill",handleClass:"rangeslider__handle",startEvent:["mousedown","touchstart","pointerdown"],moveEvent:["mousemove","touchmove","pointermove"],endEvent:["mouseup","touchend","pointerup"]};h.prototype.init=function(){this.onInit&&"function"==typeof this.onInit&&this.onInit(),this.update()},h.prototype.update=function(){this.handleWidth=g(this.$handle[0],"offsetWidth"),this.rangeWidth=g(this.$range[0],"offsetWidth"),this.maxHandleX=this.rangeWidth-this.handleWidth,this.grabX=this.handleWidth/2,this.position=this.getPositionFromValue(this.value),this.$element[0].disabled?this.$range.addClass(this.options.disabledClass):this.$range.removeClass(this.options.disabledClass),this.setPosition(this.position)},h.prototype.handleDown=function(a){if(a.preventDefault(),this.$document.on(this.moveEvent,this.handleMove),this.$document.on(this.endEvent,this.handleEnd),!((" "+a.target.className+" ").replace(/[\n\t]/g," ").indexOf(this.options.handleClass)>-1)){var b=this.getRelativePosition(a),c=this.$range[0].getBoundingClientRect().left,d=this.getPositionFromNode(this.$handle[0])-c;this.setPosition(b-this.grabX),b>=d&&ba?b:a>c?c:a},h.prototype.setPosition=function(a){var b,c;b=this.getValueFromPosition(this.cap(a,0,this.maxHandleX))/this.step*this.step,c=this.getPositionFromValue(b),this.$fill[0].style.width=c+this.grabX+"px",this.$handle[0].style.left=c+"px",this.setValue(b),this.position=c,this.value=b,this.onSlide&&"function"==typeof this.onSlide&&this.onSlide(c,b)},h.prototype.getPositionFromNode=function(a){for(var b=0;null!==a;)b+=a.offsetLeft,a=a.offsetParent;return b},h.prototype.getRelativePosition=function(a){var b=this.$range[0].getBoundingClientRect().left,c=0;return"undefined"!=typeof a.pageX?c=a.pageX:"undefined"!=typeof a.originalEvent.clientX?c=a.originalEvent.clientX:a.originalEvent.touches&&a.originalEvent.touches[0]&&"undefined"!=typeof a.originalEvent.touches[0].clientX?c=a.originalEvent.touches[0].clientX:a.currentPoint&&"undefined"!=typeof a.currentPoint.x&&(c=a.currentPoint.x),c-b},h.prototype.getPositionFromValue=function(a){var b,c;return b=(a-this.min)/(this.max-this.min),c=b*this.maxHandleX},h.prototype.getValueFromPosition=function(a){var b,c;return b=a/(this.maxHandleX||1),c=this.step*Math.round(b*(this.max-this.min)/this.step)+this.min,Number(c.toFixed(2))},h.prototype.setValue=function(a){a!==this.value&&this.$element.val(a).trigger("change",{origin:i})},h.prototype.destroy=function(){this.$document.off(this.startEvent,"#"+this.identifier,this.handleDown),this.$element.off("."+i).removeAttr("style").removeData("plugin_"+i),this.$range&&this.$range.length&&this.$range[0].parentNode.removeChild(this.$range[0]),j.splice(j.indexOf(this.$element[0]),1),j.length||this.$window.off("."+i)},a.fn[i]=function(b){return this.each(function(){var c=a(this),d=c.data("plugin_"+i);d||(c.data("plugin_"+i,d=new h(this,b)),j.push(this)),"string"==typeof b&&d[b]()})}}); \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/js/search-ie.js b/compare/thirdparty/tablesorter/docs/js/search-ie.js new file mode 100644 index 00000000..c048382f --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/search-ie.js @@ -0,0 +1,28 @@ +/* IE 8 and less */ +$(function(){ + // open side menu + var checked = false; + $('.main-header').click(function(){ + checked = !checked; + $('#main-nav').animate({ width: checked ? 250 : 0 }, 300); + $('#main') + .animate({ marginLeft: checked ? 250 : 0 }, 300) + .css({ overflow: checked ? 'hidden' : '' }) + .find('.open-menu').toggle(!checked).end() + .find('.close-menu').toggle(checked).end() + .find('#banner h1').css({ paddingLeft : checked ? 0 : 30 }); + }); + + // make sure the case insensitive button is checked (visually) + $('#csfalse').addClass('switch-selection-right'); + + // search option buttons + $('.switch-label').click(function(){ + var tar = $(this).attr('for'); + $('#' + tar) + .prop('checked', true) + .change() + .siblings('.switch-selection') + .toggleClass( 'switch-selection-right', (tar === 'letter' || tar === 'csfalse') ); + }); +}); \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/js/search.js b/compare/thirdparty/tablesorter/docs/js/search.js new file mode 100644 index 00000000..84d60165 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/search.js @@ -0,0 +1,171 @@ +/* jQuery Highlight plugin + * Based on highlight v3 by Johann Burkard + * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html + * Copyright (c) 2009 Bartek Szopka + * Licensed under MIT license. + * Modified to require a minimum number of characters before searching + *//* eslint-disable */ +;jQuery.extend({highlight:function(a,c,b,d,f) {if(3===a.nodeType) {if(c=a.data.match(c))return b=document.createElement(b||"span"),b.className=d||"highlight",a=a.splitText(c.index),a.splitText(c[0].length),d=a.cloneNode(!0),b.appendChild(d),a.parentNode.replaceChild(b,a),1}else if(1===a.nodeType&&a.childNodes&&!/(script|style)/i.test(a.tagName)&&1>$(a).closest(f).length&&(a.tagName!==b.toUpperCase()||a.className!==d))for(var e=0;e
').html('
'));return this.$tip},validate:function() {this.$element[0].parentNode||(this.hide(),this.options=this.$element=null)},enable:function() {this.enabled=!0},disable:function() {this.enabled=!1},toggleEnabled:function() {this.enabled= !this.enabled}};b.fn.tipsy=function(a) {function c(e) {var c=b.data(e,"tipsy");c||(c=new l(e,b.fn.tipsy.elementOptions(e,a)),b.data(e,"tipsy",c));return c}function d() {var b=c(this);b.hoverState="in";0==a.delayIn?b.show():(b.fixTitle(),setTimeout(function() {"in"==b.hoverState&&b.show()},a.delayIn))}function f() {var b=c(this);b.hoverState="out";0==a.delayOut?b.hide():setTimeout(function() {"out"==b.hoverState&&b.hide()},a.delayOut)}if(!0===a)return this.data("tipsy");if("string"==typeof a) {var g=this.data("tipsy"); if(g)g[a]();return this}a=b.extend({},b.fn.tipsy.defaults,a);a.live||this.each(function() {c(this)});if("manual"!=a.trigger) {var g=a.live?"live":"bind",h="hover"==a.trigger?"mouseleave":"blur";this[g]("hover"==a.trigger?"mouseenter":"focus",d)[g](h,f)}return this};b.fn.tipsy.defaults={className:null,delayIn:0,delayOut:0,fade:!1,fallback:"",gravity:"n",html:!1,live:!1,offset:0,opacity:0.8,title:"title",theme:["black","white"],trigger:"hover"};b.fn.tipsy.elementOptions=function(a,c) {return b.metadata? b.extend({},c,b(a).metadata()):c};b.fn.tipsy.autoNS=function() {return b(this).offset().top>b(document).scrollTop()+b(window).height()/2?"s":"n"};b.fn.tipsy.autoWE=function() {return b(this).offset().left>b(document).scrollLeft()+b(window).width()/2?"e":"w"};b.fn.tipsy.autoBounds=function(a,c) {return function() {var d=c[0],f=1 0 ? '' : + value.length > 2 ? 'No results' : 'Please enter three or more letters' + ); + } else { + message(''); + } + }, + message = function(text) { + $status + .attr('original-title', text) + .tipsy( text === '' ? 'hide' : 'show' ); + // make sure the result count doesn't cover the search text + $search.css('padding-right', $status.width() + 5); + }, + jumpTo = function() { + if (resultsLength) { + var resultPosition, parentPosition, leftPosition, + $current = $results.eq(index), + $collapsible = $current.closest('.collapsible, tr[id]'); + if ($collapsible.length && $collapsible.is(':hidden')) { + $collapsible.slideToggle(); + } + if ($current.length) { + resultPosition = $current.offset().top; + parentPosition = $collapsible.length ? $current.closest('tr[id]').offset().top : resultPosition; + if (parentPosition + $(window).height() < resultPosition) { + parentPosition = resultPosition; + } + leftPosition = 0; + if ( $current.position().left > $(window).width() - menuWidth ) { + leftPosition = $(window).width(); + } + window.scrollTo( leftPosition, parentPosition - 28 ); + } + } + updateStatus(); + }, + applySearch = function() { + searching = queryString.parse(search); + if (searching.q) { + $('#main-nav-check').prop('checked', true); + $search + .val( searching.q ) + // make searching.index a zero-based index + .trigger('change', [ isNaN(searching.index) ? 0 : parseInt(searching.index, 10) - 1 ]); + } + }; + + // make sure defaults are set + $('#word, #csfalse').click(); + + $search + // needed for IE + .on('keyup', function(e) { + if (e.which === 13) { + $(this).trigger('change'); + } + }) + .add('#word, #letter, #cstrue, #csfalse').on('change', function(event, newIndex) { + index = newIndex || 0; + $status.addClass('busy'); + setTimeout(function() { + $main + .unhighlight() + .highlight( $search.val(), { + ignore:'thead', + wordsOnly: $word.is(':checked'), + caseSensitive: $case.is(':checked') + }); + $results = $('.highlight'); + resultsLength = $results.length; + if (index > resultsLength) { + index = resultsLength - 1; + } + jumpTo(); + }, 1); + }); + $('.search-prev, .search-next').click(function() { + if (resultsLength) { + index = index + ($(this).hasClass('search-prev') ? -1 : 1); + if (index < 0) { index = resultsLength - 1; } + if (index > resultsLength - 1) { index = 0; } + jumpTo(); + } + }); + $('.search-clear').click(function() { + $search.val('').change(); + updateStatus(); + }); + $main.on('click', '.highlight', function() { + index = $results.index(this); + updateStatus(); + }); + $('#main-nav-check').on('change', function() { + var isChecked = this.checked; + setTimeout(function() { + $status.tipsy( isChecked ? 'show' : 'hide' ); + }, 250); + }); + + $('.tooltip-bottom').tipsy({ gravity: 'n' }); + $('.tooltip-top').tipsy({ gravity: 's' }); + $('.tooltip-edge-left').tipsy({ gravity: 'nw' }); + $('.tooltip-edge-right').tipsy({ gravity: 'ne' }); + $('.tooltip-right').tipsy({ gravity: 'w' }); + $status.tipsy({ + gravity: 's', + opacity: 1, + theme: [ '#d9534f', 'white' ] + }); + $status.tipsy('hide'); + + // search on load + // ?q=array&index=10 + if (search) { + applySearch(); + } + +}); diff --git a/compare/thirdparty/tablesorter/docs/js/select2-3.4.6.min.js b/compare/thirdparty/tablesorter/docs/js/select2-3.4.6.min.js new file mode 100644 index 00000000..335c85e8 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/select2-3.4.6.min.js @@ -0,0 +1,22 @@ +/* +Copyright 2014 Igor Vaynberg + +Version: 3.4.6 Timestamp: Sat Mar 22 22:30:15 EDT 2014 + +This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU +General Public License version 2 (the "GPL License"). You may choose either license to govern your +use of this software only upon the condition that you accept all of the terms of either the Apache +License or the GPL License. + +You may obtain a copy of the Apache License and the GPL License at: + +http://www.apache.org/licenses/LICENSE-2.0 +http://www.gnu.org/licenses/gpl-2.0.html + +Unless required by applicable law or agreed to in writing, software distributed under the Apache License +or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +either express or implied. See the Apache License and the GPL License for the specific language governing +permissions and limitations under the Apache License and the GPL License. +*/ +!function(a){"undefined"==typeof a.fn.each2&&a.extend(a.fn,{each2:function(b){for(var c=a([0]),d=-1,e=this.length;++dc;c++)e=a.charAt(c),b+=m[e]||e;return b}function p(a,b){for(var c=0,d=b.length;d>c;c+=1)if(r(a,b[c]))return c;return-1}function q(){var b=a(l);b.appendTo("body");var c={width:b.width()-b[0].clientWidth,height:b.height()-b[0].clientHeight};return b.remove(),c}function r(a,c){return a===c?!0:a===b||c===b?!1:null===a||null===c?!1:a.constructor===String?a+""==c+"":c.constructor===String?c+""==a+"":!1}function s(b,c){var d,e,f;if(null===b||b.length<1)return[];for(d=b.split(c),e=0,f=d.length;f>e;e+=1)d[e]=a.trim(d[e]);return d}function t(a){return a.outerWidth(!1)-a.width()}function u(c){var d="keyup-change-value";c.on("keydown",function(){a.data(c,d)===b&&a.data(c,d,c.val())}),c.on("keyup",function(){var e=a.data(c,d);e!==b&&c.val()!==e&&(a.removeData(c,d),c.trigger("keyup-change"))})}function v(c){c.on("mousemove",function(c){var d=i;(d===b||d.x!==c.pageX||d.y!==c.pageY)&&a(c.target).trigger("mousemove-filtered",c)})}function w(a,c,d){d=d||b;var e;return function(){var b=arguments;window.clearTimeout(e),e=window.setTimeout(function(){c.apply(d,b)},a)}}function x(a){var c,b=!1;return function(){return b===!1&&(c=a(),b=!0),c}}function y(a,b){var c=w(a,function(a){b.trigger("scroll-debounced",a)});b.on("scroll",function(a){p(a.target,b.get())>=0&&c(a)})}function z(a){a[0]!==document.activeElement&&window.setTimeout(function(){var d,b=a[0],c=a.val().length;a.focus();var e=b.offsetWidth>0||b.offsetHeight>0;e&&b===document.activeElement&&(b.setSelectionRange?b.setSelectionRange(c,c):b.createTextRange&&(d=b.createTextRange(),d.collapse(!1),d.select()))},0)}function A(b){b=a(b)[0];var c=0,d=0;if("selectionStart"in b)c=b.selectionStart,d=b.selectionEnd-c;else if("selection"in document){b.focus();var e=document.selection.createRange();d=document.selection.createRange().text.length,e.moveStart("character",-b.value.length),c=e.text.length-d}return{offset:c,length:d}}function B(a){a.preventDefault(),a.stopPropagation()}function C(a){a.preventDefault(),a.stopImmediatePropagation()}function D(b){if(!h){var c=b[0].currentStyle||window.getComputedStyle(b[0],null);h=a(document.createElement("div")).css({position:"absolute",left:"-10000px",top:"-10000px",display:"none",fontSize:c.fontSize,fontFamily:c.fontFamily,fontStyle:c.fontStyle,fontWeight:c.fontWeight,letterSpacing:c.letterSpacing,textTransform:c.textTransform,whiteSpace:"nowrap"}),h.attr("class","select2-sizer"),a("body").append(h)}return h.text(b.val()),h.width()}function E(b,c,d){var e,g,f=[];e=b.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0===this.indexOf("select2-")&&f.push(this)})),e=c.attr("class"),e&&(e=""+e,a(e.split(" ")).each2(function(){0!==this.indexOf("select2-")&&(g=d(this),g&&f.push(g))})),b.attr("class",f.join(" "))}function F(a,b,c,d){var e=o(a.toUpperCase()).indexOf(o(b.toUpperCase())),f=b.length;return 0>e?(c.push(d(a)),void 0):(c.push(d(a.substring(0,e))),c.push(""),c.push(d(a.substring(e,e+f))),c.push(""),c.push(d(a.substring(e+f,a.length))),void 0)}function G(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})}function H(c){var d,e=null,f=c.quietMillis||100,g=c.url,h=this;return function(i){window.clearTimeout(d),d=window.setTimeout(function(){var d=c.data,f=g,j=c.transport||a.fn.select2.ajaxDefaults.transport,k={type:c.type||"GET",cache:c.cache||!1,jsonpCallback:c.jsonpCallback||b,dataType:c.dataType||"json"},l=a.extend({},a.fn.select2.ajaxDefaults.params,k);d=d?d.call(h,i.term,i.page,i.context):null,f="function"==typeof f?f.call(h,i.term,i.page,i.context):f,e&&"function"==typeof e.abort&&e.abort(),c.params&&(a.isFunction(c.params)?a.extend(l,c.params.call(h)):a.extend(l,c.params)),a.extend(l,{url:f,dataType:c.dataType,data:d,success:function(a){var b=c.results(a,i.page);i.callback(b)}}),e=j.call(h,l)},f)}}function I(b){var d,e,c=b,f=function(a){return""+a.text};a.isArray(c)&&(e=c,c={results:e}),a.isFunction(c)===!1&&(e=c,c=function(){return e});var g=c();return g.text&&(f=g.text,a.isFunction(f)||(d=g.text,f=function(a){return a[d]})),function(b){var g,d=b.term,e={results:[]};return""===d?(b.callback(c()),void 0):(g=function(c,e){var h,i;if(c=c[0],c.children){h={};for(i in c)c.hasOwnProperty(i)&&(h[i]=c[i]);h.children=[],a(c.children).each2(function(a,b){g(b,h.children)}),(h.children.length||b.matcher(d,f(h),c))&&e.push(h)}else b.matcher(d,f(c),c)&&e.push(c)},a(c().results).each2(function(a,b){g(b,e.results)}),b.callback(e),void 0)}}function J(c){var d=a.isFunction(c);return function(e){var f=e.term,g={results:[]};a(d?c():c).each(function(){var a=this.text!==b,c=a?this.text:this;(""===f||e.matcher(f,c))&&g.results.push(a?this:{id:this,text:this})}),e.callback(g)}}function K(b,c){if(a.isFunction(b))return!0;if(!b)return!1;if("string"==typeof b)return!0;throw new Error(c+" must be a string, function, or falsy value")}function L(b){if(a.isFunction(b)){var c=Array.prototype.slice.call(arguments,1);return b.apply(null,c)}return b}function M(b){var c=0;return a.each(b,function(a,b){b.children?c+=M(b.children):c++}),c}function N(a,c,d,e){var h,i,j,k,l,f=a,g=!1;if(!e.createSearchChoice||!e.tokenSeparators||e.tokenSeparators.length<1)return b;for(;;){for(i=-1,j=0,k=e.tokenSeparators.length;k>j&&(l=e.tokenSeparators[j],i=a.indexOf(l),!(i>=0));j++);if(0>i)break;if(h=a.substring(0,i),a=a.substring(i+l.length),h.length>0&&(h=e.createSearchChoice.call(this,h,c),h!==b&&null!==h&&e.id(h)!==b&&null!==e.id(h))){for(g=!1,j=0,k=c.length;k>j;j++)if(r(e.id(h),e.id(c[j]))){g=!0;break}g||d(h)}}return f!==a?a:void 0}function O(b,c){var d=function(){};return d.prototype=new b,d.prototype.constructor=d,d.prototype.parent=b.prototype,d.prototype=a.extend(d.prototype,c),d}if(window.Select2===b){var c,d,e,f,g,h,j,k,i={x:0,y:0},c={TAB:9,ENTER:13,ESC:27,SPACE:32,LEFT:37,UP:38,RIGHT:39,DOWN:40,SHIFT:16,CTRL:17,ALT:18,PAGE_UP:33,PAGE_DOWN:34,HOME:36,END:35,BACKSPACE:8,DELETE:46,isArrow:function(a){switch(a=a.which?a.which:a){case c.LEFT:case c.RIGHT:case c.UP:case c.DOWN:return!0}return!1},isControl:function(a){var b=a.which;switch(b){case c.SHIFT:case c.CTRL:case c.ALT:return!0}return a.metaKey?!0:!1},isFunctionKey:function(a){return a=a.which?a.which:a,a>=112&&123>=a}},l="
",m={"\u24b6":"A","\uff21":"A","\xc0":"A","\xc1":"A","\xc2":"A","\u1ea6":"A","\u1ea4":"A","\u1eaa":"A","\u1ea8":"A","\xc3":"A","\u0100":"A","\u0102":"A","\u1eb0":"A","\u1eae":"A","\u1eb4":"A","\u1eb2":"A","\u0226":"A","\u01e0":"A","\xc4":"A","\u01de":"A","\u1ea2":"A","\xc5":"A","\u01fa":"A","\u01cd":"A","\u0200":"A","\u0202":"A","\u1ea0":"A","\u1eac":"A","\u1eb6":"A","\u1e00":"A","\u0104":"A","\u023a":"A","\u2c6f":"A","\ua732":"AA","\xc6":"AE","\u01fc":"AE","\u01e2":"AE","\ua734":"AO","\ua736":"AU","\ua738":"AV","\ua73a":"AV","\ua73c":"AY","\u24b7":"B","\uff22":"B","\u1e02":"B","\u1e04":"B","\u1e06":"B","\u0243":"B","\u0182":"B","\u0181":"B","\u24b8":"C","\uff23":"C","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\xc7":"C","\u1e08":"C","\u0187":"C","\u023b":"C","\ua73e":"C","\u24b9":"D","\uff24":"D","\u1e0a":"D","\u010e":"D","\u1e0c":"D","\u1e10":"D","\u1e12":"D","\u1e0e":"D","\u0110":"D","\u018b":"D","\u018a":"D","\u0189":"D","\ua779":"D","\u01f1":"DZ","\u01c4":"DZ","\u01f2":"Dz","\u01c5":"Dz","\u24ba":"E","\uff25":"E","\xc8":"E","\xc9":"E","\xca":"E","\u1ec0":"E","\u1ebe":"E","\u1ec4":"E","\u1ec2":"E","\u1ebc":"E","\u0112":"E","\u1e14":"E","\u1e16":"E","\u0114":"E","\u0116":"E","\xcb":"E","\u1eba":"E","\u011a":"E","\u0204":"E","\u0206":"E","\u1eb8":"E","\u1ec6":"E","\u0228":"E","\u1e1c":"E","\u0118":"E","\u1e18":"E","\u1e1a":"E","\u0190":"E","\u018e":"E","\u24bb":"F","\uff26":"F","\u1e1e":"F","\u0191":"F","\ua77b":"F","\u24bc":"G","\uff27":"G","\u01f4":"G","\u011c":"G","\u1e20":"G","\u011e":"G","\u0120":"G","\u01e6":"G","\u0122":"G","\u01e4":"G","\u0193":"G","\ua7a0":"G","\ua77d":"G","\ua77e":"G","\u24bd":"H","\uff28":"H","\u0124":"H","\u1e22":"H","\u1e26":"H","\u021e":"H","\u1e24":"H","\u1e28":"H","\u1e2a":"H","\u0126":"H","\u2c67":"H","\u2c75":"H","\ua78d":"H","\u24be":"I","\uff29":"I","\xcc":"I","\xcd":"I","\xce":"I","\u0128":"I","\u012a":"I","\u012c":"I","\u0130":"I","\xcf":"I","\u1e2e":"I","\u1ec8":"I","\u01cf":"I","\u0208":"I","\u020a":"I","\u1eca":"I","\u012e":"I","\u1e2c":"I","\u0197":"I","\u24bf":"J","\uff2a":"J","\u0134":"J","\u0248":"J","\u24c0":"K","\uff2b":"K","\u1e30":"K","\u01e8":"K","\u1e32":"K","\u0136":"K","\u1e34":"K","\u0198":"K","\u2c69":"K","\ua740":"K","\ua742":"K","\ua744":"K","\ua7a2":"K","\u24c1":"L","\uff2c":"L","\u013f":"L","\u0139":"L","\u013d":"L","\u1e36":"L","\u1e38":"L","\u013b":"L","\u1e3c":"L","\u1e3a":"L","\u0141":"L","\u023d":"L","\u2c62":"L","\u2c60":"L","\ua748":"L","\ua746":"L","\ua780":"L","\u01c7":"LJ","\u01c8":"Lj","\u24c2":"M","\uff2d":"M","\u1e3e":"M","\u1e40":"M","\u1e42":"M","\u2c6e":"M","\u019c":"M","\u24c3":"N","\uff2e":"N","\u01f8":"N","\u0143":"N","\xd1":"N","\u1e44":"N","\u0147":"N","\u1e46":"N","\u0145":"N","\u1e4a":"N","\u1e48":"N","\u0220":"N","\u019d":"N","\ua790":"N","\ua7a4":"N","\u01ca":"NJ","\u01cb":"Nj","\u24c4":"O","\uff2f":"O","\xd2":"O","\xd3":"O","\xd4":"O","\u1ed2":"O","\u1ed0":"O","\u1ed6":"O","\u1ed4":"O","\xd5":"O","\u1e4c":"O","\u022c":"O","\u1e4e":"O","\u014c":"O","\u1e50":"O","\u1e52":"O","\u014e":"O","\u022e":"O","\u0230":"O","\xd6":"O","\u022a":"O","\u1ece":"O","\u0150":"O","\u01d1":"O","\u020c":"O","\u020e":"O","\u01a0":"O","\u1edc":"O","\u1eda":"O","\u1ee0":"O","\u1ede":"O","\u1ee2":"O","\u1ecc":"O","\u1ed8":"O","\u01ea":"O","\u01ec":"O","\xd8":"O","\u01fe":"O","\u0186":"O","\u019f":"O","\ua74a":"O","\ua74c":"O","\u01a2":"OI","\ua74e":"OO","\u0222":"OU","\u24c5":"P","\uff30":"P","\u1e54":"P","\u1e56":"P","\u01a4":"P","\u2c63":"P","\ua750":"P","\ua752":"P","\ua754":"P","\u24c6":"Q","\uff31":"Q","\ua756":"Q","\ua758":"Q","\u024a":"Q","\u24c7":"R","\uff32":"R","\u0154":"R","\u1e58":"R","\u0158":"R","\u0210":"R","\u0212":"R","\u1e5a":"R","\u1e5c":"R","\u0156":"R","\u1e5e":"R","\u024c":"R","\u2c64":"R","\ua75a":"R","\ua7a6":"R","\ua782":"R","\u24c8":"S","\uff33":"S","\u1e9e":"S","\u015a":"S","\u1e64":"S","\u015c":"S","\u1e60":"S","\u0160":"S","\u1e66":"S","\u1e62":"S","\u1e68":"S","\u0218":"S","\u015e":"S","\u2c7e":"S","\ua7a8":"S","\ua784":"S","\u24c9":"T","\uff34":"T","\u1e6a":"T","\u0164":"T","\u1e6c":"T","\u021a":"T","\u0162":"T","\u1e70":"T","\u1e6e":"T","\u0166":"T","\u01ac":"T","\u01ae":"T","\u023e":"T","\ua786":"T","\ua728":"TZ","\u24ca":"U","\uff35":"U","\xd9":"U","\xda":"U","\xdb":"U","\u0168":"U","\u1e78":"U","\u016a":"U","\u1e7a":"U","\u016c":"U","\xdc":"U","\u01db":"U","\u01d7":"U","\u01d5":"U","\u01d9":"U","\u1ee6":"U","\u016e":"U","\u0170":"U","\u01d3":"U","\u0214":"U","\u0216":"U","\u01af":"U","\u1eea":"U","\u1ee8":"U","\u1eee":"U","\u1eec":"U","\u1ef0":"U","\u1ee4":"U","\u1e72":"U","\u0172":"U","\u1e76":"U","\u1e74":"U","\u0244":"U","\u24cb":"V","\uff36":"V","\u1e7c":"V","\u1e7e":"V","\u01b2":"V","\ua75e":"V","\u0245":"V","\ua760":"VY","\u24cc":"W","\uff37":"W","\u1e80":"W","\u1e82":"W","\u0174":"W","\u1e86":"W","\u1e84":"W","\u1e88":"W","\u2c72":"W","\u24cd":"X","\uff38":"X","\u1e8a":"X","\u1e8c":"X","\u24ce":"Y","\uff39":"Y","\u1ef2":"Y","\xdd":"Y","\u0176":"Y","\u1ef8":"Y","\u0232":"Y","\u1e8e":"Y","\u0178":"Y","\u1ef6":"Y","\u1ef4":"Y","\u01b3":"Y","\u024e":"Y","\u1efe":"Y","\u24cf":"Z","\uff3a":"Z","\u0179":"Z","\u1e90":"Z","\u017b":"Z","\u017d":"Z","\u1e92":"Z","\u1e94":"Z","\u01b5":"Z","\u0224":"Z","\u2c7f":"Z","\u2c6b":"Z","\ua762":"Z","\u24d0":"a","\uff41":"a","\u1e9a":"a","\xe0":"a","\xe1":"a","\xe2":"a","\u1ea7":"a","\u1ea5":"a","\u1eab":"a","\u1ea9":"a","\xe3":"a","\u0101":"a","\u0103":"a","\u1eb1":"a","\u1eaf":"a","\u1eb5":"a","\u1eb3":"a","\u0227":"a","\u01e1":"a","\xe4":"a","\u01df":"a","\u1ea3":"a","\xe5":"a","\u01fb":"a","\u01ce":"a","\u0201":"a","\u0203":"a","\u1ea1":"a","\u1ead":"a","\u1eb7":"a","\u1e01":"a","\u0105":"a","\u2c65":"a","\u0250":"a","\ua733":"aa","\xe6":"ae","\u01fd":"ae","\u01e3":"ae","\ua735":"ao","\ua737":"au","\ua739":"av","\ua73b":"av","\ua73d":"ay","\u24d1":"b","\uff42":"b","\u1e03":"b","\u1e05":"b","\u1e07":"b","\u0180":"b","\u0183":"b","\u0253":"b","\u24d2":"c","\uff43":"c","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\xe7":"c","\u1e09":"c","\u0188":"c","\u023c":"c","\ua73f":"c","\u2184":"c","\u24d3":"d","\uff44":"d","\u1e0b":"d","\u010f":"d","\u1e0d":"d","\u1e11":"d","\u1e13":"d","\u1e0f":"d","\u0111":"d","\u018c":"d","\u0256":"d","\u0257":"d","\ua77a":"d","\u01f3":"dz","\u01c6":"dz","\u24d4":"e","\uff45":"e","\xe8":"e","\xe9":"e","\xea":"e","\u1ec1":"e","\u1ebf":"e","\u1ec5":"e","\u1ec3":"e","\u1ebd":"e","\u0113":"e","\u1e15":"e","\u1e17":"e","\u0115":"e","\u0117":"e","\xeb":"e","\u1ebb":"e","\u011b":"e","\u0205":"e","\u0207":"e","\u1eb9":"e","\u1ec7":"e","\u0229":"e","\u1e1d":"e","\u0119":"e","\u1e19":"e","\u1e1b":"e","\u0247":"e","\u025b":"e","\u01dd":"e","\u24d5":"f","\uff46":"f","\u1e1f":"f","\u0192":"f","\ua77c":"f","\u24d6":"g","\uff47":"g","\u01f5":"g","\u011d":"g","\u1e21":"g","\u011f":"g","\u0121":"g","\u01e7":"g","\u0123":"g","\u01e5":"g","\u0260":"g","\ua7a1":"g","\u1d79":"g","\ua77f":"g","\u24d7":"h","\uff48":"h","\u0125":"h","\u1e23":"h","\u1e27":"h","\u021f":"h","\u1e25":"h","\u1e29":"h","\u1e2b":"h","\u1e96":"h","\u0127":"h","\u2c68":"h","\u2c76":"h","\u0265":"h","\u0195":"hv","\u24d8":"i","\uff49":"i","\xec":"i","\xed":"i","\xee":"i","\u0129":"i","\u012b":"i","\u012d":"i","\xef":"i","\u1e2f":"i","\u1ec9":"i","\u01d0":"i","\u0209":"i","\u020b":"i","\u1ecb":"i","\u012f":"i","\u1e2d":"i","\u0268":"i","\u0131":"i","\u24d9":"j","\uff4a":"j","\u0135":"j","\u01f0":"j","\u0249":"j","\u24da":"k","\uff4b":"k","\u1e31":"k","\u01e9":"k","\u1e33":"k","\u0137":"k","\u1e35":"k","\u0199":"k","\u2c6a":"k","\ua741":"k","\ua743":"k","\ua745":"k","\ua7a3":"k","\u24db":"l","\uff4c":"l","\u0140":"l","\u013a":"l","\u013e":"l","\u1e37":"l","\u1e39":"l","\u013c":"l","\u1e3d":"l","\u1e3b":"l","\u017f":"l","\u0142":"l","\u019a":"l","\u026b":"l","\u2c61":"l","\ua749":"l","\ua781":"l","\ua747":"l","\u01c9":"lj","\u24dc":"m","\uff4d":"m","\u1e3f":"m","\u1e41":"m","\u1e43":"m","\u0271":"m","\u026f":"m","\u24dd":"n","\uff4e":"n","\u01f9":"n","\u0144":"n","\xf1":"n","\u1e45":"n","\u0148":"n","\u1e47":"n","\u0146":"n","\u1e4b":"n","\u1e49":"n","\u019e":"n","\u0272":"n","\u0149":"n","\ua791":"n","\ua7a5":"n","\u01cc":"nj","\u24de":"o","\uff4f":"o","\xf2":"o","\xf3":"o","\xf4":"o","\u1ed3":"o","\u1ed1":"o","\u1ed7":"o","\u1ed5":"o","\xf5":"o","\u1e4d":"o","\u022d":"o","\u1e4f":"o","\u014d":"o","\u1e51":"o","\u1e53":"o","\u014f":"o","\u022f":"o","\u0231":"o","\xf6":"o","\u022b":"o","\u1ecf":"o","\u0151":"o","\u01d2":"o","\u020d":"o","\u020f":"o","\u01a1":"o","\u1edd":"o","\u1edb":"o","\u1ee1":"o","\u1edf":"o","\u1ee3":"o","\u1ecd":"o","\u1ed9":"o","\u01eb":"o","\u01ed":"o","\xf8":"o","\u01ff":"o","\u0254":"o","\ua74b":"o","\ua74d":"o","\u0275":"o","\u01a3":"oi","\u0223":"ou","\ua74f":"oo","\u24df":"p","\uff50":"p","\u1e55":"p","\u1e57":"p","\u01a5":"p","\u1d7d":"p","\ua751":"p","\ua753":"p","\ua755":"p","\u24e0":"q","\uff51":"q","\u024b":"q","\ua757":"q","\ua759":"q","\u24e1":"r","\uff52":"r","\u0155":"r","\u1e59":"r","\u0159":"r","\u0211":"r","\u0213":"r","\u1e5b":"r","\u1e5d":"r","\u0157":"r","\u1e5f":"r","\u024d":"r","\u027d":"r","\ua75b":"r","\ua7a7":"r","\ua783":"r","\u24e2":"s","\uff53":"s","\xdf":"s","\u015b":"s","\u1e65":"s","\u015d":"s","\u1e61":"s","\u0161":"s","\u1e67":"s","\u1e63":"s","\u1e69":"s","\u0219":"s","\u015f":"s","\u023f":"s","\ua7a9":"s","\ua785":"s","\u1e9b":"s","\u24e3":"t","\uff54":"t","\u1e6b":"t","\u1e97":"t","\u0165":"t","\u1e6d":"t","\u021b":"t","\u0163":"t","\u1e71":"t","\u1e6f":"t","\u0167":"t","\u01ad":"t","\u0288":"t","\u2c66":"t","\ua787":"t","\ua729":"tz","\u24e4":"u","\uff55":"u","\xf9":"u","\xfa":"u","\xfb":"u","\u0169":"u","\u1e79":"u","\u016b":"u","\u1e7b":"u","\u016d":"u","\xfc":"u","\u01dc":"u","\u01d8":"u","\u01d6":"u","\u01da":"u","\u1ee7":"u","\u016f":"u","\u0171":"u","\u01d4":"u","\u0215":"u","\u0217":"u","\u01b0":"u","\u1eeb":"u","\u1ee9":"u","\u1eef":"u","\u1eed":"u","\u1ef1":"u","\u1ee5":"u","\u1e73":"u","\u0173":"u","\u1e77":"u","\u1e75":"u","\u0289":"u","\u24e5":"v","\uff56":"v","\u1e7d":"v","\u1e7f":"v","\u028b":"v","\ua75f":"v","\u028c":"v","\ua761":"vy","\u24e6":"w","\uff57":"w","\u1e81":"w","\u1e83":"w","\u0175":"w","\u1e87":"w","\u1e85":"w","\u1e98":"w","\u1e89":"w","\u2c73":"w","\u24e7":"x","\uff58":"x","\u1e8b":"x","\u1e8d":"x","\u24e8":"y","\uff59":"y","\u1ef3":"y","\xfd":"y","\u0177":"y","\u1ef9":"y","\u0233":"y","\u1e8f":"y","\xff":"y","\u1ef7":"y","\u1e99":"y","\u1ef5":"y","\u01b4":"y","\u024f":"y","\u1eff":"y","\u24e9":"z","\uff5a":"z","\u017a":"z","\u1e91":"z","\u017c":"z","\u017e":"z","\u1e93":"z","\u1e95":"z","\u01b6":"z","\u0225":"z","\u0240":"z","\u2c6c":"z","\ua763":"z"};j=a(document),g=function(){var a=1;return function(){return a++}}(),j.on("mousemove",function(a){i.x=a.pageX,i.y=a.pageY}),d=O(Object,{bind:function(a){var b=this;return function(){a.apply(b,arguments)}},init:function(c){var d,e,f=".select2-results";this.opts=c=this.prepareOpts(c),this.id=c.id,c.element.data("select2")!==b&&null!==c.element.data("select2")&&c.element.data("select2").destroy(),this.container=this.createContainer(),this.liveRegion=a("",{role:"status","aria-live":"polite"}).addClass("select2-hidden-accessible").appendTo(document.body),this.containerId="s2id_"+(c.element.attr("id")||"autogen"+g()).replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g,"\\$1"),this.containerSelector="#"+this.containerId,this.container.attr("id",this.containerId),this.body=x(function(){return c.element.closest("body")}),E(this.container,this.opts.element,this.opts.adaptContainerCssClass),this.container.attr("style",c.element.attr("style")),this.container.css(L(c.containerCss)),this.container.addClass(L(c.containerCssClass)),this.elementTabIndex=this.opts.element.attr("tabindex"),this.opts.element.data("select2",this).attr("tabindex","-1").before(this.container).on("click.select2",B),this.container.data("select2",this),this.dropdown=this.container.find(".select2-drop"),E(this.dropdown,this.opts.element,this.opts.adaptDropdownCssClass),this.dropdown.addClass(L(c.dropdownCssClass)),this.dropdown.data("select2",this),this.dropdown.on("click",B),this.results=d=this.container.find(f),this.search=e=this.container.find("input.select2-input"),this.queryCount=0,this.resultsPage=0,this.context=null,this.initContainer(),this.container.on("click",B),v(this.results),this.dropdown.on("mousemove-filtered touchstart touchmove touchend",f,this.bind(this.highlightUnderEvent)),this.dropdown.on("touchend",f,this.bind(this.selectHighlighted)),this.dropdown.on("touchmove",f,this.bind(this.touchMoved)),this.dropdown.on("touchstart touchend",f,this.bind(this.clearTouchMoved)),y(80,this.results),this.dropdown.on("scroll-debounced",f,this.bind(this.loadMoreIfNeeded)),a(this.container).on("change",".select2-input",function(a){a.stopPropagation()}),a(this.dropdown).on("change",".select2-input",function(a){a.stopPropagation()}),a.fn.mousewheel&&d.mousewheel(function(a,b,c,e){var f=d.scrollTop();e>0&&0>=f-e?(d.scrollTop(0),B(a)):0>e&&d.get(0).scrollHeight-d.scrollTop()+e<=d.height()&&(d.scrollTop(d.get(0).scrollHeight-d.height()),B(a))}),u(e),e.on("keyup-change input paste",this.bind(this.updateResults)),e.on("focus",function(){e.addClass("select2-focused")}),e.on("blur",function(){e.removeClass("select2-focused")}),this.dropdown.on("mouseup",f,this.bind(function(b){a(b.target).closest(".select2-result-selectable").length>0&&(this.highlightUnderEvent(b),this.selectHighlighted(b))})),this.dropdown.on("click mouseup mousedown focusin",function(a){a.stopPropagation()}),this.nextSearchTerm=b,a.isFunction(this.opts.initSelection)&&(this.initSelection(),this.monitorSource()),null!==c.maximumInputLength&&this.search.attr("maxlength",c.maximumInputLength);var h=c.element.prop("disabled");h===b&&(h=!1),this.enable(!h);var i=c.element.prop("readonly");i===b&&(i=!1),this.readonly(i),k=k||q(),this.autofocus=c.element.prop("autofocus"),c.element.prop("autofocus",!1),this.autofocus&&this.focus(),this.search.attr("placeholder",c.searchInputPlaceholder)},destroy:function(){var a=this.opts.element,c=a.data("select2");this.close(),this.propertyObserver&&(delete this.propertyObserver,this.propertyObserver=null),c!==b&&(c.container.remove(),c.liveRegion.remove(),c.dropdown.remove(),a.removeClass("select2-offscreen").removeData("select2").off(".select2").prop("autofocus",this.autofocus||!1),this.elementTabIndex?a.attr({tabindex:this.elementTabIndex}):a.removeAttr("tabindex"),a.show())},optionToData:function(a){return a.is("option")?{id:a.prop("value"),text:a.text(),element:a.get(),css:a.attr("class"),disabled:a.prop("disabled"),locked:r(a.attr("locked"),"locked")||r(a.data("locked"),!0)}:a.is("optgroup")?{text:a.attr("label"),children:[],element:a.get(),css:a.attr("class")}:void 0},prepareOpts:function(c){var d,e,f,h,i=this;if(d=c.element,"select"===d.get(0).tagName.toLowerCase()&&(this.select=e=c.element),e&&a.each(["id","multiple","ajax","query","createSearchChoice","initSelection","data","tags"],function(){if(this in c)throw new Error("Option '"+this+"' is not allowed for Select2 when attached to a ","
"," ","
    ","
","
"].join(""));return b},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.focusser.prop("disabled",!this.isInterfaceEnabled())},opening:function(){var c,d,e;this.opts.minimumResultsForSearch>=0&&this.showSearch(!0),this.parent.opening.apply(this,arguments),this.showSearchInput!==!1&&this.search.val(this.focusser.val()),this.search.focus(),c=this.search.get(0),c.createTextRange?(d=c.createTextRange(),d.collapse(!1),d.select()):c.setSelectionRange&&(e=this.search.val().length,c.setSelectionRange(e,e)),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.focusser.prop("disabled",!0).val(""),this.updateResults(!0),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&(this.parent.close.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},focus:function(){this.opened()?this.close():(this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus())},isFocused:function(){return this.container.hasClass("select2-container-active")},cancel:function(){this.parent.cancel.apply(this,arguments),this.focusser.prop("disabled",!1),this.opts.shouldFocusInput(this)&&this.focusser.focus()},destroy:function(){a("label[for='"+this.focusser.attr("id")+"']").attr("for",this.opts.element.attr("id")),this.parent.destroy.apply(this,arguments)},initContainer:function(){var b,h,d=this.container,e=this.dropdown,f=g();this.opts.minimumResultsForSearch<0?this.showSearch(!1):this.showSearch(!0),this.selection=b=d.find(".select2-choice"),this.focusser=d.find(".select2-focusser"),b.find(".select2-chosen").attr("id","select2-chosen-"+f),this.focusser.attr("aria-labelledby","select2-chosen-"+f),this.results.attr("id","select2-results-"+f),this.search.attr("aria-owns","select2-results-"+f),this.focusser.attr("id","s2id_autogen"+f),h=a("label[for='"+this.opts.element.attr("id")+"']"),this.focusser.prev().text(h.text()).attr("for",this.focusser.attr("id"));var i=this.opts.element.attr("title");this.opts.element.attr("title",i||h.text()),this.focusser.attr("tabindex",this.elementTabIndex),this.search.attr("id",this.focusser.attr("id")+"_search"),this.search.prev().text(a("label[for='"+this.focusser.attr("id")+"']").text()).attr("for",this.search.attr("id")),this.search.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()){if(a.which===c.PAGE_UP||a.which===c.PAGE_DOWN)return B(a),void 0;switch(a.which){case c.UP:case c.DOWN:return this.moveHighlight(a.which===c.UP?-1:1),B(a),void 0;case c.ENTER:return this.selectHighlighted(),B(a),void 0;case c.TAB:return this.selectHighlighted({noFocus:!0}),void 0;case c.ESC:return this.cancel(a),B(a),void 0}}})),this.search.on("blur",this.bind(function(){document.activeElement===this.body().get(0)&&window.setTimeout(this.bind(function(){this.opened()&&this.search.focus()}),0)})),this.focusser.on("keydown",this.bind(function(a){if(this.isInterfaceEnabled()&&a.which!==c.TAB&&!c.isControl(a)&&!c.isFunctionKey(a)&&a.which!==c.ESC){if(this.opts.openOnEnter===!1&&a.which===c.ENTER)return B(a),void 0;if(a.which==c.DOWN||a.which==c.UP||a.which==c.ENTER&&this.opts.openOnEnter){if(a.altKey||a.ctrlKey||a.shiftKey||a.metaKey)return;return this.open(),B(a),void 0}return a.which==c.DELETE||a.which==c.BACKSPACE?(this.opts.allowClear&&this.clear(),B(a),void 0):void 0}})),u(this.focusser),this.focusser.on("keyup-change input",this.bind(function(a){if(this.opts.minimumResultsForSearch>=0){if(a.stopPropagation(),this.opened())return;this.open()}})),b.on("mousedown touchstart","abbr",this.bind(function(a){this.isInterfaceEnabled()&&(this.clear(),C(a),this.close(),this.selection.focus())})),b.on("mousedown touchstart",this.bind(function(c){n(b),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.opened()?this.close():this.isInterfaceEnabled()&&this.open(),B(c)})),e.on("mousedown touchstart",this.bind(function(){this.search.focus()})),b.on("focus",this.bind(function(a){B(a)})),this.focusser.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})).on("blur",this.bind(function(){this.opened()||(this.container.removeClass("select2-container-active"),this.opts.element.trigger(a.Event("select2-blur")))})),this.search.on("focus",this.bind(function(){this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active")})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.setPlaceholder()},clear:function(b){var c=this.selection.data("select2-data");if(c){var d=a.Event("select2-clearing");if(this.opts.element.trigger(d),d.isDefaultPrevented())return;var e=this.getPlaceholderOption();this.opts.element.val(e?e.val():""),this.selection.find(".select2-chosen").empty(),this.selection.removeData("select2-data"),this.setPlaceholder(),b!==!1&&(this.opts.element.trigger({type:"select2-removed",val:this.id(c),choice:c}),this.triggerChange({removed:c}))}},initSelection:function(){if(this.isPlaceholderOptionSelected())this.updateSelection(null),this.close(),this.setPlaceholder();else{var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.setPlaceholder(),c.nextSearchTerm=c.opts.nextSearchTerm(a,c.search.val()))})}},isPlaceholderOptionSelected:function(){var a;return this.getPlaceholder()?(a=this.getPlaceholderOption())!==b&&a.prop("selected")||""===this.opts.element.val()||this.opts.element.val()===b||null===this.opts.element.val():!1},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=a.find("option").filter(function(){return this.selected&&!this.disabled});b(c.optionToData(d))}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=c.val(),f=null;b.query({matcher:function(a,c,d){var g=r(e,b.id(d));return g&&(f=d),g},callback:a.isFunction(d)?function(){d(f)}:a.noop})}),b},getPlaceholder:function(){return this.select&&this.getPlaceholderOption()===b?b:this.parent.getPlaceholder.apply(this,arguments)},setPlaceholder:function(){var a=this.getPlaceholder();if(this.isPlaceholderOptionSelected()&&a!==b){if(this.select&&this.getPlaceholderOption()===b)return;this.selection.find(".select2-chosen").html(this.opts.escapeMarkup(a)),this.selection.addClass("select2-default"),this.container.removeClass("select2-allowclear")}},postprocessResults:function(a,b,c){var d=0,e=this;if(this.findHighlightableChoices().each2(function(a,b){return r(e.id(b.data("select2-data")),e.opts.element.val())?(d=a,!1):void 0}),c!==!1&&(b===!0&&d>=0?this.highlight(d):this.highlight(0)),b===!0){var g=this.opts.minimumResultsForSearch;g>=0&&this.showSearch(M(a.results)>=g)}},showSearch:function(b){this.showSearchInput!==b&&(this.showSearchInput=b,this.dropdown.find(".select2-search").toggleClass("select2-search-hidden",!b),this.dropdown.find(".select2-search").toggleClass("select2-offscreen",!b),a(this.dropdown,this.container).toggleClass("select2-with-searchbox",b))},onSelect:function(a,b){if(this.triggerSelect(a)){var c=this.opts.element.val(),d=this.data();this.opts.element.val(this.id(a)),this.updateSelection(a),this.opts.element.trigger({type:"select2-selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.close(),b&&b.noFocus||!this.opts.shouldFocusInput(this)||this.focusser.focus(),r(c,this.id(a))||this.triggerChange({added:a,removed:d})}},updateSelection:function(a){var d,e,c=this.selection.find(".select2-chosen");this.selection.data("select2-data",a),c.empty(),null!==a&&(d=this.opts.formatSelection(a,c,this.opts.escapeMarkup)),d!==b&&c.append(d),e=this.opts.formatSelectionCssClass(a,c),e!==b&&c.addClass(e),this.selection.removeClass("select2-default"),this.opts.allowClear&&this.getPlaceholder()!==b&&this.container.addClass("select2-allowclear")},val:function(){var a,c=!1,d=null,e=this,f=this.data();if(0===arguments.length)return this.opts.element.val();if(a=arguments[0],arguments.length>1&&(c=arguments[1]),this.select)this.select.val(a).find("option").filter(function(){return this.selected}).each2(function(a,b){return d=e.optionToData(b),!1}),this.updateSelection(d),this.setPlaceholder(),c&&this.triggerChange({added:d,removed:f});else{if(!a&&0!==a)return this.clear(c),void 0;if(this.opts.initSelection===b)throw new Error("cannot call val() if initSelection() is not defined");this.opts.element.val(a),this.opts.initSelection(this.opts.element,function(a){e.opts.element.val(a?e.id(a):""),e.updateSelection(a),e.setPlaceholder(),c&&e.triggerChange({added:a,removed:f})})}},clearSearch:function(){this.search.val(""),this.focusser.val("")},data:function(a){var c,d=!1;return 0===arguments.length?(c=this.selection.data("select2-data"),c==b&&(c=null),c):(arguments.length>1&&(d=arguments[1]),a?(c=this.data(),this.opts.element.val(a?this.id(a):""),this.updateSelection(a),d&&this.triggerChange({added:a,removed:c})):this.clear(d),void 0)}}),f=O(d,{createContainer:function(){var b=a(document.createElement("div")).attr({"class":"select2-container select2-container-multi"}).html(["
    ","
  • "," "," ","
  • ","
","
","
    ","
","
"].join(""));return b},prepareOpts:function(){var b=this.parent.prepareOpts.apply(this,arguments),c=this;return"select"===b.element.get(0).tagName.toLowerCase()?b.initSelection=function(a,b){var d=[];a.find("option").filter(function(){return this.selected&&!this.disabled}).each2(function(a,b){d.push(c.optionToData(b))}),b(d)}:"data"in b&&(b.initSelection=b.initSelection||function(c,d){var e=s(c.val(),b.separator),f=[];b.query({matcher:function(c,d,g){var h=a.grep(e,function(a){return r(a,b.id(g))}).length;return h&&f.push(g),h},callback:a.isFunction(d)?function(){for(var a=[],c=0;c0||(this.selectChoice(null),this.clearPlaceholder(),this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.open(),this.focusSearch(),b.preventDefault()))})),this.container.on("focus",b,this.bind(function(){this.isInterfaceEnabled()&&(this.container.hasClass("select2-container-active")||this.opts.element.trigger(a.Event("select2-focus")),this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"),this.clearPlaceholder())})),this.initContainerWidth(),this.opts.element.addClass("select2-offscreen"),this.clearSearch()},enableInterface:function(){this.parent.enableInterface.apply(this,arguments)&&this.search.prop("disabled",!this.isInterfaceEnabled())},initSelection:function(){if(""===this.opts.element.val()&&""===this.opts.element.text()&&(this.updateSelection([]),this.close(),this.clearSearch()),this.select||""!==this.opts.element.val()){var c=this;this.opts.initSelection.call(null,this.opts.element,function(a){a!==b&&null!==a&&(c.updateSelection(a),c.close(),c.clearSearch())})}},clearSearch:function(){var a=this.getPlaceholder(),c=this.getMaxSearchWidth();a!==b&&0===this.getVal().length&&this.search.hasClass("select2-focused")===!1?(this.search.val(a).addClass("select2-default"),this.search.width(c>0?c:this.container.css("width"))):this.search.val("").width(10)},clearPlaceholder:function(){this.search.hasClass("select2-default")&&this.search.val("").removeClass("select2-default")},opening:function(){this.clearPlaceholder(),this.resizeSearch(),this.parent.opening.apply(this,arguments),this.focusSearch(),""===this.search.val()&&this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.search.select()),this.updateResults(!0),this.search.focus(),this.opts.element.trigger(a.Event("select2-open"))},close:function(){this.opened()&&this.parent.close.apply(this,arguments)},focus:function(){this.close(),this.search.focus()},isFocused:function(){return this.search.hasClass("select2-focused")},updateSelection:function(b){var c=[],d=[],e=this;a(b).each(function(){p(e.id(this),c)<0&&(c.push(e.id(this)),d.push(this))}),b=d,this.selection.find(".select2-search-choice").remove(),a(b).each(function(){e.addSelectedChoice(this)}),e.postprocessResults()},tokenize:function(){var a=this.search.val();a=this.opts.tokenizer.call(this,a,this.data(),this.bind(this.onSelect),this.opts),null!=a&&a!=b&&(this.search.val(a),a.length>0&&this.open())},onSelect:function(a,c){this.triggerSelect(a)&&(this.addSelectedChoice(a),this.opts.element.trigger({type:"selected",val:this.id(a),choice:a}),this.nextSearchTerm=this.opts.nextSearchTerm(a,this.search.val()),this.clearSearch(),this.updateResults(),(this.select||!this.opts.closeOnSelect)&&this.postprocessResults(a,!1,this.opts.closeOnSelect===!0),this.opts.closeOnSelect?(this.close(),this.search.width(10)):this.countSelectableResults()>0?(this.search.width(10),this.resizeSearch(),this.getMaximumSelectionSize()>0&&this.val().length>=this.getMaximumSelectionSize()?this.updateResults(!0):this.nextSearchTerm!=b&&(this.search.val(this.nextSearchTerm),this.updateResults(),this.search.select()),this.positionDropdown()):(this.close(),this.search.width(10)),this.triggerChange({added:a}),c&&c.noFocus||this.focusSearch())},cancel:function(){this.close(),this.focusSearch()},addSelectedChoice:function(c){var j,k,d=!c.locked,e=a("
  • "),f=a("
  • "),g=d?e:f,h=this.id(c),i=this.getVal();j=this.opts.formatSelection(c,g.find("div"),this.opts.escapeMarkup),j!=b&&g.find("div").replaceWith("
    "+j+"
    "),k=this.opts.formatSelectionCssClass(c,g.find("div")),k!=b&&g.addClass(k),d&&g.find(".select2-search-choice-close").on("mousedown",B).on("click dblclick",this.bind(function(b){this.isInterfaceEnabled()&&(this.unselect(a(b.target)),this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus"),B(b),this.close(),this.focusSearch())})).on("focus",this.bind(function(){this.isInterfaceEnabled()&&(this.container.addClass("select2-container-active"),this.dropdown.addClass("select2-drop-active"))})),g.data("select2-data",c),g.insertBefore(this.searchContainer),i.push(h),this.setVal(i)},unselect:function(b){var d,e,c=this.getVal();if(b=b.closest(".select2-search-choice"),0===b.length)throw"Invalid argument: "+b+". Must be .select2-search-choice";if(d=b.data("select2-data")){var f=a.Event("select2-removing");if(f.val=this.id(d),f.choice=d,this.opts.element.trigger(f),f.isDefaultPrevented())return!1;for(;(e=p(this.id(d),c))>=0;)c.splice(e,1),this.setVal(c),this.select&&this.postprocessResults();return b.remove(),this.opts.element.trigger({type:"select2-removed",val:this.id(d),choice:d}),this.triggerChange({removed:d}),!0}},postprocessResults:function(a,b,c){var d=this.getVal(),e=this.results.find(".select2-result"),f=this.results.find(".select2-result-with-children"),g=this;e.each2(function(a,b){var c=g.id(b.data("select2-data"));p(c,d)>=0&&(b.addClass("select2-selected"),b.find(".select2-result-selectable").addClass("select2-selected"))}),f.each2(function(a,b){b.is(".select2-result-selectable")||0!==b.find(".select2-result-selectable:not(.select2-selected)").length||b.addClass("select2-selected")}),-1==this.highlight()&&c!==!1&&g.highlight(0),!this.opts.createSearchChoice&&!e.filter(".select2-result:not(.select2-selected)").length>0&&(!a||a&&!a.more&&0===this.results.find(".select2-no-results").length)&&K(g.opts.formatNoMatches,"formatNoMatches")&&this.results.append("
  • "+L(g.opts.formatNoMatches,g.search.val())+"
  • ")},getMaxSearchWidth:function(){return this.selection.width()-t(this.search)},resizeSearch:function(){var a,b,c,d,e,f=t(this.search);a=D(this.search)+10,b=this.search.offset().left,c=this.selection.width(),d=this.selection.offset().left,e=c-(b-d)-f,a>e&&(e=c-f),40>e&&(e=c-f),0>=e&&(e=a),this.search.width(Math.floor(e))},getVal:function(){var a;return this.select?(a=this.select.val(),null===a?[]:a):(a=this.opts.element.val(),s(a,this.opts.separator))},setVal:function(b){var c;this.select?this.select.val(b):(c=[],a(b).each(function(){p(this,c)<0&&c.push(this)}),this.opts.element.val(0===c.length?"":c.join(this.opts.separator)))},buildChangeDetails:function(a,b){for(var b=b.slice(0),a=a.slice(0),c=0;c0&&c--,a.splice(d,1),d--);return{added:b,removed:a}},val:function(c,d){var e,f=this;if(0===arguments.length)return this.getVal();if(e=this.data(),e.length||(e=[]),!c&&0!==c)return this.opts.element.val(""),this.updateSelection([]),this.clearSearch(),d&&this.triggerChange({added:this.data(),removed:e}),void 0;if(this.setVal(c),this.select)this.opts.initSelection(this.select,this.bind(this.updateSelection)),d&&this.triggerChange(this.buildChangeDetails(e,this.data()));else{if(this.opts.initSelection===b)throw new Error("val() cannot be called if initSelection() is not defined");this.opts.initSelection(this.opts.element,function(b){var c=a.map(b,f.id);f.setVal(c),f.updateSelection(b),f.clearSearch(),d&&f.triggerChange(f.buildChangeDetails(e,f.data()))})}this.clearSearch()},onSortStart:function(){if(this.select)throw new Error("Sorting of elements is not supported when attached to instead.");this.search.width(0),this.searchContainer.hide()},onSortEnd:function(){var b=[],c=this;this.searchContainer.show(),this.searchContainer.appendTo(this.searchContainer.parent()),this.resizeSearch(),this.selection.find(".select2-search-choice").each(function(){b.push(c.opts.id(a(this).data("select2-data")))}),this.setVal(b),this.triggerChange()},data:function(b,c){var e,f,d=this;return 0===arguments.length?this.selection.children(".select2-search-choice").map(function(){return a(this).data("select2-data")}).get():(f=this.data(),b||(b=[]),e=a.map(b,function(a){return d.opts.id(a)}),this.setVal(e),this.updateSelection(b),this.clearSearch(),c&&this.triggerChange(this.buildChangeDetails(f,this.data())),void 0)}}),a.fn.select2=function(){var d,e,f,g,h,c=Array.prototype.slice.call(arguments,0),i=["val","destroy","opened","open","close","focus","isFocused","container","dropdown","onSortStart","onSortEnd","enable","disable","readonly","positionDropdown","data","search"],j=["opened","isFocused","container","dropdown"],k=["val","data"],l={search:"externalSearch"};return this.each(function(){if(0===c.length||"object"==typeof c[0])d=0===c.length?{}:a.extend({},c[0]),d.element=a(this),"select"===d.element.get(0).tagName.toLowerCase()?h=d.element.prop("multiple"):(h=d.multiple||!1,"tags"in d&&(d.multiple=h=!0)),e=h?new window.Select2["class"].multi:new window.Select2["class"].single,e.init(d);else{if("string"!=typeof c[0])throw"Invalid arguments to select2 plugin: "+c;if(p(c[0],i)<0)throw"Unknown method: "+c[0];if(g=b,e=a(this).data("select2"),e===b)return;if(f=c[0],"container"===f?g=e.container:"dropdown"===f?g=e.dropdown:(l[f]&&(f=l[f]),g=e[f].apply(e,c.slice(1))),p(c[0],j)>=0||p(c[0],k)&&1==c.length)return!1}}),g===b?this:g},a.fn.select2.defaults={width:"copy",loadMorePadding:0,closeOnSelect:!0,openOnEnter:!0,containerCss:{},dropdownCss:{},containerCssClass:"",dropdownCssClass:"",formatResult:function(a,b,c,d){var e=[];return F(a.text,c.term,e,d),e.join("")},formatSelection:function(a,c,d){return a?d(a.text):b},sortResults:function(a){return a},formatResultCssClass:function(a){return a.css},formatSelectionCssClass:function(){return b},formatMatches:function(a){return a+" results are available, use up and down arrow keys to navigate."},formatNoMatches:function(){return"No matches found"},formatInputTooShort:function(a,b){var c=b-a.length;return"Please enter "+c+" or more character"+(1==c?"":"s")},formatInputTooLong:function(a,b){var c=a.length-b;return"Please delete "+c+" character"+(1==c?"":"s")},formatSelectionTooBig:function(a){return"You can only select "+a+" item"+(1==a?"":"s")},formatLoadMore:function(){return"Loading more results\u2026"},formatSearching:function(){return"Searching\u2026"},minimumResultsForSearch:0,minimumInputLength:0,maximumInputLength:null,maximumSelectionSize:0,id:function(a){return a==b?null:a.id},matcher:function(a,b){return o(""+b).toUpperCase().indexOf(o(""+a).toUpperCase())>=0},separator:",",tokenSeparators:[],tokenizer:N,escapeMarkup:G,blurOnChange:!1,selectOnBlur:!1,adaptContainerCssClass:function(a){return a},adaptDropdownCssClass:function(){return null},nextSearchTerm:function(){return b},searchInputPlaceholder:"",createSearchChoicePosition:"top",shouldFocusInput:function(a){return a.opts.minimumResultsForSearch<0?!1:!0}},a.fn.select2.ajaxDefaults={transport:a.ajax,params:{type:"GET",cache:!1,dataType:"json"}},window.Select2={query:{ajax:H,local:I,tags:J},util:{debounce:w,markMatch:F,escapeMarkup:G,stripDiacritics:o},"class":{"abstract":d,single:e,multi:f}}}}(jQuery); \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/docs/js/sugar.min.js b/compare/thirdparty/tablesorter/docs/js/sugar.min.js new file mode 100644 index 00000000..838c30f1 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/js/sugar.min.js @@ -0,0 +1,142 @@ +/* + * Sugar v2.0.0 + * + * Freely distributable and licensed under the MIT-style license. + * Copyright (c) Andrew Plummer + * https://sugarjs.com/ + * + * ---------------------------- */ +(function(){'use strict';(function(){function sc(a,b){return new M(a,b)}function r(a,b){for(var c in a)if(z(a,c)&&!1===b.call(a,a[c],c,a))break}function tc(a){function b(a,b,c){A(d,a,function(a,e,m){a=Hb(a,e,m);ae(d,a.methods,b,c,a.w);return d})}var c="Object"===a,d=uc(a);b("defineStatic",1);b("defineInstance",2);b("defineInstanceAndStatic",3);b("defineStaticWithArguments",1,!0);b("defineInstanceWithArguments",2,!0);A(d,"defineStaticPolyfill",function(b,c,d){b=Hb(b,c,d);ib(Aa[a],b.methods,!0,b.w)});A(d,"defineInstancePolyfill", +function(b,c,g){b=Hb(b,c,g);ib(Aa[a].prototype,b.methods,!0,b.w);r(b.methods,function(a,b){jb(d,b,a)})});A(d,"alias",function(a,b){var c=d,h="string"===typeof b?d[b]:b;c[a]=h;h.instance&&jb(c,a,h.instance)});A(d,"extend",function(b){function f(a,c){var d=b[a];if(d)for(var f=0,g;g=d[f];f++)if(g===c)return!0;return!1}function g(a,c,d){if(!c[a]||!d)return!1;for(a=0;aa||!E(a)||!isFinite(a))throw new RangeError("Invalid number"); +return O(a)}function n(a){return void 0!==a}function v(a){return void 0===a}function Ra(a){var b="_sugar_"+a;return function(a,d){return 1b&&(b=+b+h.length),Pa||b||1===C)if(l=c?b in h:z(h,b),!e||u&&!f||l){if(d){if(u||!l)return l}else if(t&&u){if(la(h))throw new TypeError("Property cannot be written");h[b]=g}h=l?h[b]:void 0}else h=h[b]=m||f&&u?[]:{}}return h}}function qe(a,b,c,d){var e,f,g; +if(e=b.match(re)){b=n(d);a=(f=e[1])?Ba(a,f,c,!1,b?!0:!1,!0):a;if(!F(a))throw new TypeError("Array required");g=e[4];f=e[2]?+e[2]:0;e=e[3]?+e[3]:a.length;e=-1===e?a.length:e+1;if(b)for(b=f;b>>0==d&&4294967295!=d&&d>=b&&c.push(+d);c.sort(function(a,c){var d=a>b;return d!==c>b?d?-1:1:a-c});return c}function Ic(a,b,c,d){var e,f=a.length;if(!F(b))return Jc(a,b,f,c,d);e=Array(b.length);x(b,function(b,h){e[h]=Jc(a,b,f,c,d)});return e}function Pb(a,b,c){a&&c&&(a%=b);0>a&&(a=b+a);return a}function Jc(a,b,c,d,e){b=Pb(b,c,d);return e?a.charAt(b):a[b]}function ma(a,b,c,d){return b?b.apply?b.apply(c, +d||[]):F(b)?b.map(function(b){return ma(a,b,c,d)}):H(a[b])?a[b].call(a):ob(a,b):a}function x(a,b){for(var c=0,d=a.length;cb&&(d=1/d);return c(a*d)/d}function Da(a,b,c,d,e){d=X(a).toString(d||10);d=ub(e||"0",b-d.replace(/\.\d+/,"").length)+d;if(c||0>a)d=(0>a?"-":"+")+d;return d}function Kc(a){if(11<=a&&13>=a)return"th";switch(a%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}}function Rb(a,b){var c,d;c=a.replace(Lc,function(a){a=q(wa,a);"."===a&&(d=!0);return a});return d?parseFloat(c):parseInt(c,b||10)}function ub(a,b){var c="";for(a= +a.toString();0>=1)a+=a;return c}function Va(a){return a.charAt(0).toUpperCase()+a.slice(1)}function Mc(a,b,c){function d(d,e){var h,k,m,n,q=e[2],r=e[3],v=e[5];e[4]&&b?(k=v,h=b):q?(k=q,h=a):r&&b?m=r:m=e[1]||e[0];h&&(g(c,q,v),n=function(a,b){return h(a,k,b)});d.push(n||f(m))}function e(a,b,c,d){if(d>c){var e=b.slice(c,d);h(e,"{");h(e,"}");a.push(function(){return e})}}function f(a){return function(){return a}}function g(a,b,c){if(a&&!a(b,c))throw new TypeError("Invalid token "+ +(b||c)+" in format string");}function h(a,b){if(-1!==a.indexOf(b))throw new TypeError("Unmatched "+b+" in format string");}var k=we,m=xe(function(a){var b=[],c=0,f;for(k.lastIndex=0;f=k.exec(a);)e(b,a,c,f.index),d(b,f),c=k.lastIndex;e(b,a,c,a.length);return b});return function(a,b,c){a=m(a);for(var d="",e=0;e=c&&!1!==a(ha[b],b);b--);}function Qc(a,b){for(;0<=a&&!1!==b(ha[a],a);)a=Wa(a)}function Wa(a){return 6===a?4:5===a?3:a-1}function vb(a,b,c,d){Wb(function(c,d){var g;g=c.name;var h=q(a,Xb(a,g));n(h)&&b(g,h,c,d);g=void 0;!1!==g&&4===d&&(g=q(a,Xb(a,"weekday")),n(g)&&b("weekday",g,c,d),g=void 0);return g}, +c,d)}function Ce(a,b){vb(a,b,7,4)}function ya(a,b,c,d){var e={};e[b]=c;return qa(a,e,d,1)}function Rc(a,b,c){b=Pc(b,!0);return qa(a,b[0],b[1],c)}function Xa(a,b){pa(a,7*Ha((G(a)-b)/7)+b);return a}function Sc(a,b){var c=b-1;pa(a,7*ia((G(a)-c)/7)+c);return a}function Ya(a,b,c){5===b&&Xa(a,B.get(c).s());return T(a,Wa(b))}function wb(a,b,c,d){5===b&&Sc(a,B.get(c).s());return T(a,Wa(b),d,!0)}function T(a,b,c,d){Qc(b,function(b,f){var g=d?b.end:b.start;H(g)&&(g=g(a));Ga(a,b.method,g);return!n(c)||f>c}); +return a}function Xb(a,b){return Mb(a,b)||Mb(a,b+"s")||"day"===b&&Mb(a,"date")}function De(a){var b={},c;b[a]=1;vb(b,function(a,b,f,g){c=g;return!1});return c}function xb(a,b,c){var d=b>a,e;d||(e=b,b=a,a=e);e=b-a;1b)break;e+=1}return d?-e:e}function Tc(a,b){if(E(b)){var c=oa(a),d=G(a);T(c,6);ga(c,4);Xa(c,1);ga(c,Z(c)+7*(b-1));var e=fa(c);Ga(a,"FullYear",e);e=S(c);Ga(a,"Month",e);ga(a,Z(c));pa(a,d||7)}return a.getTime()} +function Ia(a,b,c,d){var e,f=0;v(c)&&(c=1);v(d)&&(d=4);var g=e=Sc(oa(a),c),h=c,k=d;T(g,6);ga(g,k);Xa(g,h);b&&ab&&(b=new Date(b.getTime()-10)));return Xc(a-b,function(c){return X(xb(a,b,c))})}function Wc(a,b,c){Nc(a);b=Yc[b]||b||"{long}";return yb(b,a,c)}function Yb(a,b){var c=J(a,"Hours");return B.get(b).ampm[O(c/12)]||""}function Zc(a,b,c){var d;if(!isNaN(a.getTime())){if(y(b))switch(b= +b.trim().toLowerCase(),!0){case "future"===b:return a.getTime()>xa().getTime();case "past"===b:return a.getTime()G(a);case "weekend"===b:return 0===G(a)||6===G(a);case n(d=za.weekdayMap[b]):return G(a)===d;case n(d=za.monthMap[b]):return S(a)===d}return $c(a,b,c)}}function $c(a,b,c,d,e){var f=0,g=0,h,k,m;K(a)&&(e=e||{},e.fromUTC=!0,e.setUTC=!0); +b=Za(null,b,e,!0);0= +k-f&&c<=m+g}function Zb(a,b){var c=xa();b&&ga(c,Z(c)+b);return fa(a)===fa(c)&&S(a)===S(c)&&Z(a)===Z(c)}function ja(a,b,c){return Za(null,a,b,c).date}function Za(a,b,c,d){function e(a,b){var c=q(C,"params")||{};x(b.to,function(b,d){var e=a[d+1],f;if(e){if("yy"===b||"y"===b){b="year";f=q(C,"prefer");var e=+e,g,e=e+(50>e?2E3:1900);f&&(g=e-fa(p),g/X(g)!==f&&(e+=100*f))}else(f=q($b,b))?(b=f.h||b,f.C?g=f.C:f.sign?g="+"===e?1:-1:f.W?g=!!g:g=+e.replace(/,/,"."),"month"===f.h&&--g,e=g):e=r.T(b,e);c[b]=e}}); +return c}function f(a,b){K(a)&&!n(q(C,"fromUTC"))&&(C.fromUTC=!0);K(a)&&!n(q(C,"setUTC"))&&(C.setUTC=!0);b&&(a=new Date(a.getTime()));return a}function g(){x(w,function(a){a.call()})}function h(a){a=a.toLowerCase();r=B.get(q(C,"locale"));for(var b=0,c,d;c=r.compiledFormats[b];b++)if(d=a.match(c.reg)){r.L(c,b);l=e(d,c);if(n(l.timestamp)){a=l.timestamp;l=null;break}n(l.ampm)&&(b=l.ampm,1===b&&12>l.hour?l.hour+=12:0===b&&12===l.hour&&(l.hour=0));if(l.utc||n(l.tzHour))if(b=l.tzHour,c=l.tzMinute,d=l.tzSign, +K(p,!0),b=(d||1)*(60*(b||0)+(c||0)))l.minute=(l.minute||0)-b;n(l.shift)&&v(l.unit)&&(n(l.month)?l.unit=7:n(l.weekday)&&(l.unit=5));n(l.num)&&v(l.unit)&&(n(l.weekday)?t(l.num):n(l.month)&&(l.date=l.num));l.midday&&k(l.midday);n(l.day)&&(T(p,3),v(l.unit)&&(l.unit=4,l.num=l.day,delete l.day));n(l.unit)&&(b=l.unit,c=n(l.num)?l.num:1,n(l.weekday)&&(6===b?(t(c),c=1):(qa(p,{weekday:l.weekday},!0),delete l.weekday)),l.half&&(c*=l.half),n(l.shift)?c*=l.shift:l.sign&&(c*=l.sign),n(l.day)&&(c+=l.day,delete l.day), +u(b),l[za.units[b]]=c,A=!0);l.edge&&m(l.edge,l);l.yearSign&&(l.year*=l.yearSign);break}l?A?qa(p,l,!1,1):(K(p)&&T(p,3),qa(p,l,!0,0,q(C,"prefer"),Sb)):(p=new Date(a),q(C,"fromUTC")&&p.setTime(p.getTime()+6E4*p.getTimezoneOffset()));g();return p}function k(a){l.hour=a%24;23a?Ya(p,c,q(C,"locale")): +0=a)return p.setTime(NaN),!1;fb.specificity||(b.specificity=p);if(n=g%1){if(p){var q=ha[Wa(p)];n=Ua(h.f/q.f*n);b[q.name]=n}g=O(g)}if("weekday"===c)d||pa(a,g,f);else if(c=6===p&&28g&&(g=g%12+12),h=g%12!==S(a);h&&ga(a,0)}}var k;if(E(b)&&d)b={millisecond:b};else if(E(b))return a.setTime(b),a;vb(b,h);c&&b.specificity&&T(a,Wa(b.specificity));a:{if(k&&!(7xa();break a;case 1:c=a=p[1]&&c<=(p[2]||p[1])})),a=zb(e)));if(!a)return"";u?a=U(a):(b.push(l),a="("+a+")");c&&(a=Ge(l,a,c));t&&(a+="?");return a}function f(a){a=a.replace(/ /g," ?");return a.replace(/\{([^,]+?)\}/g, +function(a,b){var c=b.split("|");return 1]+";return jd(a.toString(),RegExp("<(\\/)?("+ +b+")(\\s+[^<>]*?)?\\s*(\\/)?>","gi"),d,c)}function jd(a,b,c,d,e){function f(f,g,m,l,p){var t=a.slice(k,f),u="",n="";y(d)?n=d:d&&(n=d.call(e,g,t,m,e)||"");c?u=n:t=n;t&&(t=jd(t,b,c,d,e));h+=u+t+(p?"":u);k=f+(l||0)}var g,h="",k=0,m,t,u=0;e=e||a;for(b=RegExp(b.source,"gi");g=b.exec(a);){var p=g[2],l=(g[3]||"").slice(1),n=!!g[1],q=!!g[4],r=g[0].length,v=-1!==Ob(Se,p.toLowerCase()),q=!n&&!q&&!v,w=p===m;m||(h+=a.slice(k,g.index),k=g.index);q?m?w&&u++:(m=p,t=l,u++,k+=r):n&&w?(u--,0===u&&(f(g.index,m,t,r, +v),t=m=null)):m||f(g.index,p,l,r,v)}m&&f(a.length,m,t);return h+=a.slice(k)}function kd(a,b,c){y(b)&&(b=a.indexOf(b),-1===b&&(b=c?a.length:0));return b}function ld(a,b){var c;a&&a.constructor&&F(a.constructor.prototype)?c=b?Ja(a):a:I(a)||y(a)?c=Array.from(a):n(a)&&(c=[a]);return c||[]}function Ja(a){var b=Array(a.length);x(a,function(a,d){b[d]=a});return b}function Te(a){var b=[];b.push(a);return b}function Ue(a,b){var c=Ja(a),d=c.length,e;e=F(b)?b:[b];c.length+=e.length;x(e,function(a,b){c[d+b]= +a});return c}function md(a,b,c){c=+c;isNaN(c)&&(c=a.length);c=[c,0];n(b)&&(c=c.concat(b));a.splice.apply(a,c);return a}function nd(a,b){var c=[],d={},e=[];x(a,function(f,g){var h=b?ma(f,b,a,[f,g,a]):f,h=Ca(h,e);z(d,h)||(c.push(f),d[h]=!0)});return c}function od(a,b,c){var d=[];b=b||Infinity;c=c||0;x(a,function(a){F(a)&&cb.length)return 1;for(d=0;db?1:0}function rd(a,b,c){y(a)||(a= +String(a));c&&(a=a.toLowerCase());b&&(a=a.replace(b,""));return a}function sd(a,b,c){a=a.charAt(b);return q(c,a)||a}function bc(a,b,c,d,e){if(F(a)){for(var f,g,h=[],k=0,m=a.length;kh||!d&&b>>0!=f||4294967295==f)return;f=+f}var g=ma(e,b,a,[e,f,a]);c(g,f)})}function Ed(a,b){if(H(a))return a;if(a)return function(c, +d,e){return ma(c,a,b,[c,d,e])}}function ra(a){var b;if(H(a))return a;b=da(a);return function(a,d,e){return b(a,d,e)}}function Ma(a,b){var c=Array.prototype[a];return function(a,e,f,g){var h=Array(2);Dc(0h?-1:1,c=e[h+g];"|"===c&&(c="");-9>f&&(b=X(f)-9);d=d?tb(2,10*h):tb(10,3*h);return Fd(Ta(a/d,b||0))+c}function Fd(a,b){var c="",d,e,f,g,h;e=Gd("decimal");d=Gd("thousands");h=E(b)?Ta(a,b||0).toFixed(D(b,0)):a.toString();h=h.replace(/^-/,"");f=h.split(".");g=f[0];f=f[1];if(/e/.test(h))c=h;else for(h=g.length;0a?"-":"")+c}function jc(a){return function(b,c){return c?Ta(b,c,a):a(b)}}function Na(a,b, +c,d,e){b=ua(b||0);Db(a)||Db(a,[]);kc(a,!1);Db(a).push(setTimeout(function(){kc(a)||c.apply(d,e||[])},b))}function Hd(a){var b=Db(a),c;if(F(b))for(;c=b.shift();)clearTimeout(c);kc(a,!0);return a}function Id(a,b,c,d){function e(){if(f.lengthc;)t=Function.prototype.apply.apply(a, +f.shift()),b--;Na(e,k,function(){g=!1;h()})}};return e}function nf(){for(var a=arguments,b=a.length,c=Array(b);b--;)c[b]=a[b];return c}function of(a,b,c){var d={},e=[],f=0;return function(){var g=b.apply(this,arguments),g=Ca(g,e);if(z(d,g))return q(d,g);f===c&&(d={},e=[],f=0);f++;return d[g]=a.apply(this,arguments)}}function M(a,b){this.start=lc(a);this.end=lc(b)}function Jd(a){return y(a)?a.charCodeAt(0):a}function Kd(a){return null==a?a:Q(a)?a.getTime():a.valueOf()}function Ld(a){a=a.toString().split("."); +return a[1]?a[1].length:0}function lc(a){return Q(a)?new Date(a.getTime()):Kd(a)}function Md(a){var b=Kd(a);return(!!b||0===b)&&-Infinity!==a&&Infinity!==a}function Eb(a){return Md(a.start)&&Md(a.end)&&typeof a.start===typeof a.end}function Fb(a,b,c,d){var e,f,g,h=a.start,k=a.end,m=k=k:t<=k;)c||p.push(t),d&&d(t,u,a),t=e(),u++;return c?u-1:p}function Nd(a){var b;if(E(a))return[a,"Milliseconds"];b=a.match(pf);a=+b[1]||1;b=Va(b[2].toLowerCase());b.match(/hour|minute|second/i)?b+="s":"Year"===b?b="FullYear":"Week"===b?(b="Date",a*=7):"Day"===b&&(b="Date");return[a,b]}function Od(a,b,c){var d=Pd[c];d?d=new Date(a.getTime()+b*d):(d=new Date(a),Ga(d,c,J(a,c)+b));return d}function mc(a,b){var c=a.start,d=a.end,e=dd?c:d;return lc(bc?c: +b)}function Oa(a){return Q(a)?a:null==a?new Date:P.create?P.create(a):new Date(a)}var w,Aa="undefined"!==typeof global&&global.Object===Object?global:this,qf="undefined"!==typeof module&&module.M,Ib=!1,kb={},vc={},Jb=Object.defineProperty&&Object.defineProperties?Object.defineProperty:he,yc=uc("Chainable"),zc=Object.getOwnPropertyNames,Ac=Object.prototype.toString,ie=Object.prototype.hasOwnProperty;(function(){w=Aa.Sugar;if(!w){w=function(a){r(w,function(b,c){z(kb,c)&&b.extend(a)});return w};if(qf)module.M= +w;else try{Aa.Sugar=w}catch(a){}r("Object Number String Array Date RegExp Function".split(" "),function(a){tc(a)});A(w,"extend",w);A(w,"toString",ce);A(w,"createNamespace",tc);A(w,"util",{hasOwn:z,getOwn:q,setProperty:A,classToString:V,defineProperty:Jb,forEachProperty:r,mapNativeToChainable:wc})}})();var se=!("0"in Object("a")),re=/^(.*?)\[([-\d]*)\.\.([-\d]*)\](.*)$/,we=/([{}])\1|\{([^}]*)\}|(%)%|(%(\w*))/g,eb=w.Object,R=w.Array,P=w.Date,fb=w.String,ba=w.Number,Qd=w.Function,Rd=w.RegExp,pb,La,E, +y,Q,ea,H,F,qb,rb,sb,Sd=ta("alias"),sa=ta("defineStatic"),N=ta("defineInstance"),Td=ta("defineStaticPolyfill"),nc=ta("defineInstancePolyfill"),Kb=ta("defineInstanceAndStatic"),gb=ta("defineInstanceWithArguments"),O=Math.trunc||function(a){return 0!==a&&isFinite(a)?0>a?ia(a):Ha(a):a},Lc,wa,dd,X=Math.abs,tb=Math.pow,aa=Math.min,D=Math.max,ia=Math.ceil,Ha=Math.floor,Ua=Math.round,ka=String.fromCharCode,Y={},K=Ra("utc"),ze=1E3;(function(){function a(a){f["[object "+a+"]"]=!0}function b(a,b){return b&& +lb(new b,"Object")?c(b):d(a)}function c(a){var b=String(a);return function(a){return String(a.constructor)===b}}function d(a){return function(b,c){return lb(b,a,c)}}function e(a){var b=a.toLowerCase();return function(c){var d=typeof c;return d===b||"object"===d&&lb(c,a)}}var f={};(function(){var c="Boolean Number String Date RegExp Function Array Error Set Map".split(" ");La=e(c[0]);E=e(c[1]);y=e(c[2]);Q=b(c[3]);ea=b(c[4]);H=b(c[5]);F=Array.isArray||b(c[6]);sb=b(c[7]);qb=b(c[8],"undefined"!==typeof Set&& +Set);rb=b(c[9],"undefined"!==typeof Map&&Map);a("Arguments");a(c[0]);a(c[1]);a(c[2]);a(c[3]);a(c[4]);a(c[6])})();(function(){x("Int8 Uint8 Uint8Clamped Int16 Uint16 Int32 Uint32 Float32 Float64".split(" "),function(b){a(b+"Array")})})();pb=function(a,b){b=b||V(a);return f[b]||nb(a,b)}})();(function(){var a="";wa={};for(var b=0,c;9>=b;b++)c=ka(b+65296),a+=c,wa[c]=ka(b+48);wa[","]="";wa["\uff0e"]=".";wa["."]=".";Lc=RegExp("["+(a+"\uff0e,.")+"]","g");dd=a})();nc(fb,{includes:function(a){var b=arguments[1], +c=Tb(this);a=Ub(a);return-1!==c.indexOf(a,b)},startsWith:function(a){var b=arguments[1],c,d,e;c=Tb(this);a=Ub(a);d=c.length;b=aa(D(+b||0,0),d);e=a.length;return e+b>d?!1:c.substr(b,e)===a?!0:!1},endsWith:function(a){var b=arguments[1],c,d,e;c=Tb(this);a=Ub(a);d=e=c.length;n(b)&&(d=+b||0);d=aa(D(d,0),e);b=a.length;d-=b;return 0>d?!1:c.substr(d,b)===a?!0:!1},repeat:function(a){a=ua(a);return ub(this,a)}});Td(ba,{isNaN:function(a){return null!=a&&a!==a}});Td(R,{from:function(a){var b=arguments[1],c= +arguments[2],d,e;n(b)&&mb(b);if(null==a)throw new TypeError("Object required.");a=Sa(a);d=O(D(0,a.length||0));if(d>>>0!=d||4294967295==d)throw new RangeError("Invalid array length");H(this)?(e=new this(d),e.length=d):e=Array(d);for(var f=0;fb&&(b=D(0,b+c));bja(b).getTime()-(c||0)},isBefore:function(a,b,c){return a.getTime()=a},isLeapYear:function(a){a= +fa(a);return 0===a%4&&0!==a%100||0===a%400},daysInMonth:function(a){return Oc(a)},format:function(a,b,c){return Wc(a,b,c)},relative:function(a,b,c){return Vc(a,null,b,c)},relativeTo:function(a,b,c){return Vc(a,ja(b),c)},is:function(a,b,c){return Zc(a,b,c)},reset:function(a,b,c){b=b?De(b):4;Ya(a,b,c);return a},clone:function(a){return oa(a)},iso:function(a){return a.toISOString()},getWeekday:function(a){return G(a)},getUTCWeekday:function(a){return a.getUTCDay()}});N(ba,{duration:function(a,b){return B.get(b).N(a)}}); +var oc={code:"en",plural:!0,timeMarkers:"at",ampm:"AM|A.M.|a,PM|P.M.|p",units:"millisecond:|s,second:|s,minute:|s,hour:|s,day:|s,week:|s,month:|s,year:|s",months:"Jan:uary|,Feb:ruary|,Mar:ch|,Apr:il|,May,Jun:e|,Jul:y|,Aug:ust|,Sep:tember|t|,Oct:ober|,Nov:ember|,Dec:ember|",weekdays:"Sun:day|,Mon:day|,Tue:sday|,Wed:nesday|,Thu:rsday|,Fri:day|,Sat:urday|+weekend",numerals:"zero,one|first,two|second,three|third,four:|th,five|fifth,six:|th,seven:|th,eight:|h,nin:e|th,ten:|th",articles:"a,an,the",tokens:"the,st|nd|rd|th,of|in,a|an,on", +time:"{H}:{mm}",past:"{num} {unit} {sign}",future:"{num} {unit} {sign}",duration:"{num} {unit}",modifiers:[{name:"half",src:"half",value:.5},{name:"midday",src:"noon",value:12},{name:"midday",src:"midnight",value:24},{name:"day",src:"yesterday",value:-1},{name:"day",src:"today|tonight",value:0},{name:"day",src:"tomorrow",value:1},{name:"sign",src:"ago|before",value:-1},{name:"sign",src:"from now|after|from|in|later",value:1},{name:"edge",src:"first day|first|beginning",value:-2},{name:"edge",src:"last day", +value:1},{name:"edge",src:"end|last",value:2},{name:"shift",src:"last",value:-1},{name:"shift",src:"the|this",value:0},{name:"shift",src:"next",value:1}],parse:"(?:just)? now;{shift} {unit:5-7};{months?} (?:{year}|'{yy});{midday} {4?} {day|weekday};{months},?(?:[-.\\/\\s]{year})?;{edge} of (?:day)? {day|weekday};{0} {num}{1?} {weekday} {2} {months},? {year?};{shift?} {day?} {weekday?} {timeMarker?} {midday};{sign?} {3?} {half} {3?} {unit:3-4|unit:7} {sign?};{0?} {edge} {weekday?} {2} {shift?} {unit:4-7?} {months?},? {year?}".split(";"), +timeParse:"{day|weekday};{shift} {unit:5?} {weekday};{0?} {date}{1?} {2?} {months?};{weekday} {2?} {shift} {unit:5};{0?} {num} {2?} {months}\\.?,? {year?};{num?} {unit:4-5} {sign} {day|weekday};{year}[-.\\/\\s]{months}[-.\\/\\s]{date};{0|months} {date?}{1?} of {shift} {unit:6-7};{0?} {num}{1?} {weekday} of {shift} {unit:6};{date}[-.\\/\\s]{months}[-.\\/\\s](?:{year}|'?{yy});{weekday?}\\.?,? {months}\\.?,? {date}{1?},? (?:{year}|'{yy})?".split(";"),timeFrontParse:["{sign} {num} {unit}","{num} {unit} {sign}", +"{4?} {day|weekday}"]},Ud=W(W({},oc),{mdy:!0,firstDayOfWeek:0,firstDayOfWeekYear:1,"short":"{MM}/{dd}/{yyyy}",medium:"{Month} {d}, {yyyy}","long":"{Month} {d}, {yyyy} {time}",full:"{Weekday}, {Month} {d}, {yyyy} {time}",stamp:"{Dow} {Mon} {d} {yyyy} {time}",time:"{h}:{mm} {TT}"}),Vd=W(W({},oc),{"short":"{dd}/{MM}/{yyyy}",medium:"{d} {Month} {yyyy}","long":"{d} {Month} {yyyy} {H}:{mm}",full:"{Weekday}, {d} {Month}, {yyyy} {time}",stamp:"{Dow} {d} {Mon} {yyyy} {time}"}),tf=W(W({},oc),{"short":"{yyyy}-{MM}-{dd}", +medium:"{d} {Month}, {yyyy}","long":"{d} {Month}, {yyyy} {H}:{mm}",full:"{Weekday}, {d} {Month}, {yyyy} {time}",stamp:"{Dow} {d} {Mon} {yyyy} {time}"}),Wd={"en-US":Ud,"en-GB":Vd,"en-AU":Vd,"en-CA":tf};(function(){function a(a){this.o={};this.add(a)}a.prototype={get:function(a,c){var d=this.o[a];!d&&Wd[a]?d=this.add(a,Wd[a]):!d&&a&&(d=this.o[a.slice(0,2)]);return d||!1===c?d:this.current},getAll:function(){return this.o},set:function(a){var c=this.get(a,!1);if(!c)throw new TypeError("Invalid Locale: "+ +a);return this.current=c},add:function(a,c){c?c.code=a:(c=a,a=c.code);var d=c.compiledFormats?c:cd(c);this.o[a]=d;this.current||(this.current=d);return d},remove:function(a){this.current.code===a&&(this.current=this.get("en"));return delete this.o[a]}};za=cd(Ud);B=new a(za)})();(function(){function a(a,b,c){b&&x(b.split(" "),function(b){a[b]=c})}function b(a){return function(b,c){return a(b,c).toLowerCase()}}function c(a){return function(b,c){var d=a(b,c);return d+B.get(c).R(d)}}function d(a,b){return function(c, +d){return Da(a(c,d),b)}}function e(a){return function(b,c){return a(b,c)%100}}function f(a){return function(b,c){return yb(a,b,c)}}function g(c,d){function e(a,b){return c.get(a,b,d)}a(L,c.b+d,e);c.j&&(L[c.j+d]=b(e))}function h(a){return function(b,c){var d=B.get(c);return yb(d[a],b,c)}}L={};hb={};x(sf,function(h){var m=h.get,t;h.j&&(L[h.j]=b(m));h.l&&(L[h.l]=c(m));h.c&&(L[h.c]=d(m,h.c.length));h.u&&(L[h.u]=d(e(m),2));h.B&&(hb[h.B]=d(e(m),2));h.g&&(t=d(m,h.g));h.alias&&(m=f(h.alias));if(h.D)for(var n= +1;5>=n;n++)g(h,n);a(L,h.b,m);a(hb,h.a,t||m)});r(Yc,function(b,c){a(L,c,f(b))});Qa(P,"short medium long full",function(b,c){var d=h(c);a(L,c,d);b[c]=d});a(L,"time",h("time"));a(L,"stamp",h("stamp"))})();(function(){yb=Mc(function(a,b,c){return q(L,b)(a,c)},function(a,b,c){return q(hb,b)(a,c)},function(a,b){return z(L,a)||z(hb,b)})})();(function(){Qa(P,ha,function(a,b,c){var d=b.name,e=Va(d);4]/g,gd={lt:"<",gt:">",amp:"&", +nbsp:" ",quot:'"',apos:"'"},pc,Oe="and or nor a an the so but to of at by from into on onto off out in over with for".split(" "),Se="area base br col command embed hr img input keygen link meta param source track wbr".split(" "),vf=RegExp("^[\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u2028\u2029\u3000\ufeff]+"),wf=RegExp("[\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u2028\u2029\u3000\ufeff]+$"), +Pe=RegExp("(?=[\t\n\x0B\f\r \u00a0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u2028\u2029\u3000\ufeff])"),xf=String.prototype.includes,qc,rc,yf=Mc(ob);N(fb,{includes:ca(function(a,b,c){if(!ea(b))return xf.call(a,b,c);c&&(a=a.slice(c));return b.test(a)})},["enhance","enhanceString"]);N(fb,{at:function(a,b,c){return Ic(a,b,c,!0)},escapeURL:function(a,b){return b?encodeURIComponent(a):encodeURI(a)},unescapeURL:function(a,b){return b?decodeURI(a):decodeURIComponent(a)}, +escapeHTML:function(a){return a.replace(uf,function(a){return q(pc,a)})},unescapeHTML:function(a){return Qe(a)},stripTags:function(a,b,c){return id(a,b,c,!0)},removeTags:function(a,b,c){return id(a,b,c,!1)},encodeBase64:function(a){return qc(a)},decodeBase64:function(a){return rc(a)},forEach:function(a,b,c){return ab(a,b,c)},chars:function(a,b,c){return ab(a,b,c)},words:function(a,b){return ab(a.trim(),/\S+/g,b)},lines:function(a,b){return ab(a.trim(),/^.*$/gm,b)},codes:function(a,b){return fd(a, +b)},shift:function(a,b){var c="";b=b||0;fd(a,function(a){c+=ka(a+b)});return c},isBlank:function(a){return 0===a.trim().length},isEmpty:function(a){return 0===a.length},insert:function(a,b,c){c=v(c)?a.length:c;return a.slice(0,c)+b+a.slice(c)},remove:function(a,b){return a.replace(b,"")},removeAll:function(a,b){return hd(a,b)},reverse:function(a){return a.split("").reverse().join("")},compact:function(a){return a.trim().replace(/([\r\n\s\u3000])+/g,function(a,c){return"\u3000"===c?c:" "})},from:function(a, +b){return a.slice(kd(a,b,!0))},to:function(a,b){v(b)&&(b=a.length);return a.slice(0,kd(a,b))},dasherize:function(a){return bb(a).replace(/_/g,"-")},underscore:function(a){return bb(a)},camelize:function(a,b){return Ke(a,b)},spacify:function(a){return bb(a).replace(/_/g," ")},titleize:function(a){return Ne(a)},parameterize:function(a,b){var c,d=b;void 0===d&&(d="-");c=a.replace(/[^a-z0-9\-_]+/gi,d);d&&(d=RegExp(["^","+|","+$|(",")","+"].join(Fa(d)),"g"),c=c.replace(d,"$1"));return encodeURI(c.toLowerCase())}, +truncate:function(a,b,c,d){return ed(a,b,c,d)},truncateOnWord:function(a,b,c,d){return ed(a,b,c,d,!0)},pad:function(a,b,c){var d;b=ua(b);d=D(0,b-a.length)/2;b=Ha(d);d=ia(d);return Ab(b,c)+a+Ab(d,c)},padLeft:function(a,b,c){b=ua(b);return Ab(D(0,b-a.length),c)+a},padRight:function(a,b,c){b=ua(b);return a+Ab(D(0,b-a.length),c)},first:function(a,b){v(b)&&(b=1);return a.substr(0,b)},last:function(a,b){v(b)&&(b=1);return a.substr(0>a.length-b?0:a.length-b)},toNumber:function(a,b){return Rb(a,b)},capitalize:function(a, +b,c){return ac(a,b,c)},trimLeft:function(a){return a.replace(vf,"")},trimRight:function(a){return a.replace(wf,"")}});gb(fb,{replaceAll:function(a,b,c){return hd(a,b,c)},format:function(a,b){var c=b[0]&&b[0].valueOf();1===b.length&&I(c)&&(b=c);return yf(a,b)}});(function(){function a(a){return function(b){try{return a(b)}catch(c){return""}}}var b,c;if("undefined"!==typeof Buffer)qc=function(a){return(new Buffer(a)).toString("base64")},rc=function(a){return(new Buffer(a,"base64")).toString("utf8")}; +else{if("undefined"!==typeof btoa)b=a(btoa),c=a(atob);else{var d=/[^A-Za-z0-9\+\/\=]/g;b=function(a){var b="",c,d,k,m,n,q,p=0;do c=a.charCodeAt(p++),d=a.charCodeAt(p++),k=a.charCodeAt(p++),m=c>>2,c=(c&3)<<4|d>>4,n=(d&15)<<2|k>>6,q=k&63,isNaN(d)?n=q=64:isNaN(k)&&(q=64),b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(m),b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(c),b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(n), +b+="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(q);while(p>4,h=(h&15)<<4|m>>2,k=(m&3)<<6|n,b+=ka(c),64!=m&&(b+=ka(h)),64!=n&&(b+=ka(k));while(q=c||65296<=c&&65305>=c)&&(48<=d&&57>=d||65296<=d&&65305>=d)&&(c=Rb(a.slice(g)),d=Rb(b.slice(g)))):(e=e!==a.charAt(g),f= +f!==b.charAt(g),e!==f&&0===h&&(h=e-f)),g+=1;while(null!=c&&null!=d&&c===d);return c===d?h:c-d},sortEquivalents:function(){var a={};x("A\u00c1\u00c0\u00c2\u00c3\u00c4 C\u00c7 E\u00c9\u00c8\u00ca\u00cb I\u00cd\u00cc\u0130\u00ce\u00cf O\u00d3\u00d2\u00d4\u00d5\u00d6 S\u00df U\u00da\u00d9\u00db\u00dc".split(" "),function(b){var c=b.charAt(0);x(b.slice(1).split(""),function(b){a[b]=c;a[b.toLowerCase()]=c.toLowerCase()})});return a}()},Ka=Lb(R,Af);sa(R,{create:function(a,b){return ld(a,b)},construct:function(a, +b){a=ua(a);return Array.from(Array(a),function(a,d){return b&&b(d)})}});N(R,{isEmpty:function(a){return 0===a.length},isEqual:function(a,b){return va(a,b)},clone:function(a){return Ja(a)},at:function(a,b,c){return Ic(a,b,c)},add:function(a,b,c){return md(Ja(a),b,c)},subtract:function(a,b){return pd(a,b,!0)},append:function(a,b,c){return md(a,b,c)},removeAt:function(a,b,c){if(v(b))return a;v(c)&&(c=b);a.splice(b,c-b+1);return a},unique:function(a,b){return nd(a,b)},flatten:function(a,b){return od(a, +b)},first:function(a,b){if(v(b))return a[0];0>b&&(b=0);return a.slice(0,b)},last:function(a,b){return v(b)?a[a.length-1]:a.slice(0>a.length-b?0:a.length-b)},from:function(a,b){return a.slice(b)},to:function(a,b){v(b)&&(b=a.length);return a.slice(0,b)},compact:function(a,b){return Ve(a,b)},groupBy:function(a,b,c){return We(a,b,c)},inGroups:function(a,b,c){var d=n(c),e=Array(b),f=ia(a.length/b);Nb(b,function(b){var h=b*f,k=a.slice(h,h+f);d&&k.lengthb&&(b+=f);b=n(b,f);Dc(c.length);r=p?p(r,e):r;d.push(k(a,b,r,e,f,g));e&&d.push(e);a=h.apply(m(a,b,g),d);q&&(a=q(a,b,f));return a}}r({forEach:{i:function(a){x(this,a)}},map:{v:Ed},"some every":{v:ra},findIndex:{v:ra,result:function(a,b,c){-1!==a&&(a=(a+b)%c);return a}}, +reduce:{apply:c},"filter find":{v:ra},reduceRight:{apply:c,slice:function(a,b,c){c||(a=a.slice(0,D(0,b+1)));return a},clamp:function(a,b){return aa(b,D(-1,a))}}},function(a,b){x(b.split(" "),function(b){gb(R,b+"FromIndex",e(b,a))})})})();var Gd=Lb(ba,{decimal:".",thousands:","});sa(ba,{random:function(a,b){var c,d;1==arguments.length&&(b=a,a=0);c=aa(a||0,v(b)?1:b);d=D(a||0,v(b)?1:b)+1;return O(Math.random()*(d-c)+c)}});N(ba,{isInteger:function(a){return 0===a%1},isOdd:function(a){return 0===a%1&& +0!==a%2},isEven:function(a){return 0===a%2},isMultipleOf:function(a,b){return 0===a%b},log:function(a,b){return Math.log(a)/(b?Math.log(b):1)},abbr:function(a,b){return ic(a,b,"|kmbt")},metric:function(a,b,c){"all"===c?c="yzafpn\u03bcm|KMGTPEZY":c||(c="n\u03bcm|k");return ic(a,b,c)},bytes:function(a,b,c,d){"binary"===d||!d&&c?d="|,Ki,Mi,Gi,Ti,Pi,Ei":"si"!==d&&d||(d="|KMGTPE");return ic(a,b,d,c)+"B"},format:function(a,b){return Fd(a,b)},hex:function(a,b){return Da(a,b||1,!1,16)},times:function(a,b){for(var c, +d,e=0;e=b)return a.call(this,d)}},once:function(a){var b= +!1,c;return function(){if(b)return c;b=!0;return c=a.apply(this,arguments)}},memoize:function(a,b,c){var d,e;E(b)||(d=b,b=c);y(d)?(e=d,d=function(a){return ob(a,e)}):d||(d=nf);return of(a,d,b)},lock:function(a,b){var c;if($d(a))return Gb(a,E(b)?b:null),a;c=function(){arguments.length=aa(Gb(c),arguments.length);return a.apply(this,arguments)};Gb(c,E(b)?b:a.length);return c}});gb(Qd,{partial:function(a,b){function c(){var e=0,f=[],g=this,h=Gb(c),k;for(k=0;k= +this.start&&a.start<=this.end&&a.end>=this.start&&a.end<=this.end:a>=this.start&&a<=this.end},every:function(a,b){return Fb(this,a,!1,b)},toArray:function(){return Fb(this)},union:function(a){return new M(this.starta.end?this.end:a.end)},intersect:function(a){return a.start>this.end||a.enda.start?this.start:a.start,this.endc?f=function(){return Fb(this,b,!0)}:(e=Pd[Va(d)],f=function(){return O((this.end- +this.start)/e)});a[d]=f});Cc(M,a)})()}).call(this);}).call(this); diff --git a/compare/thirdparty/tablesorter/docs/themes.html b/compare/thirdparty/tablesorter/docs/themes.html new file mode 100644 index 00000000..80be5445 --- /dev/null +++ b/compare/thirdparty/tablesorter/docs/themes.html @@ -0,0 +1,263 @@ + + + + + jQuery tablesorter 2.4 - Themes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +

    Blackice

    +
    (blackice)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Blue

    +
    (blue)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Dark

    +
    (dark)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Default

    +
    (default)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Green

    +
    (green)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Grey

    +
    (grey)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Ice

    +
    (ice)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Bootstrap

    +
    (bootstrap)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    jQuery UI

    +
    (jui)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Dropbox

    +
    (dropbox)
    + + + + + + + + + + +
    ABCD
    ABCD
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    +

    Metro Dark

    +
    (metro-dark)
    + + + + + + + + + + + + + + + + +
    Title
    ABCD
    ABCD
     
    1234
    1415165
    1320176
    1219187
    111098
    +
    + +
    + + + diff --git a/compare/thirdparty/tablesorter/example.json b/compare/thirdparty/tablesorter/example.json new file mode 100644 index 00000000..4eebfd46 --- /dev/null +++ b/compare/thirdparty/tablesorter/example.json @@ -0,0 +1,6 @@ +{ + "widgets" : "filter stickyHeaders uitheme", + "parsers" : "ignore-articles network", + "includeDependencies" : false, + "destFileName" : "jquery.tablesorter.custom-widgets.js" +} diff --git a/compare/thirdparty/tablesorter/index.html b/compare/thirdparty/tablesorter/index.html new file mode 100644 index 00000000..a4c0fc6e --- /dev/null +++ b/compare/thirdparty/tablesorter/index.html @@ -0,0 +1,62 @@ + + + + + Basic Tablesorter Demo + + + + + + + + + + + + + + + +
    +

    tablesorter

    +

    By Christian Bach; github updates by Rob G
    + Complete docs included (updated with missing docs from this blog post) +

    + + + + + + + + + + + + + + + + + + + + + + +
    AlphaNumeric SortCurrencyAlphabeticalSites
    abc 123£10,40Koalahttp://www.google.com
    abc 1£234,10Oxhttp://www.yahoo.com
    abc 9£10,33Girafeehttp://www.facebook.com
    zyx 24£10Bisonhttp://www.whitehouse.gov/
    abc 11£3,20Chimphttp://www.ucla.edu/
    abc 2£56,10Elephanthttp://www.wikipedia.org/
    abc 9£3,20Lionhttp://www.nytimes.com/
    ABC 10£87,00Zebrahttps://github.com
    zyx 1£99,90Koalahttp://www.mit.edu/
    zyx 12£234,10Llamahttp://www.nasa.gov/
    + +

    This is a quick demo of the columns & zebra widget with the sortReset option set to true (clicking to sort a third time will reset the sort) using jQuery v1.2.6.

    + +
    + \ No newline at end of file diff --git a/compare/thirdparty/tablesorter/js/extras/jquery.dragtable.mod.js b/compare/thirdparty/tablesorter/js/extras/jquery.dragtable.mod.js new file mode 100644 index 00000000..fe86683a --- /dev/null +++ b/compare/thirdparty/tablesorter/js/extras/jquery.dragtable.mod.js @@ -0,0 +1,602 @@ +/*! Dragtable Mod for TableSorter - updated 10/31/2015 (v2.24.0) *//* + * Requires + * tablesorter v2.8+ + * jQuery 1.7+ + * jQuery UI (Core, Widget, Mouse & Sortable) + * Dragtable by Akottr (https://github.com/akottr) modified by Rob Garrison + */ +/*jshint browser:true, jquery:true, unused:false */ +/*global jQuery: false */ +;(function( $ ) { +'use strict'; + var undef, + ts = $.tablesorter; + + ts.dragtable = { + create : function( _this ) { + var hasAccept, + $table = _this.originalTable.el, + handle = _this.options.dragHandle.replace('.', ''); + $table.children('thead').children().children('th,td').each(function(){ + var $this = $(this); + if ( !$this.find( _this.options.dragHandle + ',.' + handle + '-disabled' ).length ) { + hasAccept = _this.options.dragaccept ? $this.hasClass( _this.options.dragaccept.replace('.', '') ) : true; + $this + // sortClass includes a "." to match the tablesorter selectorSort option - for consistency + .wrapInner('
    ') + // add handle class + "-disabled" to drag-disabled columns + .prepend('
    '); + } + }); + }, + start : function( table ) { + table = $( table )[0]; + if ( table && table.config ) { + table.config.widgetOptions.dragtableLast = { + search : $( table ).data( 'lastSearch' ), + order : ts.dragtable.getOrder( table ) + }; + } + }, + update : function( _this ) { + var t, list, val, + dragTable = _this.originalTable, + table = dragTable.el[ 0 ], + $table = $( table ), + c = table.config, + wo = c && c.widgetOptions, + startIndex = dragTable.startIndex - 1, + endIndex = dragTable.endIndex - 1, + columnOrder = ts.dragtable.getOrder( table ) || [], + hasFilters = ts.hasWidget( $table, 'filter' ) || false, + last = wo && wo.dragtableLast || {}, + // update moved filters + filters = []; + + // only trigger updateAll if column order changed + if ( ( last.order || [] ).join( '' ) !== columnOrder.join( '' ) ) { + + if ( c.sortList.length ) { + // must deep extend (nested arrays) to prevent list from changing with c.sortList + list = $.extend( true, [], c.sortList ); + $.each( columnOrder, function( indx, value ) { + val = ts.isValueInArray( parseInt( value, 10 ), list ); + if ( value !== last.order[ indx ] && val >= 0 ) { + c.sortList[ val ][ 0 ] = indx; + } + }); + } + + // update filter widget + if ( hasFilters ) { + $.each( last.search || [], function( indx ) { + filters[ indx ] = last.search[ columnOrder[ indx ] ]; + }); + } + + // update preset editable widget columns + t = ( ts.hasWidget( c.$table, 'editable' ) || false ) ? wo.editable_columnsArray : false; + if ( t ) { + c.widgetOptions.editable_columnsArray = ts.dragtable.reindexArrayItem( t, startIndex, endIndex ); + } + // update ignore math columns + t = ( ts.hasWidget( c.$table, 'math' ) || false ) ? wo.math_ignore : false; + if ( t ) { + c.widgetOptions.math_ignore = ts.dragtable.reindexArrayItem( t, startIndex, endIndex ); + } + // update preset resizable widget widths + t = ( ts.hasWidget( c.$table, 'resizable' ) || false ) ? wo.resizable_widths : false; + if ( t ) { + // use zero-based indexes in the array + wo.resizable_widths = ts.dragtable.moveArrayItem( t, startIndex, endIndex ); + } + /* + // chart widget WIP - there are other options that need to be rearranged! + t = ( ts.hasWidget( c.$table, 'chart' ) || false ) ? wo.chart_ignoreColumns : false; + if ( t ) { + // use zero-based indexes in the array + wo.chart_ignoreColumns = ts.dragtable.moveArrayItem( t, startIndex, endIndex ); + } + */ + + ts.updateAll( c, false, function() { + if ( hasFilters ) { + setTimeout( function() { + // just update the filter values + c.lastCombinedFilter = null; + c.$table.data('lastSearch', filters); + ts.setFilters( $table, filters ); + if ($.isFunction(_this.options.tablesorterComplete)) { + _this.options.tablesorterComplete( c.table ); + } + }, 10 ); + } + }); + } + }, + getOrder : function( table ) { + return $( table ).children( 'thead' ).children( '.' + ts.css.headerRow ).children().map( function() { + return $( this ).attr( 'data-column' ); + }).get() || []; + }, + // bubble the moved col left or right + startColumnMove : function( dragTable ) { + var $cols, + c = dragTable.el[ 0 ].config, + startIndex = dragTable.startIndex - 1, + endIndex = dragTable.endIndex - 1, + cols = c.columns - 1, + pos = endIndex === cols ? false : endIndex <= startIndex, + $rows = c.$table.children().children( 'tr' ); + if ( c.debug ) { + console.log( 'Inserting column ' + startIndex + ( pos ? ' before' : ' after' ) + ' column ' + endIndex ); + } + $rows.each( function() { + $cols = $( this ).children(); + $cols.eq( startIndex )[ pos ? 'insertBefore' : 'insertAfter' ]( $cols.eq( endIndex ) ); + }); + // rearrange col in colgroup + $cols = c.$table.children( 'colgroup' ).children(); + $cols.eq( startIndex )[ pos ? 'insertBefore' : 'insertAfter' ]( $cols.eq( endIndex ) ); + }, + swapNodes : function( a, b ) { + var indx, aparent, asibling, + len = a.length; + for ( indx = 0; indx < len; indx++ ) { + aparent = a[ indx ].parentNode; + asibling = a[ indx ].nextSibling === b[ indx ] ? a[ indx ] : a[ indx ].nextSibling; + b[ indx ].parentNode.insertBefore( a[ indx ], b[ indx ] ); + aparent.insertBefore( b[ indx ], asibling ); + } + }, + // http://stackoverflow.com/a/5306832/145346 + moveArrayItem : function( array, oldIndex, newIndex ) { + var indx, len = array.length; + if ( newIndex >= len ) { + indx = newIndex - len; + while ( ( indx-- ) + 1 ) { + array.push( undef ); + } + } + array.splice( newIndex, 0, array.splice( oldIndex, 1 )[ 0 ] ); + return array; + }, + reindexArrayItem : function( array, oldIndex, newIndex ) { + var nIndx = $.inArray( newIndex, array ), + oIndx = $.inArray( oldIndex, array ), + max = Math.max.apply( Math, array ), + arry = []; + // columns in the array were swapped so return original array + if ( nIndx >= 0 && oIndx >= 0 ) { + return array; + } + // columns not in the array were moved + $.each( array, function( indx, value ) { + // column (not in array) inserted between indexes + if ( newIndex < oldIndex ) { + // ( [ 0,1,2,3 ], 5, 1 ) -> column inserted between 0 & 1 => [ 0,2,3,4 ] + if ( value >= newIndex ) { + // 5 -> 1 [ 0, 2, 3 ] then 1 -> 0 [ 1, 2, 3 ] + arry.push( value + ( value < oldIndex ? 1 : 0 ) ); + } else { + arry.push( value ); + } + } else if ( newIndex > oldIndex ) { + // ( [ 0,1,2,3 ], 1, 5 ) -> column in array moved outside => [ 0,1,2,5 ] + if ( value === oldIndex ) { + arry.push( newIndex ); + } else if ( value < newIndex && value >= oldIndex ) { + arry.push( value - 1 ); + } else if ( value <= newIndex ) { + arry.push( value ); + } else if ( value > oldIndex ) { + arry.push( value + ( value < newIndex ? 0 : 1 ) ); + } + } + }); + return arry.sort(); + } + }; + +/*! dragtable v2.0.14 Mod *//* + * _____ _ + * | |___ _| | + * | | | | . | . | + * |_|_|_|___|___| + * + * Copyright (c) 2010-2013, Andres akottr@gmail.com + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * Inspired by the the dragtable from Dan Vanderkam (danvk.org/dragtable/) + * Thanks to the jquery and jqueryui comitters + * + * Any comment, bug report, feature-request is welcome + * Feel free to contact me. + */ + +/* TOKNOW: + * For IE7 you need this css rule: + * table { + * border-collapse: collapse; + * } + * Or take a clean reset.css (see http://meyerweb.com/eric/tools/css/reset/) + */ + +/* TODO: investigate + * Does not work properly with css rule: + * html { + * overflow: -moz-scrollbars-vertical; + * } + * Workaround: + * Fixing Firefox issues by scrolling down the page + * http://stackoverflow.com/questions/2451528/jquery-ui-sortable-scroll-helper-element-offset-firefox-issue + * + * var start = $.noop; + * var beforeStop = $.noop; + * if($.browser.mozilla) { + * var start = function (event, ui) { + * if( ui.helper !== undefined ) + * ui.helper.css('position','absolute').css('margin-top', $(window).scrollTop() ); + * } + * var beforeStop = function (event, ui) { + * if( ui.offset !== undefined ) + * ui.helper.css('margin-top', 0); + * } + * } + * + * and pass this as start and stop function to the sortable initialisation + * start: start, + * beforeStop: beforeStop + */ +/* + * Special thx to all pull requests comitters + */ + + $.widget("akottr.dragtable", { + options: { + revert: false, // smooth revert + dragHandle: '.table-handle', // handle for moving cols, if not exists the whole 'th' is the handle + maxMovingRows: 40, // 1 -> only header. 40 row should be enough, the rest is usually not in the viewport + excludeFooter: false, // excludes the footer row(s) while moving other columns. Make sense if there is a footer with a colspan. */ + onlyHeaderThreshold: 100, // TODO: not implemented yet, switch automatically between entire col moving / only header moving + dragaccept: null, // draggable cols -> default all + persistState: null, // url or function -> plug in your custom persistState function right here. function call is persistState(originalTable) + restoreState: null, // JSON-Object or function: some kind of experimental aka Quick-Hack TODO: do it better + exact: true, // removes pixels, so that the overlay table width fits exactly the original table width + clickDelay: 10, // ms to wait before rendering sortable list and delegating click event + containment: null, // @see http://api.jqueryui.com/sortable/#option-containment, use it if you want to move in 2 dimesnions (together with axis: null) + cursor: 'move', // @see http://api.jqueryui.com/sortable/#option-cursor + cursorAt: false, // @see http://api.jqueryui.com/sortable/#option-cursorAt + distance: 0, // @see http://api.jqueryui.com/sortable/#option-distance, for immediate feedback use "0" + tolerance: 'pointer', // @see http://api.jqueryui.com/sortable/#option-tolerance + axis: 'x', // @see http://api.jqueryui.com/sortable/#option-axis, Only vertical moving is allowed. Use 'x' or null. Use this in conjunction with the 'containment' setting + beforeStart: $.noop, // returning FALSE will stop the execution chain. + beforeMoving: $.noop, + beforeReorganize: $.noop, + beforeStop: $.noop, + // new options + tablesorterComplete: null, + sortClass : '.sorter' + }, + originalTable: { + el: null, + selectedHandle: null, + sortOrder: null, + startIndex: 0, + endIndex: 0 + }, + sortableTable: { + el: $(), + selectedHandle: $(), + movingRow: $() + }, + persistState: function() { + var _this = this; + this.originalTable.el.find('th').each(function(i) { + if (this.id !== '') { + _this.originalTable.sortOrder[this.id] = i; + } + }); + $.ajax({ + url: this.options.persistState, + data: this.originalTable.sortOrder + }); + }, + /* + * persistObj looks like + * {'id1':'2','id3':'3','id2':'1'} + * table looks like + * | id2 | id1 | id3 | + */ + _restoreState: function(persistObj) { + for (var n in persistObj) { + if (n in persistObj) { + this.originalTable.startIndex = $('#' + n).closest('th').prevAll().length + 1; + this.originalTable.endIndex = parseInt(persistObj[n], 10) + 1; + this._bubbleCols(); + } + } + }, + // bubble the moved col left or right + _bubbleCols: function() { + ts.dragtable.startColumnMove(this.originalTable); + }, + _rearrangeTableBackroundProcessing: function() { + var _this = this; + return function() { + _this._bubbleCols(); + _this.options.beforeStop(_this.originalTable); + _this.sortableTable.el.remove(); + restoreTextSelection(); + ts.dragtable.update(_this); + // persist state if necessary + if ($.isFunction(_this.options.persistState)) { + _this.options.persistState(_this.originalTable); + } else { + _this.persistState(); + } + + }; + }, + _rearrangeTable: function() { + var _this = this; + return function() { + // remove handler-class -> handler is now finished + _this.originalTable.selectedHandle.removeClass('dragtable-handle-selected'); + // add disabled class -> reorgorganisation starts soon + _this.sortableTable.el.sortable("disable"); + _this.sortableTable.el.addClass('dragtable-disabled'); + _this.options.beforeReorganize(_this.originalTable, _this.sortableTable); + // do reorganisation asynchronous + // for chrome a little bit more than 1 ms because we want to force a rerender + _this.originalTable.endIndex = _this.sortableTable.movingRow.prevAll().length + 1; + setTimeout(_this._rearrangeTableBackroundProcessing(), 50); + }; + }, + /* + * Disrupts the table. The original table stays the same. + * But on a layer above the original table we are constructing a list (ul > li) + * each li with a separate table representig a single col of the original table. + */ + _generateSortable: function(e) { + if (e.cancelBubble) { + e.cancelBubble = true; + } else { + e.stopPropagation(); + } + var _this = this; + // table attributes + var attrs = this.originalTable.el[0].attributes; + var tableAttrsString = ''; + for (var i = 0; i < attrs.length; i++) { + if ( (attrs[i].value || attrs[i].nodeValue) && attrs[i].nodeName != 'id' && attrs[i].nodeName != 'width') { + tableAttrsString += attrs[i].nodeName + '="' + ( attrs[i].value || attrs[i].nodeValue ) + '" '; + } + } + // row attributes + var rowAttrsArr = []; + //compute height, special handling for ie needed :-( + var heightArr = []; + + // don't save tfoot attributes because it messes up indexing + _this.originalTable.el.children('thead, tbody').children('tr:visible').slice(0, _this.options.maxMovingRow).each(function() { + // row attributes + var attrs = this.attributes; + var attrsString = ''; + for (var j = 0; j < attrs.length; j++) { + if ( (attrs[j].value || attrs[j].nodeValue ) && attrs[j].nodeName != 'id') { + attrsString += ' ' + attrs[j].nodeName + '="' + ( attrs[j].value || attrs[j].nodeValue ) + '"'; + } + } + rowAttrsArr.push(attrsString); + heightArr.push($(this).height()); + }); + + // compute width, no special handling for ie needed :-) + var widthArr = []; + // compute total width, needed for not wrapping around after the screen ends (floating) + var totalWidth = 0; + /* Find children thead and tbody. + * Only to process the immediate tr-children. Bugfix for inner tables + */ + var thtb = _this.originalTable.el.children(); + var headerRows = thtb.filter('thead').children('tr:visible'); + var visibleRows = thtb.filter('tbody').children('tr:visible'); + + headerRows.eq(0).children('th, td').filter(':visible').each(function() { + var w = $(this).outerWidth(); + widthArr.push(w); + totalWidth += w; + }); + if(_this.options.exact) { + var difference = totalWidth - _this.originalTable.el.outerWidth(); + widthArr[0] -= difference; + } + // one extra px on right and left side + totalWidth += 2; + + var captionHeight = 0; + thtb.filter('caption').each(function(){ + captionHeight += $(this).outerHeight(); + }); + + var sortableHtml = '
      '; + var sortableColumn = []; + // assemble the needed html + // build list + var rowIndex, + columns = headerRows.eq(0).children('th, td').length; + /*jshint loopfunc:true */ + for (i = 0; i < columns; i++) { + var row = headerRows.children(':nth-child(' + (i + 1) + ')'); + if (row.is(':visible')) { + rowIndex = 0; + sortableColumn[i] = '
    • ' + + '' + + ( captionHeight ? '' : '' ) + + ''; + // thead + headerRows.each(function(j){ + sortableColumn[i] += '' + + row[j].outerHTML + ''; + }); + sortableColumn[i] += ''; + // tbody + row = visibleRows.children(':nth-child(' + (i + 1) + ')'); + if (_this.options.maxMovingRows > 1) { + row = row.add(visibleRows.children(':nth-child(' + (i + 1) + ')').slice(0, _this.options.maxMovingRows - 1)); + } + row.each(function(j) { + sortableColumn[i] += '' + + this.outerHTML + ''; + }); + sortableColumn[i] += ''; + + // add footer to end of max Rows + if (!_this.options.excludeFooter) { + sortableColumn[i] += '' + + thtb.filter('tfoot').children('tr:visible').children()[i].outerHTML + ''; + } + sortableColumn[i] += '
    • '; + } + } + sortableHtml += sortableColumn.join('') + '
    '; + this.sortableTable.el = this.originalTable.el.before(sortableHtml).prev(); + // set width if necessary + this.sortableTable.el.find('> li > table').each(function(i) { + $(this).css('width', widthArr[i] + 'px'); + }); + + // assign this.sortableTable.selectedHandle + this.sortableTable.selectedHandle = this.sortableTable.el.find('th .dragtable-handle-selected'); + + var items = !this.options.dragaccept ? 'li' : 'li:has(' + this.options.dragaccept + ')'; + this.sortableTable.el.sortable({ + items: items, + stop: this._rearrangeTable(), + // pass thru options for sortable widget + revert: this.options.revert, + tolerance: this.options.tolerance, + containment: this.options.containment, + cursor: this.options.cursor, + cursorAt: this.options.cursorAt, + distance: this.options.distance, + axis: this.options.axis + }); + + // assign start index + this.originalTable.startIndex = $(e.target).closest('th,td').prevAll().length + 1; + this.options.beforeMoving(this.originalTable, this.sortableTable); + // Start moving by delegating the original event to the new sortable table + this.sortableTable.movingRow = this.sortableTable.el.children('li:nth-child(' + this.originalTable.startIndex + ')'); + + // prevent the user from drag selecting "highlighting" surrounding page elements + disableTextSelection(); + // clone the initial event and trigger the sort with it + this.sortableTable.movingRow.trigger($.extend($.Event(e.type), { + which: 1, + clientX: e.clientX, + clientY: e.clientY, + pageX: e.pageX, + pageY: e.pageY, + screenX: e.screenX, + screenY: e.screenY + })); + + // Some inner divs to deliver the posibillity to style the placeholder more sophisticated + var placeholder = this.sortableTable.el.find('.ui-sortable-placeholder'); + if(placeholder.height() > 0) { + placeholder.css('height', this.sortableTable.el.find('.ui-sortable-helper').height()); + } + + placeholder.html('
    '); + }, + bindTo: {}, + _create: function() { + var _this = this; + _this.originalTable = { + el: _this.element, + selectedHandle: $(), + sortOrder: {}, + startIndex: 0, + endIndex: 0 + }; + ts.dragtable.create( _this ); + // filter only the cols that are accepted + _this.bindTo = '> thead > tr > ' + ( _this.options.dragaccept || 'th, td' ); + // bind draggable to handle if exists + if (_this.element.find(_this.bindTo).find(_this.options.dragHandle).length) { + _this.bindTo += ' ' + _this.options.dragHandle; + } + // restore state if necessary + if ($.isFunction(_this.options.restoreState)) { + _this.options.restoreState(_this.originalTable); + } else { + _this._restoreState(_this.options.restoreState); + } + _this.originalTable.el.on( 'mousedown.dragtable', _this.bindTo, function(evt) { + // listen only to left mouse click + if (evt.which!==1) return; + ts.dragtable.start( _this.originalTable.el ); + if (_this.options.beforeStart(_this.originalTable) === false) { + return; + } + clearTimeout(_this.downTimer); + _this.downTimer = setTimeout(function() { + _this.originalTable.selectedHandle = $(_this); + _this.originalTable.selectedHandle.addClass('dragtable-handle-selected'); + _this._generateSortable(evt); + }, _this.options.clickDelay); + }).on( 'mouseup.dragtable', _this.options.dragHandle,function() { + clearTimeout(_this.downTimer); + }); + }, + redraw: function(){ + this.destroy(); + this._create(); + }, + destroy: function() { + this.originalTable.el.off('mousedown.dragtable mouseup.dragtable', this.bindTo); + $.Widget.prototype.destroy.apply(this, arguments); // default destroy + // now do other stuff particular to this widget + } + }); + + /** closure-scoped "private" functions **/ + var body_onselectstart_save = $(document.body).attr('onselectstart'), + body_unselectable_save = $(document.body).attr('unselectable'); + + // css properties to disable user-select on the body tag by appending a '); + $(document.head).append($style); + $(document.body).attr('onselectstart', 'return false;').attr('unselectable', 'on'); + if (window.getSelection) { + window.getSelection().removeAllRanges(); + } else { + document.selection.empty(); // MSIE http://msdn.microsoft.com/en-us/library/ms535869%28v=VS.85%29.aspx + } + } + + // remove the '; + $('head').append(s); + }); + + ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), + + // internal variables + vars = wo.resizable_vars = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, + mouseXPosition : 0, + $target : null, + $next : null, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', + storedSizes : [] + }; + + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to 'auto' + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap(''); + } + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); + + wo.$resizable_container = $( '
    ' ) + .css({ top : marginTop }) + .insertBefore( $table ); + // add container + for ( column = 0; column < c.columns; column++ ) { + $header = c.$headerIndexed[ column ]; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; + if ( !noResize ) { + $( '
    ' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); + } + } + ts.resizable.bindings( c, wo ); + }, + + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_vars; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; + } + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well + $el.css({ + 'width' : width, + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' + }); + }, + + setWidths : function( c, wo, storedSizes ) { + var column, $temp, + vars = wo.resizable_vars, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || vars.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + } + } + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + } + } + }, + + setHandlePosition : function( c, wo ) { + var startPosition, + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 ); + + if ( ts.hasWidget( c.table, 'scroller' ) ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function() { + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + + if ( !wo.resizable_includeFooter && c.$table.children('tfoot').length ) { + tableHeight -= c.$table.children('tfoot').height(); + } + // subtract out table left position from resizable handles. Fixes #864 + // jQuery v3.3.0+ appears to include the start position with the $header.position().left; see #1544 + startPosition = parseFloat($.fn.jquery) >= 3.3 ? 0 : c.$table.position().left; + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( !$header ) { return; } // see #859 + if ( + !$header.is(':visible') || + ( !wo.resizable_addLastColumn && ts.resizable.checkVisibleColumns(c, column) ) + ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + display: 'inline-block', + height : tableHeight, + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }); + } + }); + }, + + // Fixes #1485 + checkVisibleColumns: function( c, column ) { + var i, + len = 0; + for ( i = column + 1; i < c.columns; i++ ) { + len += c.$headerIndexed[i].is( ':visible' ) ? 1 : 0; + } + return len === 0; + }, + + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, wo, toggle ) { + var namespace = c.namespace + 'tsresize'; + wo.resizable_vars.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, + + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_vars, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + ts.resizable.updateStoredSizes( c, wo ); + ts.resizable.toggleTextSelection(c, wo, true ); + }); + + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_vars; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { + ts.resizable.mouseMove( c, wo, event ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_vars.disabled) { return; } + ts.resizable.toggleTextSelection( c, wo, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }); + + // right click to reset columns to default widths + c.$table + .bind( 'columnUpdate pagerComplete resizableUpdate '.split( ' ' ).join( namespace + ' ' ), function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .bind( 'resizableReset' + namespace, function() { + ts.resizableReset( c.table ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_vars.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_vars.storedSizes = []; + return allowClick; + }); + + }, + + mouseMove : function( c, wo, event ) { + if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } + // resize columns + var column, + total = 0, + vars = wo.resizable_vars, + $next = vars.$next, + tar = vars.storedSizes[ vars.target ], + leftEdge = event.pageX - vars.mouseXPosition; + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); + } + vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.triggerHandler('stickyHeadersUpdate'); + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_vars; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + // will update stickyHeaders, just in case, see #912 + c.$table.triggerHandler('stickyHeadersUpdate'); + c.$table.triggerHandler('resizableComplete'); + } + }; + + // this widget saves the column widths if + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'resizable', + priority: 40, + options: { + resizable : true, // save column widths to storage + resizable_addLastColumn : false, + resizable_includeFooter: true, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + format: function( table, c, wo ) { + ts.resizable.setHandlePosition( c, wo ); + }, + remove: function( table, c, wo, refreshing ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ) + .unbind( 'contextmenu' + namespace ); + + wo.$resizable_container.remove(); + ts.resizable.toggleTextSelection( c, wo, false ); + ts.resizableReset( table, refreshing ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } + } + }); + + ts.resizableReset = function( table, refreshing ) { + $( table ).each(function() { + var index, $t, + c = this.config, + wo = c && c.widgetOptions, + vars = wo.resizable_vars; + if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', vars.tableWidth ); + } + } + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + ts.resizable.setWidth( $t, '', vars.overflow ); + } + } + + // reset stickyHeader widths + c.$table.triggerHandler( 'stickyHeadersUpdate' ); + if ( ts.storage && !refreshing ) { + ts.storage( this, ts.css.resizableStorage, [] ); + } + } + }); + }; + +})( jQuery, window ); + +/*! Widget: saveSort - updated 2018-03-19 (v2.30.1) *//* +* Requires tablesorter v2.16+ +* by Rob Garrison +*/ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + function getStoredSortList(c) { + var stored = ts.storage( c.table, 'tablesorter-savesort' ); + return (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : []; + } + + function sortListChanged(c, sortList) { + return (sortList || getStoredSortList(c)).join(',') !== c.sortList.join(','); + } + + // this widget saves the last sort only if the + // saveSort widget option is true AND the + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { 'sortList' : c.sortList }, + debug = ts.debug(c, 'saveSort'); + if (debug) { + time = new Date(); + } + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage && sortListChanged(c)) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (debug) { + console.log('saveSort >> Saving last sort: ' + c.sortList + ts.benchmark(time)); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + sortList = getStoredSortList(c); + if (debug) { + console.log('saveSort >> Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // init is true when widget init is run, this will run this widget before all other widgets have initialized + // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice. + if (init && sortList && sortList.length > 0) { + c.sortList = sortList; + } else if (table.hasInitialized && sortList && sortList.length > 0) { + // update sort change + if (sortListChanged(c, sortList)) { + ts.sortOn(c, sortList); + } + } + } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } + } + }); + +})(jQuery); +return jQuery.tablesorter;})); diff --git a/compare/thirdparty/tablesorter/js/jquery.tablesorter.js b/compare/thirdparty/tablesorter/js/jquery.tablesorter.js new file mode 100644 index 00000000..999050e2 --- /dev/null +++ b/compare/thirdparty/tablesorter/js/jquery.tablesorter.js @@ -0,0 +1,2914 @@ +/*! TableSorter (FORK) v2.32.0 *//* +* Client-side table sorting with ease! +* @requires jQuery v1.2.6+ +* +* Copyright (c) 2007 Christian Bach +* fork maintained by Rob Garrison +* +* Examples and original docs at: http://tablesorter.com +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* @type jQuery +* @name tablesorter (FORK) +* @cat Plugins/Tablesorter +* @author Christian Bach - christian.bach@polyester.se +* @contributor Rob Garrison - https://github.com/Mottie/tablesorter +* @docs (fork) - https://mottie.github.io/tablesorter/docs/ +*/ +/*jshint browser:true, jquery:true, unused:false, expr: true */ +;( function( $ ) { + 'use strict'; + var ts = $.tablesorter = { + + version : '2.32.0', + + parsers : [], + widgets : [], + defaults : { + + // *** appearance + theme : 'default', // adds tablesorter-{theme} to the table for styling + widthFixed : false, // adds colgroup to fix widths of columns + showProcessing : false, // show an indeterminate timer icon in the header when the table is sorted or filtered. + + headerTemplate : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = // class from cssIcon + onRenderTemplate : null, // function( index, template ) { return template; }, // template is a string + onRenderHeader : null, // function( index ) {}, // nothing to return + + // *** functionality + cancelSelection : true, // prevent text selection in the header + tabIndex : true, // add tabindex to header for keyboard accessibility + dateFormat : 'mmddyyyy', // other options: 'ddmmyyy' or 'yyyymmdd' + sortMultiSortKey : 'shiftKey', // key used to select additional columns + sortResetKey : 'ctrlKey', // key used to remove sorting on a column + usNumberFormat : true, // false for German '1.234.567,89' or French '1 234 567,89' + delayInit : false, // if false, the parsed table contents will not update until the first sort + serverSideSorting: false, // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used. + resort : true, // default setting to trigger a resort after an 'update', 'addRows', 'updateCell', etc has completed + + // *** sort options + headers : null, // set sorter, string, empty, locked order, sortInitialOrder, filter, etc. + ignoreCase : true, // ignore case while sorting + sortForce : null, // column(s) first sorted; always applied + sortList : [], // Initial sort order; applied initially; updated when manually sorted + sortAppend : null, // column(s) sorted last; always applied + sortStable : false, // when sorting two rows with exactly the same content, the original sort order is maintained + + sortInitialOrder : 'asc', // sort direction on first click + sortLocaleCompare: false, // replace equivalent character (accented characters) + sortReset : false, // third click on the header will reset column to default - unsorted + sortRestart : false, // restart sort to 'sortInitialOrder' when clicking on previously unsorted columns + + emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin + stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero + duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column + textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ) {} + textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function) + textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText] + numberSorter : null, // choose overall numeric sorter function( a, b, direction, maxColumnValue ) + + // *** widget options + initWidgets : true, // apply widgets on tablesorter initialization + widgetClass : 'widget-{name}', // table class name template to match to include a widget + widgets : [], // method to add widgets, e.g. widgets: ['zebra'] + widgetOptions : { + zebra : [ 'even', 'odd' ] // zebra widget alternating row class names + }, + + // *** callbacks + initialized : null, // function( table ) {}, + + // *** extra css class names + tableClass : '', + cssAsc : '', + cssDesc : '', + cssNone : '', + cssHeader : '', + cssHeaderRow : '', + cssProcessing : '', // processing icon applied to header during sort/filter + + cssChildRow : 'tablesorter-childRow', // class name indiciating that a row is to be attached to its parent + cssInfoBlock : 'tablesorter-infoOnly', // don't sort tbody with this class name (only one class name allowed here!) + cssNoSort : 'tablesorter-noSort', // class name added to element inside header; clicking on it won't cause a sort + cssIgnoreRow : 'tablesorter-ignoreRow',// header row to ignore; cells within this row will not be added to c.$headers + + cssIcon : 'tablesorter-icon', // if this class does not exist, the {icon} will not be added from the headerTemplate + cssIconNone : '', // class name added to the icon when there is no column sort + cssIconAsc : '', // class name added to the icon when the column has an ascending sort + cssIconDesc : '', // class name added to the icon when the column has a descending sort + cssIconDisabled : '', // class name added to the icon when the column has a disabled sort + + // *** events + pointerClick : 'click', + pointerDown : 'mousedown', + pointerUp : 'mouseup', + + // *** selectors + selectorHeaders : '> thead th, > thead td', + selectorSort : 'th, td', // jQuery selector of content within selectorHeaders that is clickable to trigger a sort + selectorRemove : '.remove-me', + + // *** advanced + debug : false, + + // *** Internal variables + headerList: [], + empties: {}, + strings: {}, + parsers: [], + + // *** parser options for validator; values must be falsy! + globalize: 0, + imgAttr: 0 + + // removed: widgetZebra: { css: ['even', 'odd'] } + + }, + + // internal css classes - these will ALWAYS be added to + // the table and MUST only contain one class name - fixes #381 + css : { + table : 'tablesorter', + cssHasChild: 'tablesorter-hasChildRow', + childRow : 'tablesorter-childRow', + colgroup : 'tablesorter-colgroup', + header : 'tablesorter-header', + headerRow : 'tablesorter-headerRow', + headerIn : 'tablesorter-header-inner', + icon : 'tablesorter-icon', + processing : 'tablesorter-processing', + sortAsc : 'tablesorter-headerAsc', + sortDesc : 'tablesorter-headerDesc', + sortNone : 'tablesorter-headerUnSorted' + }, + + // labels applied to sortable headers for accessibility (aria) support + language : { + sortAsc : 'Ascending sort applied, ', + sortDesc : 'Descending sort applied, ', + sortNone : 'No sort applied, ', + sortDisabled : 'sorting is disabled', + nextAsc : 'activate to apply an ascending sort', + nextDesc : 'activate to apply a descending sort', + nextNone : 'activate to remove the sort' + }, + + regex : { + templateContent : /\{content\}/g, + templateIcon : /\{icon\}/g, + templateName : /\{name\}/i, + spaces : /\s+/g, + nonWord : /\W/g, + formElements : /(input|select|button|textarea)/i, + + // *** sort functions *** + // regex used in natural sort + // chunk/tokenize numbers & letters + chunk : /(^([+\-]?(?:\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, + // replace chunks @ ends + chunks : /(^\\0|\\0$)/, + hex : /^0x[0-9a-f]+$/i, + + // *** formatFloat *** + comma : /,/g, + digitNonUS : /[\s|\.]/g, + digitNegativeTest : /^\s*\([.\d]+\)/, + digitNegativeReplace : /^\s*\(([.\d]+)\)/, + + // *** isDigit *** + digitTest : /^[\-+(]?\d+[)]?$/, + digitReplace : /[,.'"\s]/g + + }, + + // digit sort, text location + string : { + max : 1, + min : -1, + emptymin : 1, + emptymax : -1, + zero : 0, + none : 0, + 'null' : 0, + top : true, + bottom : false + }, + + keyCodes : { + enter : 13 + }, + + // placeholder date parser data (globalize) + dates : {}, + + // These methods can be applied on table.config instance + instanceMethods : {}, + + /* + ▄█████ ██████ ██████ ██ ██ █████▄ + ▀█▄ ██▄▄ ██ ██ ██ ██▄▄██ + ▀█▄ ██▀▀ ██ ██ ██ ██▀▀▀ + █████▀ ██████ ██ ▀████▀ ██ + */ + + setup : function( table, c ) { + // if no thead or tbody, or tablesorter is already present, quit + if ( !table || !table.tHead || table.tBodies.length === 0 || table.hasInitialized === true ) { + if ( ts.debug(c, 'core') ) { + if ( table.hasInitialized ) { + console.warn( 'Stopping initialization. Tablesorter has already been initialized' ); + } else { + console.error( 'Stopping initialization! No table, thead or tbody', table ); + } + } + return; + } + + var tmp = '', + $table = $( table ), + meta = $.metadata; + // initialization flag + table.hasInitialized = false; + // table is being processed flag + table.isProcessing = true; + // make sure to store the config object + table.config = c; + // save the settings where they read + $.data( table, 'tablesorter', c ); + if ( ts.debug(c, 'core') ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing tablesorter v' + ts.version ); + $.data( table, 'startoveralltimer', new Date() ); + } + + // removing this in version 3 (only supports jQuery 1.7+) + c.supportsDataObject = ( function( version ) { + version[ 0 ] = parseInt( version[ 0 ], 10 ); + return ( version[ 0 ] > 1 ) || ( version[ 0 ] === 1 && parseInt( version[ 1 ], 10 ) >= 4 ); + })( $.fn.jquery.split( '.' ) ); + // ensure case insensitivity + c.emptyTo = c.emptyTo.toLowerCase(); + c.stringTo = c.stringTo.toLowerCase(); + c.last = { sortList : [], clickedIndex : -1 }; + // add table theme class only if there isn't already one there + if ( !/tablesorter\-/.test( $table.attr( 'class' ) ) ) { + tmp = ( c.theme !== '' ? ' tablesorter-' + c.theme : '' ); + } + + // give the table a unique id, which will be used in namespace binding + if ( !c.namespace ) { + c.namespace = '.tablesorter' + Math.random().toString( 16 ).slice( 2 ); + } else { + // make sure namespace starts with a period & doesn't have weird characters + c.namespace = '.' + c.namespace.replace( ts.regex.nonWord, '' ); + } + + c.table = table; + c.$table = $table + // add namespace to table to allow bindings on extra elements to target + // the parent table (e.g. parser-input-select) + .addClass( ts.css.table + ' ' + c.tableClass + tmp + ' ' + c.namespace.slice(1) ) + .attr( 'role', 'grid' ); + c.$headers = $table.find( c.selectorHeaders ); + + c.$table.children().children( 'tr' ).attr( 'role', 'row' ); + c.$tbodies = $table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ).attr({ + 'aria-live' : 'polite', + 'aria-relevant' : 'all' + }); + if ( c.$table.children( 'caption' ).length ) { + tmp = c.$table.children( 'caption' )[ 0 ]; + if ( !tmp.id ) { tmp.id = c.namespace.slice( 1 ) + 'caption'; } + c.$table.attr( 'aria-labelledby', tmp.id ); + } + c.widgetInit = {}; // keep a list of initialized widgets + // change textExtraction via data-attribute + c.textExtraction = c.$table.attr( 'data-text-extraction' ) || c.textExtraction || 'basic'; + // build headers + ts.buildHeaders( c ); + // fixate columns if the users supplies the fixedWidth option + // do this after theme has been applied + ts.fixColumnWidth( table ); + // add widgets from class name + ts.addWidgetFromClass( table ); + // add widget options before parsing (e.g. grouping widget has parser settings) + ts.applyWidgetOptions( table ); + // try to auto detect column type, and store in tables config + ts.setupParsers( c ); + // start total row count at zero + c.totalRows = 0; + // only validate options while debugging. See #1528 + if (c.debug) { + ts.validateOptions( c ); + } + // build the cache for the tbody cells + // delayInit will delay building the cache until the user starts a sort + if ( !c.delayInit ) { ts.buildCache( c ); } + // bind all header events and methods + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + // get sort list from jQuery data or metadata + // in jQuery < 1.4, an error occurs when calling $table.data() + if ( c.supportsDataObject && typeof $table.data().sortlist !== 'undefined' ) { + c.sortList = $table.data().sortlist; + } else if ( meta && ( $table.metadata() && $table.metadata().sortlist ) ) { + c.sortList = $table.metadata().sortlist; + } + // apply widget init code + ts.applyWidget( table, true ); + // if user has supplied a sort list to constructor + if ( c.sortList.length > 0 ) { + // save sortList before any sortAppend is added + c.last.sortList = c.sortList; + ts.sortOn( c, c.sortList, {}, !c.initWidgets ); + } else { + ts.setHeadersCss( c ); + if ( c.initWidgets ) { + // apply widget format + ts.applyWidget( table, false ); + } + } + + // show processesing icon + if ( c.showProcessing ) { + $table + .unbind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace ) + .bind( 'sortBegin' + c.namespace + ' sortEnd' + c.namespace, function( e ) { + clearTimeout( c.timerProcessing ); + ts.isProcessing( table ); + if ( e.type === 'sortBegin' ) { + c.timerProcessing = setTimeout( function() { + ts.isProcessing( table, true ); + }, 500 ); + } + }); + } + + // initialized + table.hasInitialized = true; + table.isProcessing = false; + if ( ts.debug(c, 'core') ) { + console.log( 'Overall initialization time:' + ts.benchmark( $.data( table, 'startoveralltimer' ) ) ); + if ( ts.debug(c, 'core') && console.groupEnd ) { console.groupEnd(); } + } + $table.triggerHandler( 'tablesorter-initialized', table ); + if ( typeof c.initialized === 'function' ) { + c.initialized( table ); + } + }, + + bindMethods : function( c ) { + var $table = c.$table, + namespace = c.namespace, + events = ( 'sortReset update updateRows updateAll updateHeaders addRows updateCell updateComplete ' + + 'sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup ' + + 'mouseleave ' ).split( ' ' ) + .join( namespace + ' ' ); + // apply easy methods that trigger bound events + $table + .unbind( events.replace( ts.regex.spaces, ' ' ) ) + .bind( 'sortReset' + namespace, function( e, callback ) { + e.stopPropagation(); + // using this.config to ensure functions are getting a non-cached version of the config + ts.sortReset( this.config, function( table ) { + if (table.isApplyingWidgets) { + // multiple triggers in a row... filterReset, then sortReset - see #1361 + // wait to update widgets + setTimeout( function() { + ts.applyWidget( table, '', callback ); + }, 100 ); + } else { + ts.applyWidget( table, '', callback ); + } + }); + }) + .bind( 'updateAll' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.updateAll( this.config, resort, callback ); + }) + .bind( 'update' + namespace + ' updateRows' + namespace, function( e, resort, callback ) { + e.stopPropagation(); + ts.update( this.config, resort, callback ); + }) + .bind( 'updateHeaders' + namespace, function( e, callback ) { + e.stopPropagation(); + ts.updateHeaders( this.config, callback ); + }) + .bind( 'updateCell' + namespace, function( e, cell, resort, callback ) { + e.stopPropagation(); + ts.updateCell( this.config, cell, resort, callback ); + }) + .bind( 'addRows' + namespace, function( e, $row, resort, callback ) { + e.stopPropagation(); + ts.addRows( this.config, $row, resort, callback ); + }) + .bind( 'updateComplete' + namespace, function() { + this.isUpdating = false; + }) + .bind( 'sorton' + namespace, function( e, list, callback, init ) { + e.stopPropagation(); + ts.sortOn( this.config, list, callback, init ); + }) + .bind( 'appendCache' + namespace, function( e, callback, init ) { + e.stopPropagation(); + ts.appendCache( this.config, init ); + if ( $.isFunction( callback ) ) { + callback( this ); + } + }) + // $tbodies variable is used by the tbody sorting widget + .bind( 'updateCache' + namespace, function( e, callback, $tbodies ) { + e.stopPropagation(); + ts.updateCache( this.config, callback, $tbodies ); + }) + .bind( 'applyWidgetId' + namespace, function( e, id ) { + e.stopPropagation(); + ts.applyWidgetId( this, id ); + }) + .bind( 'applyWidgets' + namespace, function( e, callback ) { + e.stopPropagation(); + // apply widgets (false = not initializing) + ts.applyWidget( this, false, callback ); + }) + .bind( 'refreshWidgets' + namespace, function( e, all, dontapply ) { + e.stopPropagation(); + ts.refreshWidgets( this, all, dontapply ); + }) + .bind( 'removeWidget' + namespace, function( e, name, refreshing ) { + e.stopPropagation(); + ts.removeWidget( this, name, refreshing ); + }) + .bind( 'destroy' + namespace, function( e, removeClasses, callback ) { + e.stopPropagation(); + ts.destroy( this, removeClasses, callback ); + }) + .bind( 'resetToLoadState' + namespace, function( e ) { + e.stopPropagation(); + // remove all widgets + ts.removeWidget( this, true, false ); + var tmp = $.extend( true, {}, c.originalSettings ); + // restore original settings; this clears out current settings, but does not clear + // values saved to storage. + c = $.extend( true, {}, ts.defaults, tmp ); + c.originalSettings = tmp; + this.hasInitialized = false; + // setup the entire table again + ts.setup( this, c ); + }); + }, + + bindEvents : function( table, $headers, core ) { + table = $( table )[ 0 ]; + var tmp, + c = table.config, + namespace = c.namespace, + downTarget = null; + if ( core !== true ) { + $headers.addClass( namespace.slice( 1 ) + '_extra_headers' ); + tmp = ts.getClosest( $headers, 'table' ); + if ( tmp.length && tmp[ 0 ].nodeName === 'TABLE' && tmp[ 0 ] !== table ) { + $( tmp[ 0 ] ).addClass( namespace.slice( 1 ) + '_extra_table' ); + } + } + tmp = ( c.pointerDown + ' ' + c.pointerUp + ' ' + c.pointerClick + ' sort keyup ' ) + .replace( ts.regex.spaces, ' ' ) + .split( ' ' ) + .join( namespace + ' ' ); + // apply event handling to headers and/or additional headers (stickyheaders, scroller, etc) + $headers + // http://stackoverflow.com/questions/5312849/jquery-find-self; + .find( c.selectorSort ) + .add( $headers.filter( c.selectorSort ) ) + .unbind( tmp ) + .bind( tmp, function( e, external ) { + var $cell, cell, temp, + $target = $( e.target ), + // wrap event type in spaces, so the match doesn't trigger on inner words + type = ' ' + e.type + ' '; + // only recognize left clicks + if ( ( ( e.which || e.button ) !== 1 && !type.match( ' ' + c.pointerClick + ' | sort | keyup ' ) ) || + // allow pressing enter + ( type === ' keyup ' && e.which !== ts.keyCodes.enter ) || + // allow triggering a click event (e.which is undefined) & ignore physical clicks + ( type.match( ' ' + c.pointerClick + ' ' ) && typeof e.which !== 'undefined' ) ) { + return; + } + // ignore mouseup if mousedown wasn't on the same target + if ( type.match( ' ' + c.pointerUp + ' ' ) && downTarget !== e.target && external !== true ) { + return; + } + // set target on mousedown + if ( type.match( ' ' + c.pointerDown + ' ' ) ) { + downTarget = e.target; + // preventDefault needed or jQuery v1.3.2 and older throws an + // "Uncaught TypeError: handler.apply is not a function" error + temp = $target.jquery.split( '.' ); + if ( temp[ 0 ] === '1' && temp[ 1 ] < 4 ) { e.preventDefault(); } + return; + } + downTarget = null; + $cell = ts.getClosest( $( this ), '.' + ts.css.header ); + // prevent sort being triggered on form elements + if ( ts.regex.formElements.test( e.target.nodeName ) || + // nosort class name, or elements within a nosort container + $target.hasClass( c.cssNoSort ) || $target.parents( '.' + c.cssNoSort ).length > 0 || + // disabled cell directly clicked + $cell.hasClass( 'sorter-false' ) || + // elements within a button + $target.parents( 'button' ).length > 0 ) { + return !c.cancelSelection; + } + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + // use column index from data-attribute or index of current row; fixes #1116 + c.last.clickedIndex = $cell.attr( 'data-column' ) || $cell.index(); + cell = c.$headerIndexed[ c.last.clickedIndex ][0]; + if ( cell && !cell.sortDisabled ) { + ts.initSort( c, cell, e ); + } + }); + if ( c.cancelSelection ) { + // cancel selection + $headers + .attr( 'unselectable', 'on' ) + .bind( 'selectstart', false ) + .css({ + 'user-select' : 'none', + 'MozUserSelect' : 'none' // not needed for jQuery 1.8+ + }); + } + }, + + buildHeaders : function( c ) { + var $temp, icon, timer, indx; + c.headerList = []; + c.headerContent = []; + c.sortVars = []; + if ( ts.debug(c, 'core') ) { + timer = new Date(); + } + // children tr in tfoot - see issue #196 & #547 + // don't pass table.config to computeColumnIndex here - widgets (math) pass it to "quickly" index tbody cells + c.columns = ts.computeColumnIndex( c.$table.children( 'thead, tfoot' ).children( 'tr' ) ); + // add icon if cssIcon option exists + icon = c.cssIcon ? + '' : + ''; + // redefine c.$headers here in case of an updateAll that replaces or adds an entire header cell - see #683 + c.$headers = $( $.map( c.$table.find( c.selectorHeaders ), function( elem, index ) { + var configHeaders, header, column, template, tmp, + $elem = $( elem ); + // ignore cell (don't add it to c.$headers) if row has ignoreRow class + if ( ts.getClosest( $elem, 'tr' ).hasClass( c.cssIgnoreRow ) ) { return; } + // transfer data-column to element if not th/td - #1459 + if ( !/(th|td)/i.test( elem.nodeName ) ) { + tmp = ts.getClosest( $elem, 'th, td' ); + $elem.attr( 'data-column', tmp.attr( 'data-column' ) ); + } + // make sure to get header cell & not column indexed cell + configHeaders = ts.getColumnData( c.table, c.headers, index, true ); + // save original header content + c.headerContent[ index ] = $elem.html(); + // if headerTemplate is empty, don't reformat the header cell + if ( c.headerTemplate !== '' && !$elem.find( '.' + ts.css.headerIn ).length ) { + // set up header template + template = c.headerTemplate + .replace( ts.regex.templateContent, $elem.html() ) + .replace( ts.regex.templateIcon, $elem.find( '.' + ts.css.icon ).length ? '' : icon ); + if ( c.onRenderTemplate ) { + header = c.onRenderTemplate.apply( $elem, [ index, template ] ); + // only change t if something is returned + if ( header && typeof header === 'string' ) { + template = header; + } + } + $elem.html( '
    ' + template + '
    ' ); // faster than wrapInner + } + if ( c.onRenderHeader ) { + c.onRenderHeader.apply( $elem, [ index, c, c.$table ] ); + } + column = parseInt( $elem.attr( 'data-column' ), 10 ); + elem.column = column; + tmp = ts.getOrder( ts.getData( $elem, configHeaders, 'sortInitialOrder' ) || c.sortInitialOrder ); + // this may get updated numerous times if there are multiple rows + c.sortVars[ column ] = { + count : -1, // set to -1 because clicking on the header automatically adds one + order : tmp ? + ( c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ] ) : // desc, asc, unsorted + ( c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ] ), // asc, desc, unsorted + lockedOrder : false, + sortedBy : '' + }; + tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false; + if ( typeof tmp !== 'undefined' && tmp !== false ) { + c.sortVars[ column ].lockedOrder = true; + c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1 ] : [ 0, 0 ]; + } + // add cell to headerList + c.headerList[ index ] = elem; + $elem.addClass( ts.css.header + ' ' + c.cssHeader ); + // add to parent in case there are multiple rows + ts.getClosest( $elem, 'tr' ) + .addClass( ts.css.headerRow + ' ' + c.cssHeaderRow ) + .attr( 'role', 'row' ); + // allow keyboard cursor to focus on element + if ( c.tabIndex ) { + $elem.attr( 'tabindex', 0 ); + } + return elem; + }) ); + // cache headers per column + c.$headerIndexed = []; + for ( indx = 0; indx < c.columns; indx++ ) { + // colspan in header making a column undefined + if ( ts.isEmptyObject( c.sortVars[ indx ] ) ) { + c.sortVars[ indx ] = {}; + } + // Use c.$headers.parent() in case selectorHeaders doesn't point to the th/td + $temp = c.$headers.filter( '[data-column="' + indx + '"]' ); + // target sortable column cells, unless there are none, then use non-sortable cells + // .last() added in jQuery 1.4; use .filter(':last') to maintain compatibility with jQuery v1.2.6 + c.$headerIndexed[ indx ] = $temp.length ? + $temp.not( '.sorter-false' ).length ? + $temp.not( '.sorter-false' ).filter( ':last' ) : + $temp.filter( ':last' ) : + $(); + } + c.$table.find( c.selectorHeaders ).attr({ + scope: 'col', + role : 'columnheader' + }); + // enable/disable sorting + ts.updateHeader( c ); + if ( ts.debug(c, 'core') ) { + console.log( 'Built headers:' + ts.benchmark( timer ) ); + console.log( c.$headers ); + } + }, + + // Use it to add a set of methods to table.config which will be available for all tables. + // This should be done before table initialization + addInstanceMethods : function( methods ) { + $.extend( ts.instanceMethods, methods ); + }, + + /* + █████▄ ▄████▄ █████▄ ▄█████ ██████ █████▄ ▄█████ + ██▄▄██ ██▄▄██ ██▄▄██ ▀█▄ ██▄▄ ██▄▄██ ▀█▄ + ██▀▀▀ ██▀▀██ ██▀██ ▀█▄ ██▀▀ ██▀██ ▀█▄ + ██ ██ ██ ██ ██ █████▀ ██████ ██ ██ █████▀ + */ + setupParsers : function( c, $tbodies ) { + var rows, list, span, max, colIndex, indx, header, configHeaders, + noParser, parser, extractor, time, tbody, len, + table = c.table, + tbodyIndex = 0, + debug = ts.debug(c, 'core'), + debugOutput = {}; + // update table bodies in case we start with an empty table + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies; + len = tbody.length; + if ( len === 0 ) { + return debug ? console.warn( 'Warning: *Empty table!* Not building a parser cache' ) : ''; + } else if ( debug ) { + time = new Date(); + console[ console.group ? 'group' : 'log' ]( 'Detecting parsers for each column' ); + } + list = { + extractors: [], + parsers: [] + }; + while ( tbodyIndex < len ) { + rows = tbody[ tbodyIndex ].rows; + if ( rows.length ) { + colIndex = 0; + max = c.columns; + for ( indx = 0; indx < max; indx++ ) { + header = c.$headerIndexed[ colIndex ]; + if ( header && header.length ) { + // get column indexed table cell; adding true parameter fixes #1362 but + // it would break backwards compatibility... + configHeaders = ts.getColumnData( table, c.headers, colIndex ); // , true ); + // get column parser/extractor + extractor = ts.getParserById( ts.getData( header, configHeaders, 'extractor' ) ); + parser = ts.getParserById( ts.getData( header, configHeaders, 'sorter' ) ); + noParser = ts.getData( header, configHeaders, 'parser' ) === 'false'; + // empty cells behaviour - keeping emptyToBottom for backwards compatibility + c.empties[colIndex] = ( + ts.getData( header, configHeaders, 'empty' ) || + c.emptyTo || ( c.emptyToBottom ? 'bottom' : 'top' ) ).toLowerCase(); + // text strings behaviour in numerical sorts + c.strings[colIndex] = ( + ts.getData( header, configHeaders, 'string' ) || + c.stringTo || + 'max' ).toLowerCase(); + if ( noParser ) { + parser = ts.getParserById( 'no-parser' ); + } + if ( !extractor ) { + // For now, maybe detect someday + extractor = false; + } + if ( !parser ) { + parser = ts.detectParserForColumn( c, rows, -1, colIndex ); + } + if ( debug ) { + debugOutput[ '(' + colIndex + ') ' + header.text() ] = { + parser : parser.id, + extractor : extractor ? extractor.id : 'none', + string : c.strings[ colIndex ], + empty : c.empties[ colIndex ] + }; + } + list.parsers[ colIndex ] = parser; + list.extractors[ colIndex ] = extractor; + span = header[ 0 ].colSpan - 1; + if ( span > 0 ) { + colIndex += span; + max += span; + while ( span + 1 > 0 ) { + // set colspan columns to use the same parsers & extractors + list.parsers[ colIndex - span ] = parser; + list.extractors[ colIndex - span ] = extractor; + span--; + } + } + } + colIndex++; + } + } + tbodyIndex += ( list.parsers.length ) ? len : 1; + } + if ( debug ) { + if ( !ts.isEmptyObject( debugOutput ) ) { + console[ console.table ? 'table' : 'log' ]( debugOutput ); + } else { + console.warn( ' No parsers detected!' ); + } + console.log( 'Completed detecting parsers' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } + c.parsers = list.parsers; + c.extractors = list.extractors; + }, + + addParser : function( parser ) { + var indx, + len = ts.parsers.length, + add = true; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === parser.id.toLowerCase() ) { + add = false; + } + } + if ( add ) { + ts.parsers[ ts.parsers.length ] = parser; + } + }, + + getParserById : function( name ) { + /*jshint eqeqeq:false */ // eslint-disable-next-line eqeqeq + if ( name == 'false' ) { return false; } + var indx, + len = ts.parsers.length; + for ( indx = 0; indx < len; indx++ ) { + if ( ts.parsers[ indx ].id.toLowerCase() === ( name.toString() ).toLowerCase() ) { + return ts.parsers[ indx ]; + } + } + return false; + }, + + detectParserForColumn : function( c, rows, rowIndex, cellIndex ) { + var cur, $node, row, + indx = ts.parsers.length, + node = false, + nodeValue = '', + debug = ts.debug(c, 'core'), + keepLooking = true; + while ( nodeValue === '' && keepLooking ) { + rowIndex++; + row = rows[ rowIndex ]; + // stop looking after 50 empty rows + if ( row && rowIndex < 50 ) { + if ( row.className.indexOf( ts.cssIgnoreRow ) < 0 ) { + node = rows[ rowIndex ].cells[ cellIndex ]; + nodeValue = ts.getElementText( c, node, cellIndex ); + $node = $( node ); + if ( debug ) { + console.log( 'Checking if value was empty on row ' + rowIndex + ', column: ' + + cellIndex + ': "' + nodeValue + '"' ); + } + } + } else { + keepLooking = false; + } + } + while ( --indx >= 0 ) { + cur = ts.parsers[ indx ]; + // ignore the default text parser because it will always be true + if ( cur && cur.id !== 'text' && cur.is && cur.is( nodeValue, c.table, node, $node ) ) { + return cur; + } + } + // nothing found, return the generic parser (text) + return ts.getParserById( 'text' ); + }, + + getElementText : function( c, node, cellIndex ) { + if ( !node ) { return ''; } + var tmp, + extract = c.textExtraction || '', + // node could be a jquery object + // http://jsperf.com/jquery-vs-instanceof-jquery/2 + $node = node.jquery ? node : $( node ); + if ( typeof extract === 'string' ) { + // check data-attribute first when set to 'basic'; don't use node.innerText - it's really slow! + // http://www.kellegous.com/j/2013/02/27/innertext-vs-textcontent/ + if ( extract === 'basic' && typeof ( tmp = $node.attr( c.textAttribute ) ) !== 'undefined' ) { + return $.trim( tmp ); + } + return $.trim( node.textContent || $node.text() ); + } else { + if ( typeof extract === 'function' ) { + return $.trim( extract( $node[ 0 ], c.table, cellIndex ) ); + } else if ( typeof ( tmp = ts.getColumnData( c.table, extract, cellIndex ) ) === 'function' ) { + return $.trim( tmp( $node[ 0 ], c.table, cellIndex ) ); + } + } + // fallback + return $.trim( $node[ 0 ].textContent || $node.text() ); + }, + + // centralized function to extract/parse cell contents + getParsedText : function( c, cell, colIndex, txt ) { + if ( typeof txt === 'undefined' ) { + txt = ts.getElementText( c, cell, colIndex ); + } + // if no parser, make sure to return the txt + var val = '' + txt, + parser = c.parsers[ colIndex ], + extractor = c.extractors[ colIndex ]; + if ( parser ) { + // do extract before parsing, if there is one + if ( extractor && typeof extractor.format === 'function' ) { + txt = extractor.format( txt, c.table, cell, colIndex ); + } + // allow parsing if the string is empty, previously parsing would change it to zero, + // in case the parser needs to extract data from the table cell attributes + val = parser.id === 'no-parser' ? '' : + // make sure txt is a string (extractor may have converted it) + parser.format( '' + txt, c.table, cell, colIndex ); + if ( c.ignoreCase && typeof val === 'string' ) { + val = val.toLowerCase(); + } + } + return val; + }, + + /* + ▄████▄ ▄████▄ ▄████▄ ██ ██ ██████ + ██ ▀▀ ██▄▄██ ██ ▀▀ ██▄▄██ ██▄▄ + ██ ▄▄ ██▀▀██ ██ ▄▄ ██▀▀██ ██▀▀ + ▀████▀ ██ ██ ▀████▀ ██ ██ ██████ + */ + buildCache : function( c, callback, $tbodies ) { + var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row, + cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData, + colMax, span, cacheIndex, hasParser, max, len, index, + table = c.table, + parsers = c.parsers, + debug = ts.debug(c, 'core'); + // update tbody variable + c.$tbodies = c.$table.children( 'tbody:not(.' + c.cssInfoBlock + ')' ); + $tbody = typeof $tbodies === 'undefined' ? c.$tbodies : $tbodies, + c.cache = {}; + c.totalRows = 0; + // if no parsers found, return - it's an empty table. + if ( !parsers ) { + return debug ? console.warn( 'Warning: *Empty table!* Not building a cache' ) : ''; + } + if ( debug ) { + cacheTime = new Date(); + } + // processing icon + if ( c.showProcessing ) { + ts.isProcessing( table, true ); + } + for ( tbodyIndex = 0; tbodyIndex < $tbody.length; tbodyIndex++ ) { + colMax = []; // column max value per tbody + cache = c.cache[ tbodyIndex ] = { + normalized: [] // array of normalized row data; last entry contains 'rowData' above + // colMax: # // added at the end + }; + + totalRows = ( $tbody[ tbodyIndex ] && $tbody[ tbodyIndex ].rows.length ) || 0; + for ( rowIndex = 0; rowIndex < totalRows; ++rowIndex ) { + rowData = { + // order: original row order # + // $row : jQuery Object[] + child: [], // child row text (filter widget) + raw: [] // original row text + }; + /** Add the table data to main data array */ + $row = $( $tbody[ tbodyIndex ].rows[ rowIndex ] ); + cols = []; + // ignore "remove-me" rows + if ( $row.hasClass( c.selectorRemove.slice(1) ) ) { + continue; + } + // if this is a child row, add it to the last row's children and continue to the next row + // ignore child row class, if it is the first row + if ( $row.hasClass( c.cssChildRow ) && rowIndex !== 0 ) { + len = cache.normalized.length - 1; + prevRowData = cache.normalized[ len ][ c.columns ]; + prevRowData.$row = prevRowData.$row.add( $row ); + // add 'hasChild' class name to parent row + if ( !$row.prev().hasClass( c.cssChildRow ) ) { + $row.prev().addClass( ts.css.cssHasChild ); + } + // save child row content (un-parsed!) + $cells = $row.children( 'th, td' ); + len = prevRowData.child.length; + prevRowData.child[ len ] = []; + // child row content does not account for colspans/rowspans; so indexing may be off + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; colIndex++ ) { + cell = $cells[ colIndex ]; + if ( cell ) { + prevRowData.child[ len ][ colIndex ] = ts.getParsedText( c, cell, colIndex ); + span = $cells[ colIndex ].colSpan - 1; + if ( span > 0 ) { + cacheIndex += span; + max += span; + } + } + cacheIndex++; + } + // go to the next for loop + continue; + } + rowData.$row = $row; + rowData.order = rowIndex; // add original row position to rowCache + cacheIndex = 0; + max = c.columns; + for ( colIndex = 0; colIndex < max; ++colIndex ) { + cell = $row[ 0 ].cells[ colIndex ]; + if ( cell && cacheIndex < c.columns ) { + hasParser = typeof parsers[ cacheIndex ] !== 'undefined'; + if ( !hasParser && debug ) { + console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex + + '; cell containing: "' + $(cell).text() + '"; does it have a header?' ); + } + val = ts.getElementText( c, cell, cacheIndex ); + rowData.raw[ cacheIndex ] = val; // save original row text + // save raw column text even if there is no parser set + txt = ts.getParsedText( c, cell, cacheIndex, val ); + cols[ cacheIndex ] = txt; + if ( hasParser && ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) { + // determine column max value (ignore sign) + colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 ); + } + // allow colSpan in tbody + span = cell.colSpan - 1; + if ( span > 0 ) { + index = 0; + while ( index <= span ) { + // duplicate text (or not) to spanned columns + // instead of setting duplicate span to empty string, use textExtraction to try to get a value + // see http://stackoverflow.com/q/36449711/145346 + txt = c.duplicateSpan || index === 0 ? + txt : + typeof c.textExtraction !== 'string' ? + ts.getElementText( c, cell, cacheIndex + index ) || '' : + ''; + rowData.raw[ cacheIndex + index ] = txt; + cols[ cacheIndex + index ] = txt; + index++; + } + cacheIndex += span; + max += span; + } + } + cacheIndex++; + } + // ensure rowData is always in the same location (after the last column) + cols[ c.columns ] = rowData; + cache.normalized[ cache.normalized.length ] = cols; + } + cache.colMax = colMax; + // total up rows, not including child rows + c.totalRows += cache.normalized.length; + + } + if ( c.showProcessing ) { + ts.isProcessing( table ); // remove processing icon + } + if ( debug ) { + len = Math.min( 5, c.cache[ 0 ].normalized.length ); + console[ console.group ? 'group' : 'log' ]( 'Building cache for ' + c.totalRows + + ' rows (showing ' + len + ' rows in log) and ' + c.columns + ' columns' + + ts.benchmark( cacheTime ) ); + val = {}; + for ( colIndex = 0; colIndex < c.columns; colIndex++ ) { + for ( cacheIndex = 0; cacheIndex < len; cacheIndex++ ) { + if ( !val[ 'row: ' + cacheIndex ] ) { + val[ 'row: ' + cacheIndex ] = {}; + } + val[ 'row: ' + cacheIndex ][ c.$headerIndexed[ colIndex ].text() ] = + c.cache[ 0 ].normalized[ cacheIndex ][ colIndex ]; + } + } + console[ console.table ? 'table' : 'log' ]( val ); + if ( console.groupEnd ) { console.groupEnd(); } + } + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, + + getColumnText : function( table, column, callback, rowFilter ) { + table = $( table )[0]; + var tbodyIndex, rowIndex, cache, row, tbodyLen, rowLen, raw, parsed, $cell, result, + hasCallback = typeof callback === 'function', + allColumns = column === 'all', + data = { raw : [], parsed: [], $cell: [] }, + c = table.config; + if ( ts.isEmptyObject( c ) ) { + if ( ts.debug(c, 'core') ) { + console.warn( 'No cache found - aborting getColumnText function!' ); + } + } else { + tbodyLen = c.$tbodies.length; + for ( tbodyIndex = 0; tbodyIndex < tbodyLen; tbodyIndex++ ) { + cache = c.cache[ tbodyIndex ].normalized; + rowLen = cache.length; + for ( rowIndex = 0; rowIndex < rowLen; rowIndex++ ) { + row = cache[ rowIndex ]; + if ( rowFilter && !row[ c.columns ].$row.is( rowFilter ) ) { + continue; + } + result = true; + parsed = ( allColumns ) ? row.slice( 0, c.columns ) : row[ column ]; + row = row[ c.columns ]; + raw = ( allColumns ) ? row.raw : row.raw[ column ]; + $cell = ( allColumns ) ? row.$row.children() : row.$row.children().eq( column ); + if ( hasCallback ) { + result = callback({ + tbodyIndex : tbodyIndex, + rowIndex : rowIndex, + parsed : parsed, + raw : raw, + $row : row.$row, + $cell : $cell + }); + } + if ( result !== false ) { + data.parsed[ data.parsed.length ] = parsed; + data.raw[ data.raw.length ] = raw; + data.$cell[ data.$cell.length ] = $cell; + } + } + } + // return everything + return data; + } + }, + + /* + ██ ██ █████▄ █████▄ ▄████▄ ██████ ██████ + ██ ██ ██▄▄██ ██ ██ ██▄▄██ ██ ██▄▄ + ██ ██ ██▀▀▀ ██ ██ ██▀▀██ ██ ██▀▀ + ▀████▀ ██ █████▀ ██ ██ ██ ██████ + */ + setHeadersCss : function( c ) { + var indx, column, + list = c.sortList, + len = list.length, + none = ts.css.sortNone + ' ' + c.cssNone, + css = [ ts.css.sortAsc + ' ' + c.cssAsc, ts.css.sortDesc + ' ' + c.cssDesc ], + cssIcon = [ c.cssIconAsc, c.cssIconDesc, c.cssIconNone ], + aria = [ 'ascending', 'descending' ], + updateColumnSort = function($el, index) { + $el + .removeClass( none ) + .addClass( css[ index ] ) + .attr( 'aria-sort', aria[ index ] ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon[ 2 ] ) + .addClass( cssIcon[ index ] ); + }, + // find the footer + $extras = c.$table + .find( 'tfoot tr' ) + .children( 'td, th' ) + .add( $( c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ), + // remove all header information + $sorted = c.$headers + .add( $( 'thead ' + c.namespace + '_extra_headers' ) ) + .removeClass( css.join( ' ' ) ) + .addClass( none ) + .attr( 'aria-sort', 'none' ) + .find( '.' + ts.css.icon ) + .removeClass( cssIcon.join( ' ' ) ) + .end(); + // add css none to all sortable headers + $sorted + .not( '.sorter-false' ) + .find( '.' + ts.css.icon ) + .addClass( cssIcon[ 2 ] ); + // add disabled css icon class + if ( c.cssIconDisabled ) { + $sorted + .filter( '.sorter-false' ) + .find( '.' + ts.css.icon ) + .addClass( c.cssIconDisabled ); + } + for ( indx = 0; indx < len; indx++ ) { + // direction = 2 means reset! + if ( list[ indx ][ 1 ] !== 2 ) { + // multicolumn sorting updating - see #1005 + // .not(function() {}) needs jQuery 1.4 + // filter(function(i, el) {}) <- el is undefined in jQuery v1.2.6 + $sorted = c.$headers.filter( function( i ) { + // only include headers that are in the sortList (this includes colspans) + var include = true, + $el = c.$headers.eq( i ), + col = parseInt( $el.attr( 'data-column' ), 10 ), + end = col + ts.getClosest( $el, 'th, td' )[0].colSpan; + for ( ; col < end; col++ ) { + include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false; + } + return include; + }); + + // choose the :last in case there are nested columns + $sorted = $sorted + .not( '.sorter-false' ) + .filter( '[data-column="' + list[ indx ][ 0 ] + '"]' + ( len === 1 ? ':last' : '' ) ); + if ( $sorted.length ) { + for ( column = 0; column < $sorted.length; column++ ) { + if ( !$sorted[ column ].sortDisabled ) { + updateColumnSort( $sorted.eq( column ), list[ indx ][ 1 ] ); + } + } + } + // add sorted class to footer & extra headers, if they exist + if ( $extras.length ) { + updateColumnSort( $extras.filter( '[data-column="' + list[ indx ][ 0 ] + '"]' ), list[ indx ][ 1 ] ); + } + } + } + // add verbose aria labels + len = c.$headers.length; + for ( indx = 0; indx < len; indx++ ) { + ts.setColumnAriaLabel( c, c.$headers.eq( indx ) ); + } + }, + + getClosest : function( $el, selector ) { + // jQuery v1.2.6 doesn't have closest() + if ( $.fn.closest ) { + return $el.closest( selector ); + } + return $el.is( selector ) ? + $el : + $el.parents( selector ).filter( ':first' ); + }, + + // nextSort (optional), lets you disable next sort text + setColumnAriaLabel : function( c, $header, nextSort ) { + if ( $header.length ) { + var column = parseInt( $header.attr( 'data-column' ), 10 ), + vars = c.sortVars[ column ], + tmp = $header.hasClass( ts.css.sortAsc ) ? + 'sortAsc' : + $header.hasClass( ts.css.sortDesc ) ? 'sortDesc' : 'sortNone', + txt = $.trim( $header.text() ) + ': ' + ts.language[ tmp ]; + if ( $header.hasClass( 'sorter-false' ) || nextSort === false ) { + txt += ts.language.sortDisabled; + } else { + tmp = ( vars.count + 1 ) % vars.order.length; + nextSort = vars.order[ tmp ]; + // if nextSort + txt += ts.language[ nextSort === 0 ? 'nextAsc' : nextSort === 1 ? 'nextDesc' : 'nextNone' ]; + } + $header.attr( 'aria-label', txt ); + if (vars.sortedBy) { + $header.attr( 'data-sortedBy', vars.sortedBy ); + } else { + $header.removeAttr('data-sortedBy'); + } + } + }, + + updateHeader : function( c ) { + var index, isDisabled, $header, col, + table = c.table, + len = c.$headers.length; + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + col = ts.getColumnData( table, c.headers, index, true ); + // add 'sorter-false' class if 'parser-false' is set + isDisabled = ts.getData( $header, col, 'sorter' ) === 'false' || ts.getData( $header, col, 'parser' ) === 'false'; + ts.setColumnSort( c, $header, isDisabled ); + } + }, + + setColumnSort : function( c, $header, isDisabled ) { + var id = c.table.id; + $header[ 0 ].sortDisabled = isDisabled; + $header[ isDisabled ? 'addClass' : 'removeClass' ]( 'sorter-false' ) + .attr( 'aria-disabled', '' + isDisabled ); + // disable tab index on disabled cells + if ( c.tabIndex ) { + if ( isDisabled ) { + $header.removeAttr( 'tabindex' ); + } else { + $header.attr( 'tabindex', '0' ); + } + } + // aria-controls - requires table ID + if ( id ) { + if ( isDisabled ) { + $header.removeAttr( 'aria-controls' ); + } else { + $header.attr( 'aria-controls', id ); + } + } + }, + + updateHeaderSortCount : function( c, list ) { + var col, dir, group, indx, primary, temp, val, order, + sortList = list || c.sortList, + len = sortList.length; + c.sortList = []; + for ( indx = 0; indx < len; indx++ ) { + val = sortList[ indx ]; + // ensure all sortList values are numeric - fixes #127 + col = parseInt( val[ 0 ], 10 ); + // prevents error if sorton array is wrong + if ( col < c.columns ) { + + // set order if not already defined - due to colspan header without associated header cell + // adding this check prevents a javascript error + if ( !c.sortVars[ col ].order ) { + if ( ts.getOrder( c.sortInitialOrder ) ) { + order = c.sortReset ? [ 1, 0, 2 ] : [ 1, 0 ]; + } else { + order = c.sortReset ? [ 0, 1, 2 ] : [ 0, 1 ]; + } + c.sortVars[ col ].order = order; + c.sortVars[ col ].count = 0; + } + + order = c.sortVars[ col ].order; + dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ ); + dir = dir ? dir[ 0 ] : ''; + // 0/(a)sc (default), 1/(d)esc, (s)ame, (o)pposite, (n)ext + switch ( dir ) { + case '1' : case 'd' : // descending + dir = 1; + break; + case 's' : // same direction (as primary column) + // if primary sort is set to 's', make it ascending + dir = primary || 0; + break; + case 'o' : + temp = order[ ( primary || 0 ) % order.length ]; + // opposite of primary column; but resets if primary resets + dir = temp === 0 ? 1 : temp === 1 ? 0 : 2; + break; + case 'n' : + dir = order[ ( ++c.sortVars[ col ].count ) % order.length ]; + break; + default : // ascending + dir = 0; + break; + } + primary = indx === 0 ? dir : primary; + group = [ col, parseInt( dir, 10 ) || 0 ]; + c.sortList[ c.sortList.length ] = group; + dir = $.inArray( group[ 1 ], order ); // fixes issue #167 + c.sortVars[ col ].count = dir >= 0 ? dir : group[ 1 ] % order.length; + } + } + }, + + updateAll : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + ts.refreshWidgets( table, true, true ); + ts.buildHeaders( c ); + ts.bindEvents( table, c.$headers, true ); + ts.bindMethods( c ); + ts.commonUpdate( c, resort, callback ); + }, + + update : function( c, resort, callback ) { + var table = c.table; + table.isUpdating = true; + // update sorting (if enabled/disabled) + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + }, + + // simple header update - see #989 + updateHeaders : function( c, callback ) { + c.table.isUpdating = true; + ts.buildHeaders( c ); + ts.bindEvents( c.table, c.$headers, true ); + ts.resortComplete( c, callback ); + }, + + updateCell : function( c, cell, resort, callback ) { + // updateCell for child rows is a mess - we'll ignore them for now + // eventually I'll break out the "update" row cache code to make everything consistent + if ( $( cell ).closest( 'tr' ).hasClass( c.cssChildRow ) ) { + console.warn('Tablesorter Warning! "updateCell" for child row content has been disabled, use "update" instead'); + return; + } + if ( ts.isEmptyObject( c.cache ) ) { + // empty table, do an update instead - fixes #1099 + ts.updateHeader( c ); + ts.commonUpdate( c, resort, callback ); + return; + } + c.table.isUpdating = true; + c.$table.find( c.selectorRemove ).remove(); + // get position from the dom + var tmp, indx, row, icell, cache, len, + $tbodies = c.$tbodies, + $cell = $( cell ), + // update cache - format: function( s, table, cell, cellIndex ) + // no closest in jQuery v1.2.6 + tbodyIndex = $tbodies.index( ts.getClosest( $cell, 'tbody' ) ), + tbcache = c.cache[ tbodyIndex ], + $row = ts.getClosest( $cell, 'tr' ); + cell = $cell[ 0 ]; // in case cell is a jQuery object + // tbody may not exist if update is initialized while tbody is removed for processing + if ( $tbodies.length && tbodyIndex >= 0 ) { + row = $tbodies.eq( tbodyIndex ).find( 'tr' ).not( '.' + c.cssChildRow ).index( $row ); + cache = tbcache.normalized[ row ]; + len = $row[ 0 ].cells.length; + if ( len !== c.columns ) { + // colspan in here somewhere! + icell = 0; + tmp = false; + for ( indx = 0; indx < len; indx++ ) { + if ( !tmp && $row[ 0 ].cells[ indx ] !== cell ) { + icell += $row[ 0 ].cells[ indx ].colSpan; + } else { + tmp = true; + } + } + } else { + icell = $cell.index(); + } + tmp = ts.getElementText( c, cell, icell ); // raw + cache[ c.columns ].raw[ icell ] = tmp; + tmp = ts.getParsedText( c, cell, icell, tmp ); + cache[ icell ] = tmp; // parsed + if ( ( c.parsers[ icell ].type || '' ).toLowerCase() === 'numeric' ) { + // update column max value (ignore sign) + tbcache.colMax[ icell ] = Math.max( Math.abs( tmp ) || 0, tbcache.colMax[ icell ] || 0 ); + } + tmp = resort !== 'undefined' ? resort : c.resort; + if ( tmp !== false ) { + // widgets will be reapplied + ts.checkResort( c, tmp, callback ); + } else { + // don't reapply widgets is resort is false, just in case it causes + // problems with element focus + ts.resortComplete( c, callback ); + } + } else { + if ( ts.debug(c, 'core') ) { + console.error( 'updateCell aborted, tbody missing or not within the indicated table' ); + } + c.table.isUpdating = false; + } + }, + + addRows : function( c, $row, resort, callback ) { + var txt, val, tbodyIndex, rowIndex, rows, cellIndex, len, order, + cacheIndex, rowData, cells, cell, span, + // allow passing a row string if only one non-info tbody exists in the table + valid = typeof $row === 'string' && c.$tbodies.length === 1 && / 0 ) { + cacheIndex += span; + } + cacheIndex++; + } + // add the row data to the end + cells[ c.columns ] = rowData; + // update cache + c.cache[ tbodyIndex ].normalized[ order ] = cells; + } + // resort using current settings + ts.checkResort( c, resort, callback ); + } + }, + + updateCache : function( c, callback, $tbodies ) { + // rebuild parsers + if ( !( c.parsers && c.parsers.length ) ) { + ts.setupParsers( c, $tbodies ); + } + // rebuild the cache map + ts.buildCache( c, callback, $tbodies ); + }, + + // init flag (true) used by pager plugin to prevent widget application + // renamed from appendToTable + appendCache : function( c, init ) { + var parsed, totalRows, $tbody, $curTbody, rowIndex, tbodyIndex, appendTime, + table = c.table, + $tbodies = c.$tbodies, + rows = [], + cache = c.cache; + // empty table - fixes #206/#346 + if ( ts.isEmptyObject( cache ) ) { + // run pager appender in case the table was just emptied + return c.appender ? c.appender( table, rows ) : + table.isUpdating ? c.$table.triggerHandler( 'updateComplete', table ) : ''; // Fixes #532 + } + if ( ts.debug(c, 'core') ) { + appendTime = new Date(); + } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = $tbodies.eq( tbodyIndex ); + if ( $tbody.length ) { + // detach tbody for manipulation + $curTbody = ts.processTbody( table, $tbody, true ); + parsed = cache[ tbodyIndex ].normalized; + totalRows = parsed.length; + for ( rowIndex = 0; rowIndex < totalRows; rowIndex++ ) { + rows[rows.length] = parsed[ rowIndex ][ c.columns ].$row; + // removeRows used by the pager plugin; don't render if using ajax - fixes #411 + if ( !c.appender || ( c.pager && !c.pager.removeRows && !c.pager.ajax ) ) { + $curTbody.append( parsed[ rowIndex ][ c.columns ].$row ); + } + } + // restore tbody + ts.processTbody( table, $curTbody, false ); + } + } + if ( c.appender ) { + c.appender( table, rows ); + } + if ( ts.debug(c, 'core') ) { + console.log( 'Rebuilt table' + ts.benchmark( appendTime ) ); + } + // apply table widgets; but not before ajax completes + if ( !init && !c.appender ) { + ts.applyWidget( table ); + } + if ( table.isUpdating ) { + c.$table.triggerHandler( 'updateComplete', table ); + } + }, + + commonUpdate : function( c, resort, callback ) { + // remove rows/elements before update + c.$table.find( c.selectorRemove ).remove(); + // rebuild parsers + ts.setupParsers( c ); + // rebuild the cache map + ts.buildCache( c ); + ts.checkResort( c, resort, callback ); + }, + + /* + ▄█████ ▄████▄ █████▄ ██████ ██ █████▄ ▄████▄ + ▀█▄ ██ ██ ██▄▄██ ██ ██ ██ ██ ██ ▄▄▄ + ▀█▄ ██ ██ ██▀██ ██ ██ ██ ██ ██ ▀██ + █████▀ ▀████▀ ██ ██ ██ ██ ██ ██ ▀████▀ + */ + initSort : function( c, cell, event ) { + if ( c.table.isUpdating ) { + // let any updates complete before initializing a sort + return setTimeout( function() { + ts.initSort( c, cell, event ); + }, 50 ); + } + + var arry, indx, headerIndx, dir, temp, tmp, $header, + notMultiSort = !event[ c.sortMultiSortKey ], + table = c.table, + len = c.$headers.length, + th = ts.getClosest( $( cell ), 'th, td' ), + col = parseInt( th.attr( 'data-column' ), 10 ), + sortedBy = event.type === 'mouseup' ? 'user' : event.type, + order = c.sortVars[ col ].order; + th = th[0]; + // Only call sortStart if sorting is enabled + c.$table.triggerHandler( 'sortStart', table ); + // get current column sort order + tmp = ( c.sortVars[ col ].count + 1 ) % order.length; + c.sortVars[ col ].count = event[ c.sortResetKey ] ? 2 : tmp; + // reset all sorts on non-current column - issue #30 + if ( c.sortRestart ) { + for ( headerIndx = 0; headerIndx < len; headerIndx++ ) { + $header = c.$headers.eq( headerIndx ); + tmp = parseInt( $header.attr( 'data-column' ), 10 ); + // only reset counts on columns that weren't just clicked on and if not included in a multisort + if ( col !== tmp && ( notMultiSort || $header.hasClass( ts.css.sortNone ) ) ) { + c.sortVars[ tmp ].count = -1; + } + } + } + // user only wants to sort on one column + if ( notMultiSort ) { + $.each( c.sortVars, function( i ) { + c.sortVars[ i ].sortedBy = ''; + }); + // flush the sort list + c.sortList = []; + c.last.sortList = []; + if ( c.sortForce !== null ) { + arry = c.sortForce; + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col ) { + c.sortList[ c.sortList.length ] = arry[ indx ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortForce'; + } + } + } + // add column to sort list + dir = order[ c.sortVars[ col ].count ]; + if ( dir < 2 ) { + c.sortList[ c.sortList.length ] = [ col, dir ]; + c.sortVars[ col ].sortedBy = sortedBy; + // add other columns if header spans across multiple + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; + } + } + } + // multi column sorting + } else { + // get rid of the sortAppend before adding more - fixes issue #115 & #523 + c.sortList = $.extend( [], c.last.sortList ); + + // the user has clicked on an already sorted column + if ( ts.isValueInArray( col, c.sortList ) >= 0 ) { + // reverse the sorting direction + c.sortVars[ col ].sortedBy = sortedBy; + for ( indx = 0; indx < c.sortList.length; indx++ ) { + tmp = c.sortList[ indx ]; + if ( tmp[ 0 ] === col ) { + // order.count seems to be incorrect when compared to cell.count + tmp[ 1 ] = order[ c.sortVars[ col ].count ]; + if ( tmp[1] === 2 ) { + c.sortList.splice( indx, 1 ); + c.sortVars[ col ].count = -1; + } + } + } + } else { + // add column to sort list array + dir = order[ c.sortVars[ col ].count ]; + c.sortVars[ col ].sortedBy = sortedBy; + if ( dir < 2 ) { + c.sortList[ c.sortList.length ] = [ col, dir ]; + // add other columns if header spans across multiple + if ( th.colSpan > 1 ) { + for ( indx = 1; indx < th.colSpan; indx++ ) { + c.sortList[ c.sortList.length ] = [ col + indx, dir ]; + // update count on columns in colSpan + c.sortVars[ col + indx ].count = $.inArray( dir, order ); + c.sortVars[ col + indx ].sortedBy = sortedBy; + } + } + } + } + } + // save sort before applying sortAppend + c.last.sortList = $.extend( [], c.sortList ); + if ( c.sortList.length && c.sortAppend ) { + arry = $.isArray( c.sortAppend ) ? c.sortAppend : c.sortAppend[ c.sortList[ 0 ][ 0 ] ]; + if ( !ts.isEmptyObject( arry ) ) { + for ( indx = 0; indx < arry.length; indx++ ) { + if ( arry[ indx ][ 0 ] !== col && ts.isValueInArray( arry[ indx ][ 0 ], c.sortList ) < 0 ) { + dir = arry[ indx ][ 1 ]; + temp = ( '' + dir ).match( /^(a|d|s|o|n)/ ); + if ( temp ) { + tmp = c.sortList[ 0 ][ 1 ]; + switch ( temp[ 0 ] ) { + case 'd' : + dir = 1; + break; + case 's' : + dir = tmp; + break; + case 'o' : + dir = tmp === 0 ? 1 : 0; + break; + case 'n' : + dir = ( tmp + 1 ) % order.length; + break; + default: + dir = 0; + break; + } + } + c.sortList[ c.sortList.length ] = [ arry[ indx ][ 0 ], dir ]; + c.sortVars[ arry[ indx ][ 0 ] ].sortedBy = 'sortAppend'; + } + } + } + } + // sortBegin event triggered immediately before the sort + c.$table.triggerHandler( 'sortBegin', table ); + // setTimeout needed so the processing icon shows up + setTimeout( function() { + // set css for headers + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); + }, 1 ); + }, + + // sort multiple columns + multisort : function( c ) { /*jshint loopfunc:true */ + var tbodyIndex, sortTime, colMax, rows, tmp, + table = c.table, + sorter = [], + dir = 0, + textSorter = c.textSorter || '', + sortList = c.sortList, + sortLen = sortList.length, + len = c.$tbodies.length; + if ( c.serverSideSorting || ts.isEmptyObject( c.cache ) ) { + // empty table - fixes #206/#346 + return; + } + if ( ts.debug(c, 'core') ) { sortTime = new Date(); } + // cache textSorter to optimize speed + if ( typeof textSorter === 'object' ) { + colMax = c.columns; + while ( colMax-- ) { + tmp = ts.getColumnData( table, textSorter, colMax ); + if ( typeof tmp === 'function' ) { + sorter[ colMax ] = tmp; + } + } + } + for ( tbodyIndex = 0; tbodyIndex < len; tbodyIndex++ ) { + colMax = c.cache[ tbodyIndex ].colMax; + rows = c.cache[ tbodyIndex ].normalized; + + rows.sort( function( a, b ) { + var sortIndex, num, col, order, sort, x, y; + // rows is undefined here in IE, so don't use it! + for ( sortIndex = 0; sortIndex < sortLen; sortIndex++ ) { + col = sortList[ sortIndex ][ 0 ]; + order = sortList[ sortIndex ][ 1 ]; + // sort direction, true = asc, false = desc + dir = order === 0; + + if ( c.sortStable && a[ col ] === b[ col ] && sortLen === 1 ) { + return a[ c.columns ].order - b[ c.columns ].order; + } + + // fallback to natural sort since it is more robust + num = /n/i.test( ts.getSortType( c.parsers, col ) ); + if ( num && c.strings[ col ] ) { + // sort strings in numerical columns + if ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) { + num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 ); + } else { + num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0; + } + // fall back to built-in numeric sort + // var sort = $.tablesorter['sort' + s]( a[col], b[col], dir, colMax[col], table ); + sort = c.numberSorter ? c.numberSorter( a[ col ], b[ col ], dir, colMax[ col ], table ) : + ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ], b[ col ], num, colMax[ col ], col, c ); + } else { + // set a & b depending on sort direction + x = dir ? a : b; + y = dir ? b : a; + // text sort function + if ( typeof textSorter === 'function' ) { + // custom OVERALL text sorter + sort = textSorter( x[ col ], y[ col ], dir, col, table ); + } else if ( typeof sorter[ col ] === 'function' ) { + // custom text sorter for a SPECIFIC COLUMN + sort = sorter[ col ]( x[ col ], y[ col ], dir, col, table ); + } else { + // fall back to natural sort + sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( a[ col ] || '', b[ col ] || '', col, c ); + } + } + if ( sort ) { return sort; } + } + return a[ c.columns ].order - b[ c.columns ].order; + }); + } + if ( ts.debug(c, 'core') ) { + console.log( 'Applying sort ' + sortList.toString() + ts.benchmark( sortTime ) ); + } + }, + + resortComplete : function( c, callback ) { + if ( c.table.isUpdating ) { + c.$table.triggerHandler( 'updateComplete', c.table ); + } + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, + + checkResort : function( c, resort, callback ) { + var sortList = $.isArray( resort ) ? resort : c.sortList, + // if no resort parameter is passed, fallback to config.resort (true by default) + resrt = typeof resort === 'undefined' ? c.resort : resort; + // don't try to resort if the table is still processing + // this will catch spamming of the updateCell method + if ( resrt !== false && !c.serverSideSorting && !c.table.isProcessing ) { + if ( sortList.length ) { + ts.sortOn( c, sortList, function() { + ts.resortComplete( c, callback ); + }, true ); + } else { + ts.sortReset( c, function() { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } ); + } + } else { + ts.resortComplete( c, callback ); + ts.applyWidget( c.table, false ); + } + }, + + sortOn : function( c, list, callback, init ) { + var indx, + table = c.table; + c.$table.triggerHandler( 'sortStart', table ); + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].sortedBy = ts.isValueInArray( indx, list ) > -1 ? 'sorton' : ''; + } + // update header count index + ts.updateHeaderSortCount( c, list ); + // set css for headers + ts.setHeadersCss( c ); + // fixes #346 + if ( c.delayInit && ts.isEmptyObject( c.cache ) ) { + ts.buildCache( c ); + } + c.$table.triggerHandler( 'sortBegin', table ); + // sort the table and append it to the dom + ts.multisort( c ); + ts.appendCache( c, init ); + c.$table.triggerHandler( 'sortBeforeEnd', table ); + c.$table.triggerHandler( 'sortEnd', table ); + ts.applyWidget( table ); + if ( $.isFunction( callback ) ) { + callback( table ); + } + }, + + sortReset : function( c, callback ) { + c.sortList = []; + var indx; + for (indx = 0; indx < c.columns; indx++) { + c.sortVars[ indx ].count = -1; + c.sortVars[ indx ].sortedBy = ''; + } + ts.setHeadersCss( c ); + ts.multisort( c ); + ts.appendCache( c ); + if ( $.isFunction( callback ) ) { + callback( c.table ); + } + }, + + getSortType : function( parsers, column ) { + return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : ''; + }, + + getOrder : function( val ) { + // look for 'd' in 'desc' order; return true + return ( /^d/i.test( val ) || val === 1 ); + }, + + // Natural sort - https://github.com/overset/javascript-natural-sort (date sorting removed) + sortNatural : function( a, b ) { + if ( a === b ) { return 0; } + a = ( a || '' ).toString(); + b = ( b || '' ).toString(); + var aNum, bNum, aFloat, bFloat, indx, max, + regex = ts.regex; + // first try and sort Hex codes + if ( regex.hex.test( b ) ) { + aNum = parseInt( a.match( regex.hex ), 16 ); + bNum = parseInt( b.match( regex.hex ), 16 ); + if ( aNum < bNum ) { return -1; } + if ( aNum > bNum ) { return 1; } + } + // chunk/tokenize + aNum = a.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + bNum = b.replace( regex.chunk, '\\0$1\\0' ).replace( regex.chunks, '' ).split( '\\0' ); + max = Math.max( aNum.length, bNum.length ); + // natural sorting through split numeric strings and default strings + for ( indx = 0; indx < max; indx++ ) { + // find floats not starting with '0', string or 0 if not defined + aFloat = isNaN( aNum[ indx ] ) ? aNum[ indx ] || 0 : parseFloat( aNum[ indx ] ) || 0; + bFloat = isNaN( bNum[ indx ] ) ? bNum[ indx ] || 0 : parseFloat( bNum[ indx ] ) || 0; + // handle numeric vs string comparison - number < string - (Kyle Adams) + if ( isNaN( aFloat ) !== isNaN( bFloat ) ) { return isNaN( aFloat ) ? 1 : -1; } + // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2' + if ( typeof aFloat !== typeof bFloat ) { + aFloat += ''; + bFloat += ''; + } + if ( aFloat < bFloat ) { return -1; } + if ( aFloat > bFloat ) { return 1; } + } + return 0; + }, + + sortNaturalAsc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + return ts.sortNatural( a, b ); + }, + + sortNaturalDesc : function( a, b, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + return ts.sortNatural( b, a ); + }, + + // basic alphabetical sort + sortText : function( a, b ) { + return a > b ? 1 : ( a < b ? -1 : 0 ); + }, + + // return text string value by adding up ascii value + // so the text is somewhat sorted when using a digital sort + // this is NOT an alphanumeric sort + getTextValue : function( val, num, max ) { + if ( max ) { + // make sure the text value is greater than the max numerical value (max) + var indx, + len = val ? val.length : 0, + n = max + num; + for ( indx = 0; indx < len; indx++ ) { + n += val.charCodeAt( indx ); + } + return num * n; + } + return 0; + }, + + sortNumericAsc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : -empty || -1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : empty || 1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return a - b; + }, + + sortNumericDesc : function( a, b, num, max, col, c ) { + if ( a === b ) { return 0; } + var empty = ts.string[ ( c.empties[ col ] || c.emptyTo ) ]; + if ( a === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? -1 : 1 ) : empty || 1; } + if ( b === '' && empty !== 0 ) { return typeof empty === 'boolean' ? ( empty ? 1 : -1 ) : -empty || -1; } + if ( isNaN( a ) ) { a = ts.getTextValue( a, num, max ); } + if ( isNaN( b ) ) { b = ts.getTextValue( b, num, max ); } + return b - a; + }, + + sortNumeric : function( a, b ) { + return a - b; + }, + + /* + ██ ██ ██ ██ █████▄ ▄████▄ ██████ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ▄▄▄ ██▄▄ ██ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ▀██ ██▀▀ ██ ▀█▄ + ███████▀ ██ █████▀ ▀████▀ ██████ ██ █████▀ + */ + addWidget : function( widget ) { + if ( widget.id && !ts.isEmptyObject( ts.getWidgetById( widget.id ) ) ) { + console.warn( '"' + widget.id + '" widget was loaded more than once!' ); + } + ts.widgets[ ts.widgets.length ] = widget; + }, + + hasWidget : function( $table, name ) { + $table = $( $table ); + return $table.length && $table[ 0 ].config && $table[ 0 ].config.widgetInit[ name ] || false; + }, + + getWidgetById : function( name ) { + var indx, widget, + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id && widget.id.toLowerCase() === name.toLowerCase() ) { + return widget; + } + } + }, + + applyWidgetOptions : function( table ) { + var indx, widget, wo, + c = table.config, + len = c.widgets.length; + if ( len ) { + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( c.widgets[ indx ] ); + if ( widget && widget.options ) { + wo = $.extend( true, {}, widget.options ); + c.widgetOptions = $.extend( true, wo, c.widgetOptions ); + // add widgetOptions to defaults for option validator + $.extend( true, ts.defaults.widgetOptions, widget.options ); + } + } + } + }, + + addWidgetFromClass : function( table ) { + var len, indx, + c = table.config, + // look for widgets to apply from table class + // don't match from 'ui-widget-content'; use \S instead of \w to include widgets + // with dashes in the name, e.g. "widget-test-2" extracts out "test-2" + regex = '^' + c.widgetClass.replace( ts.regex.templateName, '(\\S+)+' ) + '$', + widgetClass = new RegExp( regex, 'g' ), + // split up table class (widget id's can include dashes) - stop using match + // otherwise only one widget gets extracted, see #1109 + widgets = ( table.className || '' ).split( ts.regex.spaces ); + if ( widgets.length ) { + len = widgets.length; + for ( indx = 0; indx < len; indx++ ) { + if ( widgets[ indx ].match( widgetClass ) ) { + c.widgets[ c.widgets.length ] = widgets[ indx ].replace( widgetClass, '$1' ); + } + } + } + }, + + applyWidgetId : function( table, id, init ) { + table = $(table)[0]; + var applied, time, name, + c = table.config, + wo = c.widgetOptions, + debug = ts.debug(c, 'core'), + widget = ts.getWidgetById( id ); + if ( widget ) { + name = widget.id; + applied = false; + // add widget name to option list so it gets reapplied after sorting, filtering, etc + if ( $.inArray( name, c.widgets ) < 0 ) { + c.widgets[ c.widgets.length ] = name; + } + if ( debug ) { time = new Date(); } + + if ( init || !( c.widgetInit[ name ] ) ) { + // set init flag first to prevent calling init more than once (e.g. pager) + c.widgetInit[ name ] = true; + if ( table.hasInitialized ) { + // don't reapply widget options on tablesorter init + ts.applyWidgetOptions( table ); + } + if ( typeof widget.init === 'function' ) { + applied = true; + if ( debug ) { + console[ console.group ? 'group' : 'log' ]( 'Initializing ' + name + ' widget' ); + } + widget.init( table, widget, c, wo ); + } + } + if ( !init && typeof widget.format === 'function' ) { + applied = true; + if ( debug ) { + console[ console.group ? 'group' : 'log' ]( 'Updating ' + name + ' widget' ); + } + widget.format( table, c, wo, false ); + } + if ( debug ) { + if ( applied ) { + console.log( 'Completed ' + ( init ? 'initializing ' : 'applying ' ) + name + ' widget' + ts.benchmark( time ) ); + if ( console.groupEnd ) { console.groupEnd(); } + } + } + } + }, + + applyWidget : function( table, init, callback ) { + table = $( table )[ 0 ]; // in case this is called externally + var indx, len, names, widget, time, + c = table.config, + debug = ts.debug(c, 'core'), + widgets = []; + // prevent numerous consecutive widget applications + if ( init !== false && table.hasInitialized && ( table.isApplyingWidgets || table.isUpdating ) ) { + return; + } + if ( debug ) { time = new Date(); } + ts.addWidgetFromClass( table ); + // prevent "tablesorter-ready" from firing multiple times in a row + clearTimeout( c.timerReady ); + if ( c.widgets.length ) { + table.isApplyingWidgets = true; + // ensure unique widget ids + c.widgets = $.grep( c.widgets, function( val, index ) { + return $.inArray( val, c.widgets ) === index; + }); + names = c.widgets || []; + len = names.length; + // build widget array & add priority as needed + for ( indx = 0; indx < len; indx++ ) { + widget = ts.getWidgetById( names[ indx ] ); + if ( widget && widget.id ) { + // set priority to 10 if not defined + if ( !widget.priority ) { widget.priority = 10; } + widgets[ indx ] = widget; + } else if ( debug ) { + console.warn( '"' + names[ indx ] + '" was enabled, but the widget code has not been loaded!' ); + } + } + // sort widgets by priority + widgets.sort( function( a, b ) { + return a.priority < b.priority ? -1 : a.priority === b.priority ? 0 : 1; + }); + // add/update selected widgets + len = widgets.length; + if ( debug ) { + console[ console.group ? 'group' : 'log' ]( 'Start ' + ( init ? 'initializing' : 'applying' ) + ' widgets' ); + } + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget && widget.id ) { + ts.applyWidgetId( table, widget.id, init ); + } + } + if ( debug && console.groupEnd ) { console.groupEnd(); } + } + c.timerReady = setTimeout( function() { + table.isApplyingWidgets = false; + $.data( table, 'lastWidgetApplication', new Date() ); + c.$table.triggerHandler( 'tablesorter-ready' ); + // callback executed on init only + if ( !init && typeof callback === 'function' ) { + callback( table ); + } + if ( debug ) { + widget = c.widgets.length; + console.log( 'Completed ' + + ( init === true ? 'initializing ' : 'applying ' ) + widget + + ' widget' + ( widget !== 1 ? 's' : '' ) + ts.benchmark( time ) ); + } + }, 10 ); + }, + + removeWidget : function( table, name, refreshing ) { + table = $( table )[ 0 ]; + var index, widget, indx, len, + c = table.config; + // if name === true, add all widgets from $.tablesorter.widgets + if ( name === true ) { + name = []; + len = ts.widgets.length; + for ( indx = 0; indx < len; indx++ ) { + widget = ts.widgets[ indx ]; + if ( widget && widget.id ) { + name[ name.length ] = widget.id; + } + } + } else { + // name can be either an array of widgets names, + // or a space/comma separated list of widget names + name = ( $.isArray( name ) ? name.join( ',' ) : name || '' ).toLowerCase().split( /[\s,]+/ ); + } + len = name.length; + for ( index = 0; index < len; index++ ) { + widget = ts.getWidgetById( name[ index ] ); + indx = $.inArray( name[ index ], c.widgets ); + // don't remove the widget from config.widget if refreshing + if ( indx >= 0 && refreshing !== true ) { + c.widgets.splice( indx, 1 ); + } + if ( widget && widget.remove ) { + if ( ts.debug(c, 'core') ) { + console.log( ( refreshing ? 'Refreshing' : 'Removing' ) + ' "' + name[ index ] + '" widget' ); + } + widget.remove( table, c, c.widgetOptions, refreshing ); + c.widgetInit[ name[ index ] ] = false; + } + } + c.$table.triggerHandler( 'widgetRemoveEnd', table ); + }, + + refreshWidgets : function( table, doAll, dontapply ) { + table = $( table )[ 0 ]; // see issue #243 + var indx, widget, + c = table.config, + curWidgets = c.widgets, + widgets = ts.widgets, + len = widgets.length, + list = [], + callback = function( table ) { + $( table ).triggerHandler( 'refreshComplete' ); + }; + // remove widgets not defined in config.widgets, unless doAll is true + for ( indx = 0; indx < len; indx++ ) { + widget = widgets[ indx ]; + if ( widget && widget.id && ( doAll || $.inArray( widget.id, curWidgets ) < 0 ) ) { + list[ list.length ] = widget.id; + } + } + ts.removeWidget( table, list.join( ',' ), true ); + if ( dontapply !== true ) { + // call widget init if + ts.applyWidget( table, doAll || false, callback ); + if ( doAll ) { + // apply widget format + ts.applyWidget( table, false, callback ); + } + } else { + callback( table ); + } + }, + + /* + ██ ██ ██████ ██ ██ ██ ██████ ██ ██████ ▄█████ + ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ▀█▄ + ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀█▄ + ▀████▀ ██ ██ ██████ ██ ██ ██ ██████ █████▀ + */ + benchmark : function( diff ) { + return ( ' (' + ( new Date().getTime() - diff.getTime() ) + ' ms)' ); + }, + // deprecated ts.log + log : function() { + console.log( arguments ); + }, + debug : function(c, name) { + return c && ( + c.debug === true || + typeof c.debug === 'string' && c.debug.indexOf(name) > -1 + ); + }, + + // $.isEmptyObject from jQuery v1.4 + isEmptyObject : function( obj ) { + /*jshint forin: false */ + for ( var name in obj ) { + return false; + } + return true; + }, + + isValueInArray : function( column, arry ) { + var indx, + len = arry && arry.length || 0; + for ( indx = 0; indx < len; indx++ ) { + if ( arry[ indx ][ 0 ] === column ) { + return indx; + } + } + return -1; + }, + + formatFloat : function( str, table ) { + if ( typeof str !== 'string' || str === '' ) { return str; } + // allow using formatFloat without a table; defaults to US number format + var num, + usFormat = table && table.config ? table.config.usNumberFormat !== false : + typeof table !== 'undefined' ? table : true; + if ( usFormat ) { + // US Format - 1,234,567.89 -> 1234567.89 + str = str.replace( ts.regex.comma, '' ); + } else { + // German Format = 1.234.567,89 -> 1234567.89 + // French Format = 1 234 567,89 -> 1234567.89 + str = str.replace( ts.regex.digitNonUS, '' ).replace( ts.regex.comma, '.' ); + } + if ( ts.regex.digitNegativeTest.test( str ) ) { + // make (#) into a negative number -> (10) = -10 + str = str.replace( ts.regex.digitNegativeReplace, '-$1' ); + } + num = parseFloat( str ); + // return the text instead of zero + return isNaN( num ) ? $.trim( str ) : num; + }, + + isDigit : function( str ) { + // replace all unwanted chars and match + return isNaN( str ) ? + ts.regex.digitTest.test( str.toString().replace( ts.regex.digitReplace, '' ) ) : + str !== ''; + }, + + // computeTableHeaderCellIndexes from: + // http://www.javascripttoolbox.com/lib/table/examples.php + // http://www.javascripttoolbox.com/temp/table_cellindex.html + computeColumnIndex : function( $rows, c ) { + var i, j, k, l, cell, cells, rowIndex, rowSpan, colSpan, firstAvailCol, + // total columns has been calculated, use it to set the matrixrow + columns = c && c.columns || 0, + matrix = [], + matrixrow = new Array( columns ); + for ( i = 0; i < $rows.length; i++ ) { + cells = $rows[ i ].cells; + for ( j = 0; j < cells.length; j++ ) { + cell = cells[ j ]; + rowIndex = i; + rowSpan = cell.rowSpan || 1; + colSpan = cell.colSpan || 1; + if ( typeof matrix[ rowIndex ] === 'undefined' ) { + matrix[ rowIndex ] = []; + } + // Find first available column in the first row + for ( k = 0; k < matrix[ rowIndex ].length + 1; k++ ) { + if ( typeof matrix[ rowIndex ][ k ] === 'undefined' ) { + firstAvailCol = k; + break; + } + } + // jscs:disable disallowEmptyBlocks + if ( columns && cell.cellIndex === firstAvailCol ) { + // don't to anything + } else if ( cell.setAttribute ) { + // jscs:enable disallowEmptyBlocks + // add data-column (setAttribute = IE8+) + cell.setAttribute( 'data-column', firstAvailCol ); + } else { + // remove once we drop support for IE7 - 1/12/2016 + $( cell ).attr( 'data-column', firstAvailCol ); + } + for ( k = rowIndex; k < rowIndex + rowSpan; k++ ) { + if ( typeof matrix[ k ] === 'undefined' ) { + matrix[ k ] = []; + } + matrixrow = matrix[ k ]; + for ( l = firstAvailCol; l < firstAvailCol + colSpan; l++ ) { + matrixrow[ l ] = 'x'; + } + } + } + } + ts.checkColumnCount($rows, matrix, matrixrow.length); + return matrixrow.length; + }, + + checkColumnCount : function($rows, matrix, columns) { + // this DOES NOT report any tbody column issues, except for the math and + // and column selector widgets + var i, len, + valid = true, + cells = []; + for ( i = 0; i < matrix.length; i++ ) { + // some matrix entries are undefined when testing the footer because + // it is using the rowIndex property + if ( matrix[i] ) { + len = matrix[i].length; + if ( matrix[i].length !== columns ) { + valid = false; + break; + } + } + } + if ( !valid ) { + $rows.each( function( indx, el ) { + var cell = el.parentElement.nodeName; + if ( cells.indexOf( cell ) < 0 ) { + cells.push( cell ); + } + }); + console.error( + 'Invalid or incorrect number of columns in the ' + + cells.join( ' or ' ) + '; expected ' + columns + + ', but found ' + len + ' columns' + ); + } + }, + + // automatically add a colgroup with col elements set to a percentage width + fixColumnWidth : function( table ) { + table = $( table )[ 0 ]; + var overallWidth, percent, $tbodies, len, index, + c = table.config, + $colgroup = c.$table.children( 'colgroup' ); + // remove plugin-added colgroup, in case we need to refresh the widths + if ( $colgroup.length && $colgroup.hasClass( ts.css.colgroup ) ) { + $colgroup.remove(); + } + if ( c.widthFixed && c.$table.children( 'colgroup' ).length === 0 ) { + $colgroup = $( '' ); + overallWidth = c.$table.width(); + // only add col for visible columns - fixes #371 + $tbodies = c.$tbodies.find( 'tr:first' ).children( ':visible' ); + len = $tbodies.length; + for ( index = 0; index < len; index++ ) { + percent = parseInt( ( $tbodies.eq( index ).width() / overallWidth ) * 1000, 10 ) / 10 + '%'; + $colgroup.append( $( '' ).css( 'width', percent ) ); + } + c.$table.prepend( $colgroup ); + } + }, + + // get sorter, string, empty, etc options for each column from + // jQuery data, metadata, header option or header class name ('sorter-false') + // priority = jQuery data > meta > headers option > header class name + getData : function( header, configHeader, key ) { + var meta, cl4ss, + val = '', + $header = $( header ); + if ( !$header.length ) { return ''; } + meta = $.metadata ? $header.metadata() : false; + cl4ss = ' ' + ( $header.attr( 'class' ) || '' ); + if ( typeof $header.data( key ) !== 'undefined' || + typeof $header.data( key.toLowerCase() ) !== 'undefined' ) { + // 'data-lockedOrder' is assigned to 'lockedorder'; but 'data-locked-order' is assigned to 'lockedOrder' + // 'data-sort-initial-order' is assigned to 'sortInitialOrder' + val += $header.data( key ) || $header.data( key.toLowerCase() ); + } else if ( meta && typeof meta[ key ] !== 'undefined' ) { + val += meta[ key ]; + } else if ( configHeader && typeof configHeader[ key ] !== 'undefined' ) { + val += configHeader[ key ]; + } else if ( cl4ss !== ' ' && cl4ss.match( ' ' + key + '-' ) ) { + // include sorter class name 'sorter-text', etc; now works with 'sorter-my-custom-parser' + val = cl4ss.match( new RegExp( '\\s' + key + '-([\\w-]+)' ) )[ 1 ] || ''; + } + return $.trim( val ); + }, + + getColumnData : function( table, obj, indx, getCell, $headers ) { + if ( typeof obj !== 'object' || obj === null ) { + return obj; + } + table = $( table )[ 0 ]; + var $header, key, + c = table.config, + $cells = ( $headers || c.$headers ), + // c.$headerIndexed is not defined initially + $cell = c.$headerIndexed && c.$headerIndexed[ indx ] || + $cells.find( '[data-column="' + indx + '"]:last' ); + if ( typeof obj[ indx ] !== 'undefined' ) { + return getCell ? obj[ indx ] : obj[ $cells.index( $cell ) ]; + } + for ( key in obj ) { + if ( typeof key === 'string' ) { + $header = $cell + // header cell with class/id + .filter( key ) + // find elements within the header cell with cell/id + .add( $cell.find( key ) ); + if ( $header.length ) { + return obj[ key ]; + } + } + } + return; + }, + + // *** Process table *** + // add processing indicator + isProcessing : function( $table, toggle, $headers ) { + $table = $( $table ); + var c = $table[ 0 ].config, + // default to all headers + $header = $headers || $table.find( '.' + ts.css.header ); + if ( toggle ) { + // don't use sortList if custom $headers used + if ( typeof $headers !== 'undefined' && c.sortList.length > 0 ) { + // get headers from the sortList + $header = $header.filter( function() { + // get data-column from attr to keep compatibility with jQuery 1.2.6 + return this.sortDisabled ? + false : + ts.isValueInArray( parseFloat( $( this ).attr( 'data-column' ) ), c.sortList ) >= 0; + }); + } + $table.add( $header ).addClass( ts.css.processing + ' ' + c.cssProcessing ); + } else { + $table.add( $header ).removeClass( ts.css.processing + ' ' + c.cssProcessing ); + } + }, + + // detach tbody but save the position + // don't use tbody because there are portions that look for a tbody index (updateCell) + processTbody : function( table, $tb, getIt ) { + table = $( table )[ 0 ]; + if ( getIt ) { + table.isProcessing = true; + $tb.before( '' ); + return $.fn.detach ? $tb.detach() : $tb.remove(); + } + var holdr = $( table ).find( 'colgroup.tablesorter-savemyplace' ); + $tb.insertAfter( holdr ); + holdr.remove(); + table.isProcessing = false; + }, + + clearTableBody : function( table ) { + $( table )[ 0 ].config.$tbodies.children().detach(); + }, + + // used when replacing accented characters during sorting + characterEquivalents : { + 'a' : '\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5', // áàâãäąå + 'A' : '\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5', // ÁÀÂÃÄĄÅ + 'c' : '\u00e7\u0107\u010d', // çćč + 'C' : '\u00c7\u0106\u010c', // ÇĆČ + 'e' : '\u00e9\u00e8\u00ea\u00eb\u011b\u0119', // éèêëěę + 'E' : '\u00c9\u00c8\u00ca\u00cb\u011a\u0118', // ÉÈÊËĚĘ + 'i' : '\u00ed\u00ec\u0130\u00ee\u00ef\u0131', // íìİîïı + 'I' : '\u00cd\u00cc\u0130\u00ce\u00cf', // ÍÌİÎÏ + 'o' : '\u00f3\u00f2\u00f4\u00f5\u00f6\u014d', // óòôõöō + 'O' : '\u00d3\u00d2\u00d4\u00d5\u00d6\u014c', // ÓÒÔÕÖŌ + 'ss': '\u00df', // ß (s sharp) + 'SS': '\u1e9e', // ẞ (Capital sharp s) + 'u' : '\u00fa\u00f9\u00fb\u00fc\u016f', // úùûüů + 'U' : '\u00da\u00d9\u00db\u00dc\u016e' // ÚÙÛÜŮ + }, + + replaceAccents : function( str ) { + var chr, + acc = '[', + eq = ts.characterEquivalents; + if ( !ts.characterRegex ) { + ts.characterRegexArray = {}; + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + acc += eq[ chr ]; + ts.characterRegexArray[ chr ] = new RegExp( '[' + eq[ chr ] + ']', 'g' ); + } + } + ts.characterRegex = new RegExp( acc + ']' ); + } + if ( ts.characterRegex.test( str ) ) { + for ( chr in eq ) { + if ( typeof chr === 'string' ) { + str = str.replace( ts.characterRegexArray[ chr ], chr ); + } + } + } + return str; + }, + + validateOptions : function( c ) { + var setting, setting2, typ, timer, + // ignore options containing an array + ignore = 'headers sortForce sortList sortAppend widgets'.split( ' ' ), + orig = c.originalSettings; + if ( orig ) { + if ( ts.debug(c, 'core') ) { + timer = new Date(); + } + for ( setting in orig ) { + typ = typeof ts.defaults[setting]; + if ( typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '" option not recognized' ); + } else if ( typ === 'object' ) { + for ( setting2 in orig[setting] ) { + typ = ts.defaults[setting] && typeof ts.defaults[setting][setting2]; + if ( $.inArray( setting, ignore ) < 0 && typ === 'undefined' ) { + console.warn( 'Tablesorter Warning! "table.config.' + setting + '.' + setting2 + '" option not recognized' ); + } + } + } + } + if ( ts.debug(c, 'core') ) { + console.log( 'validate options time:' + ts.benchmark( timer ) ); + } + } + }, + + // restore headers + restoreHeaders : function( table ) { + var index, $cell, + c = $( table )[ 0 ].config, + $headers = c.$table.find( c.selectorHeaders ), + len = $headers.length; + // don't use c.$headers here in case header cells were swapped + for ( index = 0; index < len; index++ ) { + $cell = $headers.eq( index ); + // only restore header cells if it is wrapped + // because this is also used by the updateAll method + if ( $cell.find( '.' + ts.css.headerIn ).length ) { + $cell.html( c.headerContent[ index ] ); + } + } + }, + + destroy : function( table, removeClasses, callback ) { + table = $( table )[ 0 ]; + if ( !table.hasInitialized ) { return; } + // remove all widgets + ts.removeWidget( table, true, false ); + var events, + $t = $( table ), + c = table.config, + $h = $t.find( 'thead:first' ), + $r = $h.find( 'tr.' + ts.css.headerRow ).removeClass( ts.css.headerRow + ' ' + c.cssHeaderRow ), + $f = $t.find( 'tfoot:first > tr' ).children( 'th, td' ); + if ( removeClasses === false && $.inArray( 'uitheme', c.widgets ) >= 0 ) { + // reapply uitheme classes, in case we want to maintain appearance + $t.triggerHandler( 'applyWidgetId', [ 'uitheme' ] ); + $t.triggerHandler( 'applyWidgetId', [ 'zebra' ] ); + } + // remove widget added rows, just in case + $h.find( 'tr' ).not( $r ).remove(); + // disable tablesorter - not using .unbind( namespace ) because namespacing was + // added in jQuery v1.4.3 - see http://api.jquery.com/event.namespace/ + events = 'sortReset update updateRows updateAll updateHeaders updateCell addRows updateComplete sorton ' + + 'appendCache updateCache applyWidgetId applyWidgets refreshWidgets removeWidget destroy mouseup mouseleave ' + + 'keypress sortBegin sortEnd resetToLoadState '.split( ' ' ) + .join( c.namespace + ' ' ); + $t + .removeData( 'tablesorter' ) + .unbind( events.replace( ts.regex.spaces, ' ' ) ); + c.$headers + .add( $f ) + .removeClass( [ ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone ].join( ' ' ) ) + .removeAttr( 'data-column' ) + .removeAttr( 'aria-label' ) + .attr( 'aria-disabled', 'true' ); + $r + .find( c.selectorSort ) + .unbind( ( 'mousedown mouseup keypress '.split( ' ' ).join( c.namespace + ' ' ) ).replace( ts.regex.spaces, ' ' ) ); + ts.restoreHeaders( table ); + $t.toggleClass( ts.css.table + ' ' + c.tableClass + ' tablesorter-' + c.theme, removeClasses === false ); + $t.removeClass(c.namespace.slice(1)); + // clear flag in case the plugin is initialized again + table.hasInitialized = false; + delete table.config.cache; + if ( typeof callback === 'function' ) { + callback( table ); + } + if ( ts.debug(c, 'core') ) { + console.log( 'tablesorter has been removed' ); + } + } + + }; + + $.fn.tablesorter = function( settings ) { + return this.each( function() { + var table = this, + // merge & extend config options + c = $.extend( true, {}, ts.defaults, settings, ts.instanceMethods ); + // save initial settings + c.originalSettings = settings; + // create a table from data (build table widget) + if ( !table.hasInitialized && ts.buildTable && this.nodeName !== 'TABLE' ) { + // return the table (in case the original target is the table's container) + ts.buildTable( table, c ); + } else { + ts.setup( table, c ); + } + }); + }; + + // set up debug logs + if ( !( window.console && window.console.log ) ) { + // access $.tablesorter.logs for browsers that don't have a console... + ts.logs = []; + /*jshint -W020 */ + console = {}; + console.log = console.warn = console.error = console.table = function() { + var arg = arguments.length > 1 ? arguments : arguments[0]; + ts.logs[ ts.logs.length ] = { date: Date.now(), log: arg }; + }; + } + + // add default parsers + ts.addParser({ + id : 'no-parser', + is : function() { + return false; + }, + format : function() { + return ''; + }, + type : 'text' + }); + + ts.addParser({ + id : 'text', + is : function() { + return true; + }, + format : function( str, table ) { + var c = table.config; + if ( str ) { + str = $.trim( c.ignoreCase ? str.toLocaleLowerCase() : str ); + str = c.sortLocaleCompare ? ts.replaceAccents( str ) : str; + } + return str; + }, + type : 'text' + }); + + ts.regex.nondigit = /[^\w,. \-()]/g; + ts.addParser({ + id : 'digit', + is : function( str ) { + return ts.isDigit( str ); + }, + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; + }, + type : 'numeric' + }); + + ts.regex.currencyReplace = /[+\-,. ]/g; + ts.regex.currencyTest = /^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/; + ts.addParser({ + id : 'currency', + is : function( str ) { + str = ( str || '' ).replace( ts.regex.currencyReplace, '' ); + // test for £$€¤¥¢ + return ts.regex.currencyTest.test( str ); + }, + format : function( str, table ) { + var num = ts.formatFloat( ( str || '' ).replace( ts.regex.nondigit, '' ), table ); + return str && typeof num === 'number' ? num : + str ? $.trim( str && table.config.ignoreCase ? str.toLocaleLowerCase() : str ) : str; + }, + type : 'numeric' + }); + + // too many protocols to add them all https://en.wikipedia.org/wiki/URI_scheme + // now, this regex can be updated before initialization + ts.regex.urlProtocolTest = /^(https?|ftp|file):\/\//; + ts.regex.urlProtocolReplace = /(https?|ftp|file):\/\/(www\.)?/; + ts.addParser({ + id : 'url', + is : function( str ) { + return ts.regex.urlProtocolTest.test( str ); + }, + format : function( str ) { + return str ? $.trim( str.replace( ts.regex.urlProtocolReplace, '' ) ) : str; + }, + type : 'text' + }); + + ts.regex.dash = /-/g; + ts.regex.isoDate = /^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/; + ts.addParser({ + id : 'isoDate', + is : function( str ) { + return ts.regex.isoDate.test( str ); + }, + format : function( str ) { + var date = str ? new Date( str.replace( ts.regex.dash, '/' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; + }, + type : 'numeric' + }); + + ts.regex.percent = /%/g; + ts.regex.percentTest = /(\d\s*?%|%\s*?\d)/; + ts.addParser({ + id : 'percent', + is : function( str ) { + return ts.regex.percentTest.test( str ) && str.length < 15; + }, + format : function( str, table ) { + return str ? ts.formatFloat( str.replace( ts.regex.percent, '' ), table ) : str; + }, + type : 'numeric' + }); + + // added image parser to core v2.17.9 + ts.addParser({ + id : 'image', + is : function( str, table, node, $node ) { + return $node.find( 'img' ).length > 0; + }, + format : function( str, table, cell ) { + return $( cell ).find( 'img' ).attr( table.config.imgAttr || 'alt' ) || str; + }, + parsed : true, // filter widget flag + type : 'text' + }); + + ts.regex.dateReplace = /(\S)([AP]M)$/i; // used by usLongDate & time parser + ts.regex.usLongDateTest1 = /^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i; + ts.regex.usLongDateTest2 = /^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i; + ts.addParser({ + id : 'usLongDate', + is : function( str ) { + // two digit years are not allowed cross-browser + // Jan 01, 2013 12:34:56 PM or 01 Jan 2013 + return ts.regex.usLongDateTest1.test( str ) || ts.regex.usLongDateTest2.test( str ); + }, + format : function( str ) { + var date = str ? new Date( str.replace( ts.regex.dateReplace, '$1 $2' ) ) : str; + return date instanceof Date && isFinite( date ) ? date.getTime() : str; + }, + type : 'numeric' + }); + + // testing for ##-##-#### or ####-##-##, so it's not perfect; time can be included + ts.regex.shortDateTest = /(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})|(^\d{4}[\/\s]\d{1,2}[\/\s]\d{1,2})/; + // escaped "-" because JSHint in Firefox was showing it as an error + ts.regex.shortDateReplace = /[\-.,]/g; + // XXY covers MDY & DMY formats + ts.regex.shortDateXXY = /(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/; + ts.regex.shortDateYMD = /(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/; + ts.convertFormat = function( dateString, format ) { + dateString = ( dateString || '' ) + .replace( ts.regex.spaces, ' ' ) + .replace( ts.regex.shortDateReplace, '/' ); + if ( format === 'mmddyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$1/$2' ); + } else if ( format === 'ddmmyyyy' ) { + dateString = dateString.replace( ts.regex.shortDateXXY, '$3/$2/$1' ); + } else if ( format === 'yyyymmdd' ) { + dateString = dateString.replace( ts.regex.shortDateYMD, '$1/$2/$3' ); + } + var date = new Date( dateString ); + return date instanceof Date && isFinite( date ) ? date.getTime() : ''; + }; + + ts.addParser({ + id : 'shortDate', // 'mmddyyyy', 'ddmmyyyy' or 'yyyymmdd' + is : function( str ) { + str = ( str || '' ).replace( ts.regex.spaces, ' ' ).replace( ts.regex.shortDateReplace, '/' ); + return ts.regex.shortDateTest.test( str ); + }, + format : function( str, table, cell, cellIndex ) { + if ( str ) { + var c = table.config, + $header = c.$headerIndexed[ cellIndex ], + format = $header.length && $header.data( 'dateFormat' ) || + ts.getData( $header, ts.getColumnData( table, c.headers, cellIndex ), 'dateFormat' ) || + c.dateFormat; + // save format because getData can be slow... + if ( $header.length ) { + $header.data( 'dateFormat', format ); + } + return ts.convertFormat( str, format ) || str; + } + return str; + }, + type : 'numeric' + }); + + // match 24 hour time & 12 hours time + am/pm - see http://regexr.com/3c3tk + ts.regex.timeTest = /^(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)$|^((?:[01]\d|[2][0-4]):[0-5]\d)$/i; + ts.regex.timeMatch = /(0?[1-9]|1[0-2]):([0-5]\d)(\s[AP]M)|((?:[01]\d|[2][0-4]):[0-5]\d)/i; + ts.addParser({ + id : 'time', + is : function( str ) { + return ts.regex.timeTest.test( str ); + }, + format : function( str ) { + // isolate time... ignore month, day and year + var temp, + timePart = ( str || '' ).match( ts.regex.timeMatch ), + orig = new Date( str ), + // no time component? default to 00:00 by leaving it out, but only if str is defined + time = str && ( timePart !== null ? timePart[ 0 ] : '00:00 AM' ), + date = time ? new Date( '2000/01/01 ' + time.replace( ts.regex.dateReplace, '$1 $2' ) ) : time; + if ( date instanceof Date && isFinite( date ) ) { + temp = orig instanceof Date && isFinite( orig ) ? orig.getTime() : 0; + // if original string was a valid date, add it to the decimal so the column sorts in some kind of order + // luckily new Date() ignores the decimals + return temp ? parseFloat( date.getTime() + '.' + orig.getTime() ) : date.getTime(); + } + return str; + }, + type : 'numeric' + }); + + ts.addParser({ + id : 'metadata', + is : function() { + return false; + }, + format : function( str, table, cell ) { + var c = table.config, + p = ( !c.parserMetadataName ) ? 'sortValue' : c.parserMetadataName; + return $( cell ).metadata()[ p ]; + }, + type : 'numeric' + }); + + /* + ██████ ██████ █████▄ █████▄ ▄████▄ + ▄█▀ ██▄▄ ██▄▄██ ██▄▄██ ██▄▄██ + ▄█▀ ██▀▀ ██▀▀██ ██▀▀█ ██▀▀██ + ██████ ██████ █████▀ ██ ██ ██ ██ + */ + // add default widgets + ts.addWidget({ + id : 'zebra', + priority : 90, + format : function( table, c, wo ) { + var $visibleRows, $row, count, isEven, tbodyIndex, rowIndex, len, + child = new RegExp( c.cssChildRow, 'i' ), + $tbodies = c.$tbodies.add( $( c.namespace + '_extra_table' ).children( 'tbody:not(.' + c.cssInfoBlock + ')' ) ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + // loop through the visible rows + count = 0; + $visibleRows = $tbodies.eq( tbodyIndex ).children( 'tr:visible' ).not( c.selectorRemove ); + len = $visibleRows.length; + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + $row = $visibleRows.eq( rowIndex ); + // style child rows the same way the parent row was styled + if ( !child.test( $row[ 0 ].className ) ) { count++; } + isEven = ( count % 2 === 0 ); + $row + .removeClass( wo.zebra[ isEven ? 1 : 0 ] ) + .addClass( wo.zebra[ isEven ? 0 : 1 ] ); + } + } + }, + remove : function( table, c, wo, refreshing ) { + if ( refreshing ) { return; } + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + toRemove = ( wo.zebra || [ 'even', 'odd' ] ).join( ' ' ); + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( toRemove ); + ts.processTbody( table, $tbody, false ); // restore tbody + } + } + }); + +})( jQuery ); diff --git a/compare/thirdparty/tablesorter/js/jquery.tablesorter.widgets.js b/compare/thirdparty/tablesorter/js/jquery.tablesorter.widgets.js new file mode 100644 index 00000000..bcbf5e4e --- /dev/null +++ b/compare/thirdparty/tablesorter/js/jquery.tablesorter.widgets.js @@ -0,0 +1,3184 @@ +/*** This file is dynamically generated *** +█████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ██▄▄██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ +█████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ +*/ +/*! tablesorter (FORK) - updated 2024-08-13 (v2.32.0)*/ +/* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ +(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { +/*! Widget: storage - updated 2018-03-18 (v2.30.0) */ +/*global JSON:false */ +;(function ($, window, document) { + 'use strict'; + + var ts = $.tablesorter || {}; + + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_storageType: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); + + // *** Store data in local storage, with a cookie fallback *** + /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json) + if you need it, then include https://github.com/douglascrockford/JSON-js + + $.parseJSON is not available is jQuery versions older than 1.4.1, using older + versions will only allow storing information for one page at a time + + // *** Save data (JSON format only) *** + // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid + var val = { "mywidget" : "data1" }; // valid JSON uses double quotes + // $.tablesorter.storage(table, key, val); + $.tablesorter.storage(table, 'tablesorter-mywidget', val); + + // *** Get data: $.tablesorter.storage(table, key); *** + v = $.tablesorter.storage(table, 'tablesorter-mywidget'); + // val may be empty, so also check for your data + val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : ''; + alert(val); // 'data1' if saved, or '' if not + */ + ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + debug = ts.debug(c, 'storage'), + storageType = ( + ( options && options.storageType ) || ( wo && wo.storage_storageType ) + ).toString().charAt(0).toLowerCase(), + // deprecating "useSessionStorage"; any storageType setting overrides it + session = storageType ? '' : + ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ), + $table = $(table), + // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table 'data-table-page' attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + + // skip if using cookies + if (storageType !== 'c') { + storageType = (storageType === 's' || session) ? 'sessionStorage' : 'localStorage'; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + console.warn( storageType + ' is not supported in this browser' ); + } + } + } + if (debug) { + console.log('Storage >> Using', hasStorage ? storageType : 'cookies'); + } + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } + } + // allow value to be an empty string too + if (typeof value !== 'undefined' && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g, '\"') + '; expires=' + date.toGMTString() + '; path=/'; + } + } else { + return values && values[url] ? values[url][id] : ''; + } + }; + +})(jQuery, window, document); + +/*! Widget: uitheme - updated 2018-03-18 (v2.30.0) */ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add 'bootstrap-icon-white' to make them white; this icon class is added to the in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the in the header + iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n ui-icon-caret-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } + }; + + $.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable + }); + + ts.addWidget({ + id: 'uitheme', + priority: 10, + format: function(table, c, wo) { + var i, tmp, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ), + debug = ts.debug(c, 'uitheme'); + if (debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [ oldtheme.even, oldtheme.odd ].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes + $headers + .removeClass( (hasOldTheme ? [ oldtheme.header, oldtheme.hover, oldremove ].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function() { + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('
    '); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') + .addClass(themes.icons || ''); + } + // filter widget initializes after uitheme + if (ts.hasWidget( c.table, 'filter' )) { + tmp = function() { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + }; + if (wo.filter_initialized) { + tmp(); + } else { + $table.one('filterInit', function() { + tmp(); + }); + } + } + } + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [ themes.sortAsc, themes.active ].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [ themes.sortDesc, themes.active ].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); + } + } + } + if (debug) { + console.log('uitheme >> Applied ' + theme + ' theme' + ts.benchmark(time)); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); + } + }); + +})(jQuery); + +/*! Widget: columns - updated 5/24/2017 (v2.28.11) */ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + ts.addWidget({ + id: 'columns', + priority: 65, + options : { + columns : [ 'primary', 'secondary', 'tertiary' ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + $table = c.$table, + $tbodies = c.$tbodies, + sortList = c.sortList, + len = sortList.length, + // removed c.widgetColumns support + css = wo && wo.columns || [ 'primary', 'secondary', 'tertiary' ], + last = css.length - 1; + remove = css.join(' '); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } + } + } + } + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? [ 'thead tr' ] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } + } + } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ 'primary', 'secondary', 'tertiary' ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } + } + }); + +})(jQuery); + +/*! Widget: filter - updated 2018-03-18 (v2.30.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;( function ( $ ) { + 'use strict'; + var tsf, tsfRegex, + ts = $.tablesorter || {}, + tscss = ts.css, + tskeyCodes = ts.keyCodes; + + $.extend( tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' + }); + + $.extend( tskeyCodes, { + backSpace : 8, + escape : 27, + space : 32, + left : 37, + down : 40 + }); + + ts.addWidget({ + id: 'filter', + priority: 50, + options : { + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_childWithSibs : true, // if true, include matching child row siblings + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows + filter_filterLabel : 'Filter "{{label}}" column by...', // Aria-label added to filter input/select; see #1495 + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_matchType : { 'input': 'exact', 'select': 'exact' }, // global query settings ('exact' or 'match'); overridden by "filter-match" or "filter-exact" class + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_resetOnEsc : true, // Reset filter input when the user presses escape - normalized across browsers + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_selectSourceSeparator : '|', // filter_selectSource array text left of the separator is added to the option value, right into the option text + filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false // filter all data using parsed content + }, + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + tsf.init( table, c, wo ); + } + }, + remove: function( table, c, wo, refreshing ) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = ( + 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterFomatterUpdate filterEnd search stickyHeadersInit ' + ).split( ' ' ).join( c.namespace + 'filter ' ); + $table + .removeClass( 'hasFilters' ) + // add filter namespace to all BUT search + .unbind( events.replace( ts.regex.spaces, ' ' ) ) + // remove the filter row even if refreshing, because the column might have been moved + .find( '.' + tscss.filterRow ).remove(); + wo.filter_initialized = false; + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody + } + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ); + } + } + }); + + tsf = ts.filter = { + + // regex used in filter 'check' functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([migyu]{0,5})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + operators : /[<>=]/g, // replace operators + query : '(q|query)', // replace filter queries + wild01 : /\?/g, // wild card match 0 or 1 + wild0More : /\*/g, // wild care match 0 or more + quote : /\"/g, + isNeg1 : /(>=?\s*-\d)/, + isNeg2 : /(<=?\s*\d)/ + }, + // function( c, data ) { } + // c = table.config + // data.$row = jQuery object of the row currently being processed + // data.$cells = jQuery object of all cells within the current row + // data.filters = array of filters for all columns ( some may be undefined ) + // data.filter = filter for the current column + // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled; may be a number & not a string ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true; may be a number & not a string ) + // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) + // data.cacheArray = An array of parsed content from each table cell in the row being processed + // data.index = column index; table = table element ( DOM ) + // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) + types: { + or : function( c, data, vars ) { + // look for "|", but not if it is inside of a regular expression + if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) && + // this test for regex has potential to slow down the overall search + !tsfRegex.regex.test( data.filter ) ) { + var indx, filterMatched, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + filter = data.filter.split( tsfRegex.orSplit ), + iFilter = data.iFilter.split( tsfRegex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')'; + try { + // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, + // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } catch ( error ) { + return null; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( tsfRegex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + filter = data.filter.split( tsfRegex.andSplit ), + iFilter = data.iFilter.split( tsfRegex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ); + try { + // use try/catch just in case RegExp is invalid + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } catch ( error ) { + return null; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for regex + regex: function( c, data ) { + if ( tsfRegex.regex.test( data.filter ) ) { + var matches, + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; + try { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { + matches = false; + } + return matches; + } + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + // ignore empty strings... because '' < 10 is true + if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, + table = c.table, + parsed = data.parsed[ data.index ], + query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), + parser = c.parsers[ data.index ] || {}, + savedSearch = query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) ); + result = tsf.parseFilter( c, txt, data, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + } + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( tsfRegex.gtTest.test( data.iFilter ) ) { + result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsfRegex.ltTest.test( data.iFilter ) ) { + result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { + result = true; + } + return result; + } + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( tsfRegex.notTest.test( data.iFilter ) ) { + var indx, + txt = data.iFilter.replace( '!', '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; + if ( tsfRegex.exact.test( filter ) ) { + // look for exact not matches - see #628 + filter = filter.replace( tsfRegex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; + } else { + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : + // return true if not found + data.anyMatch ? indx < 0 : + // return false if found + !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + } + } + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if ( tsfRegex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsfRegex.exact, '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; + // eslint-disable-next-line eqeqeq + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; + } + return null; + }, + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( tsfRegex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( tsfRegex.toSplit ); + + tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); + tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[ index ].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); + } + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( tsfRegex.wildOrTest.test( data.iFilter ) ) { + var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' ); + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + try { + return new RegExp( + query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); + } catch ( error ) { + return null; + } + } + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) + fuzzy: function( c, data ) { + if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + txt = data.iFilter.slice( 1 ), + pattern = tsf.parseFilter( c, txt, data ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { + patternIndx += 1; + } + } + return patternIndx === pattern.length; + } + return null; + } + }, + init: function( table ) { + // filter language options + ts.language = $.extend( true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language ); + + var options, string, txt, $header, column, val, fxn, noSelect, + c = table.config, + wo = c.widgetOptions, + processStr = function(prefix, str, suffix) { + str = str.trim(); + // don't include prefix/suffix if str is empty + return str === '' ? '' : (prefix || '') + str + (suffix || ''); + }; + c.$table.addClass( 'hasFilters' ); + c.lastSearch = []; + + // define timers so using clearTimeout won't cause an undefined error + wo.filter_searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + val = '\\{' + tsfRegex.query + '\\}'; + $.extend( tsfRegex, { + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(-' + processStr('|', ts.language.or) + processStr('|', ts.language.to) + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-' + processStr('|', ts.language.to) + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-' + processStr('|', ts.language.to) + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + processStr('', ts.language.and, '|') + '&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + processStr('', ts.language.and, '|') + '&&)\\s+)', 'gi' ), + orTest : new RegExp( '(\\|' + processStr('|\\s+', ts.language.or, '\\s+') + ')', 'i' ), + orSplit : new RegExp( '(?:\\|' + processStr('|\\s+(?:', ts.language.or, ')\\s+') + ')', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ), + operTest : /^[<>]=?/, + gtTest : />/, + gteTest : />=/, + ltTest : /' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '' : ''; + val = string; + txt = string; + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); + txt = val[1]; + val = val[0]; + } + options += ''; + } + } + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); + txt = wo.filter_selectSource; + fxn = typeof txt === 'function' ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { + // updating so the extra options are appended + tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + } + } + } + } + } + // not really updating, but if the column has both the 'filter-select' class & + // filter_functions set to true, it would append the same options twice. + tsf.buildDefault( table, true ); + + tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { + tsf.bindSearch( table, wo.filter_external ); + } + + if ( wo.filter_hideFilters ) { + tsf.hideFilters( c ); + } + + // show processing icon + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter-sp ' ); + c.$table + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) + .bind( txt, function( event, columns ) { + // only add processing to certain columns to all columns + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); + }); + } + + // set filtered rows count ( intially unfiltered ) + c.filteredRows = c.totalRows; + + // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) + .bind( txt, function() { + tsf.completeInit( this ); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.triggerHandler( 'filterFomatterUpdate' ); + setTimeout( function() { + tsf.filterInitComplete( c ); + }, 100 ); + } else if ( !wo.filter_initialized ) { + tsf.completeInit( table ); + } + }, + completeInit: function( table ) { + // redefine 'c' & 'wo' so they update properly inside this callback + var c = table.config, + wo = c.widgetOptions, + filters = tsf.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } + } + c.$table.triggerHandler( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + tsf.filterInitComplete( c ); + } + }, 100 ); + }, + + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + // prevent error if $cell is undefined - see #1056 + var $table = $cell && $cell.closest( 'table' ); + var config = $table.length && $table[0].config, + wo = config && config.widgetOptions; + if ( wo && !wo.filter_initialized ) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[ column ] = 1; + } + }, + filterInitComplete: function( c ) { + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function() { + wo.filter_initialized = true; + // update lastSearch - it gets cleared often + c.lastSearch = c.$table.data( 'lastSearch' ); + c.$table.triggerHandler( 'filterInit', c ); + tsf.findRows( c.table, c.lastSearch || [] ); + if (ts.debug(c, 'filter')) { + console.log('Filter >> Widget initialized'); + } + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { + completed(); + } else { + len = wo.filter_formatterInit.length; + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { + count++; + } + } + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { + // filter widget initialized + completed(); + } else if ( !wo.filter_initialized ) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { + completed(); + }, 500 ); + } + } + }, + // encode or decode filters for storage; see #1026 + processFilters: function( filters, encode ) { + var indx, + // fixes #1237; previously returning an encoded "filters" value + result = [], + mode = encode ? encodeURIComponent : decodeURIComponent, + len = filters.length; + for ( indx = 0; indx < len; indx++ ) { + if ( filters[ indx ] ) { + result[ indx ] = mode( filters[ indx ] ); + } + } + return result; + }, + setDefaults: function( table, c, wo ) { + var isArray, saved, indx, col, $filters, + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray( saved ); + // make sure we're not just getting an empty array + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = tsf.processFilters( saved ); + } + } + // if no filters saved, then check default settings + if ( filters.join( '' ) === '' ) { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters + col = indx === c.columns ? 'all' : indx; + filters[ indx ] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + } + } + c.$table.data( 'lastSearch', filters ); + return filters; + }, + parseFilter: function( c, filter, data, parsed ) { + return parsed || data.parsed[ data.index ] ? + c.parsers[ data.index ].format( filter, c.table, [], data.index ) : + filter; + }, + buildRow: function( table, c, wo ) { + var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, + // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, + columns = c.columns, + arry = $.isArray( cellFilter ), + buildFilter = ''; + for ( column = 0; column < columns; column++ ) { + if ( c.$headerIndexed[ column ].length ) { + // account for entire column set with colspan. See #1047 + tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; + if ( tmp > 1 ) { + buildFilter += '' ).appendTo( $filter ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( $filter, column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = $filter.children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) { + $filter.append( buildFilter ); + } + } else { + buildFilter = $( '' ).appendTo( $filter ); + } + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); + } + } + if ( buildFilter ) { + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + // copy data-column from table cell (it will include colspan) + buildFilter.addClass( tscss.filter + ' ' + name ); + name = wo.filter_filterLabel; + tmp = name.match(/{{([^}]+?)}}/g); + if (!tmp) { + tmp = [ '{{label}}' ]; + } + $.each(tmp, function(indx, attr) { + var regex = new RegExp(attr, 'g'), + data = $header.attr('data-' + attr.replace(/{{|}}/g, '')), + text = typeof data === 'undefined' ? $header.text() : data; + name = name.replace( regex, $.trim( text ) ); + }); + buildFilter.attr({ + 'data-column': $filter.attr( 'data-column' ), + 'aria-label': name + }); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } + } + } + } + }, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, + wo = c.widgetOptions, + namespace = c.namespace + 'filter', + $ext = wo.filter_$externalFilters; + if ( internal !== true ) { + // save anyMatch element + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; + } + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); + } + // unbind events + tmp = ( 'keypress keyup keydown search change input '.split( ' ' ).join( namespace + ' ' ) ); + $el + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) + .bind( 'keydown' + namespace, function( event ) { + if ( event.which === tskeyCodes.escape && !table.config.widgetOptions.filter_resetOnEsc ) { + // prevent keypress event + return false; + } + }) + .bind( 'keyup' + namespace, function( event ) { + wo = table.config.widgetOptions; // make sure "wo" isn't cached + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + // emulate what webkit does.... escape clears the filter + if ( event.which === tskeyCodes.escape ) { + // make sure to restore the last value on escape + this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; + // don't return if the search value is empty ( all rows need to be revealed ) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof liveSearch === 'number' && this.value.length < liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && + ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { + return; + // live search + } else if ( liveSearch === false ) { + if ( this.value !== '' && event.which !== tskeyCodes.enter ) { + return; + } + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + tsf.searching( table, true, true, column ); + }) + // include change for select - fixes #473 + .bind( 'search change keypress input blur '.split( ' ' ).join( namespace + ' ' ), function( event ) { + // don't get cached data, in case data-column changes dynamically + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + eventType = event.type, + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( table.config.widgetOptions.filter_initialized && + // immediate search if user presses enter + ( event.which === tskeyCodes.enter || + // immediate search if a "search" or "blur" is triggered on the input + ( eventType === 'search' || eventType === 'blur' ) || + // change & input events must be ignored if liveSearch !== true + ( eventType === 'change' || eventType === 'input' ) && + // prevent search if liveSearch is a number + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== 'INPUT' ) && + // don't allow 'change' or 'input' event to process if the input value + // is the same - fixes #685 + this.value !== c.lastSearch[column] + ) + ) { + event.preventDefault(); + // init search with no delay + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + tsf.searching( table, eventType !== 'keypress' || event.which === tskeyCodes.enter, true, column ); + } + }); + }, + searching: function( table, filter, skipFirst, column ) { + var liveSearch, + wo = table.config.widgetOptions; + if (typeof column === 'undefined') { + // no delay + liveSearch = false; + } else { + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + // get column setting, or set to fallback value, or default to false + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + } + clearTimeout( wo.filter_searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { + // delay filtering + wo.filter_searchTimer = setTimeout( function() { + tsf.checkFilters( table, filter, skipFirst ); + }, liveSearch ? wo.filter_searchDelay : 10 ); + } else { + // skip delay + tsf.checkFilters( table, filter, skipFirst ); + } + }, + equalFilters: function (c, filter1, filter2) { + var indx, + f1 = [], + f2 = [], + len = c.columns + 1; // add one to include anyMatch filter + filter1 = $.isArray(filter1) ? filter1 : []; + filter2 = $.isArray(filter2) ? filter2 : []; + for (indx = 0; indx < len; indx++) { + f1[indx] = filter1[indx] || ''; + f2[indx] = filter2[indx] || ''; + } + return f1.join(',') === f2.join(','); + }, + checkFilters: function( table, filter, skipFirst ) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + currentFilters = filters || []; // current filter values + // prevent errors if delay init is set + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && ( !c.pager || c.pager && c.pager.initialized ) ) { + ts.updateCache( c, function() { + tsf.checkFilters( table, false, skipFirst ); + }); + } + return; + } + // add filter array back into inputs + if ( filterArray ) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if ( !wo.filter_initialized ) { + c.lastSearch = []; + c.lastCombinedFilter = ''; + } + } + if ( wo.filter_hideFilters ) { + // show/hide filter row as needed + c.$table + .find( '.' + tscss.filterRow ) + .triggerHandler( tsf.hideFiltersCheck( c ) ? 'mouseleave' : 'mouseenter' ); + } + // return if the last search is the same; but filter === false when updating the search + // see example-widget-filter.html filter toggle buttons + if ( tsf.equalFilters(c, c.lastSearch, currentFilters) ) { + if ( filter !== false ) { + return; + } else { + // force filter refresh + c.lastCombinedFilter = ''; + c.lastSearch = []; + } + } + // define filter inside it is false + filters = filters || []; + // convert filters to strings - see #1070 + filters = Array.prototype.map ? + filters.map( String ) : + // for IE8 & older browsers - maybe not the best method + filters.join( '\ufffd' ).split( '\ufffd' ); + + if ( wo.filter_initialized ) { + c.$table.triggerHandler( 'filterStart', [ filters ] ); + } + if ( c.showProcessing ) { + // give it time for the processing icon to kick in + setTimeout( function() { + tsf.findRows( table, filters, currentFilters ); + return false; + }, 30 ); + } else { + tsf.findRows( table, filters, currentFilters ); + return false; + } + }, + hideFiltersCheck: function( c ) { + if (typeof c.widgetOptions.filter_hideFilters === 'function') { + var val = c.widgetOptions.filter_hideFilters( c ); + if (typeof val === 'boolean') { + return val; + } + } + return ts.getFilters( c.$table ).join( '' ) === ''; + }, + hideFilters: function( c, $table ) { + var timer; + ( $table || c.$table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) + .bind( 'mouseenter mouseleave', function( e ) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e, + $row = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $row.removeClass( tscss.filterRowHide ); + } else { + // don't hide if input has focus + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $row[0] ) { + // don't hide row if any filter has a value + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) ); + } + } + }, 200 ); + }) + .find( 'input, select' ).bind( 'focus blur', function( e ) { + var event = e, + $row = $( this ).closest( 'tr' ); + clearTimeout( timer ); + timer = setTimeout( function() { + clearTimeout( timer ); + // don't hide row if any filter has a value + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) && event.type !== 'focus' ); + }, 200 ); + }); + }, + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } + var regex = tsfRegex.iQuery, + maskLen = mask.match( tsfRegex.igQuery ).length, + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one 'word' in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); + } + } + return val; + }, + getLatestSearch: function( $input ) { + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); + }); + } + return $input || $(); + }, + findRange: function( c, val, ignoreRanges ) { + // look for multiple columns '1-3,4-6,8' in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + columns = []; + if ( /^[0-9]+$/.test( val ) ) { + // always return an array + return [ parseInt( val, 10 ) ]; + } + // process column range + if ( !ignoreRanges && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges ? ranges.length : 0; + for ( indx = 0; indx < len; indx++ ) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } + for ( ; start <= end; start++ ) { + columns[ columns.length ] = start; + } + // remove processed range from val + val = val.replace( ranges[ indx ], '' ); + } + } + // process single columns + if ( !ignoreRanges && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); + if ( indx < c.columns ) { + columns[ columns.length ] = indx; + } + } + } + } + // return all columns + if ( !columns.length ) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns[ columns.length ] = indx; + } + } + return columns; + }, + getColumnElm: function( c, $elements, column ) { + // data-column may contain multiple columns '1-3,5-6,8' + // replaces: c.$filters.filter( '[data-column="' + column + '"]' ); + return $elements.filter( function() { + var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) ); + return $.inArray( column, cols ) > -1; + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + return tsf.findRange( c, val, !targets ); + }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in tsf.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = tsf.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + data.matchedOn = ffxn; + filterMatched = matches; + } + } + } + return filterMatched; + }, + matchType: function( c, columnIndex ) { + var isMatch, + wo = c.widgetOptions, + $el = c.$headerIndexed[ columnIndex ]; + // filter-exact > filter-match > filter_matchType for type + if ( $el.hasClass( 'filter-exact' ) ) { + isMatch = false; + } else if ( $el.hasClass( 'filter-match' ) ) { + isMatch = true; + } else { + // filter-select is not applied when filter_functions are used, so look for a select + if ( wo.filter_columnFilters ) { + $el = c.$filters + .find( '.' + tscss.filter ) + .add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ); + } else if ( wo.filter_$externalFilters ) { + $el = wo.filter_$externalFilters.filter( '[data-column="' + columnIndex + '"]' ); + } + isMatch = $el.length ? + c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : + // default to exact, if no inputs found + false; + } + return isMatch; + }, + processRow: function( c, data, vars ) { + var result, filterMatched, + fxn, ffxn, txt, + wo = c.widgetOptions, + showRow = true, + hasAnyMatchInput = wo.filter_$anyMatch && wo.filter_$anyMatch.length, + + // if wo.filter_$anyMatch data-column attribute is changed dynamically + // we don't want to do an "anyMatch" search on one column using data + // for the entire row - see #998 + columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ? + // look for multiple columns '1-3,4-6,8' + tsf.multipleColumns( c, wo.filter_$anyMatch ) : + []; + data.$cells = data.$row.children(); + data.matchedOn = null; + if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { + data.anyMatch = true; + data.isMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + vars.excludeMatch = vars.noAnyMatch; + filterMatched = tsf.processTypes( c, data, vars ); + if ( filterMatched !== null ) { + showRow = filterMatched; + } else { + if ( wo.filter_startsWith ) { + showRow = false; + // data.rowArray may not contain all columns + columnIndex = Math.min( c.columns, data.rowArray.length ); + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; + } + } + + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; + + // filter types to exclude, per column + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; + + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + result = data.parsed[ columnIndex ] ? data.cache : data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + data.isMatch = tsf.matchType( c, columnIndex ); + + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + // replace column specific default filters - see #1088 + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + } + + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + filterMatched = null; + if ( fxn ) { + if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + filterMatched = tsf.processTypes( c, data, vars ); + // select with exact match; ignore "and" or "or" within the text; fixes #1486 + txt = fxn === true && (data.matchedOn === 'and' || data.matchedOn === 'or'); + if ( filterMatched !== null && !txt) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + // check fxn (filter-select in header) after filter types are checked + // without this, the filter + jQuery UI selectmenu demo was breaking + if ( fxn === true ) { + // default selector uses exact match unless 'filter-match' class is found + result = data.isMatch ? + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else { + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } + } + } else { + result = filterMatched; + } + showRow = ( result ) ? showRow : false; + } + } + return showRow; + }, + findRows: function( table, filters, currentFilters ) { + if ( + tsf.equalFilters(table.config, table.config.lastSearch, currentFilters) || + !table.config.widgetOptions.filter_initialized + ) { + return; + } + var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, showParent, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), + c = table.config, + wo = c.widgetOptions, + debug = ts.debug(c, 'filter'), + // data object passed to filters; anyMatch is a flag for the filters + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [] + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + // parse columns after formatter, in case the class is added at that point + data.parsed = []; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.parsed[ columnIndex ] = wo.filter_useParsedData || + // parser has a "parsed" parameter + ( c.parsers && c.parsers[ columnIndex ] && c.parsers[ columnIndex ].parsed || + // getData may not return 'parsed' if other 'filter-' class names exist + // ( e.g. ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); + + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ) || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); + } + + if ( debug ) { + console.log( 'Filter >> Starting filter widget search', filters ); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + currentFilters = ( storedFilters || [] ); + + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( currentFilters.join('') === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); + } else { + // filter out child rows + $rows = $rows.not( '.' + c.cssChildRow ); + len = $rows.length; + + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { + // specific columns search + query = data.anyMatchFilter.split( tsfRegex.andSplit ); + injected = false; + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + if ( isNaN( res[0] ) ) { + $.each( c.headerContent, function( i, txt ) { + // multiple matches are possible + if ( txt.toLowerCase().indexOf( res[0] ) > -1 ) { + id = i; + filters[ id ] = res[1]; + } + }); + } else { + id = parseInt( res[0], 10 ) - 1; + } + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[ id ] = res[1]; + query.splice( indx, 1 ); + indx--; + injected = true; + } + } + } + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); + } + } + } + + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if ( !searchFiltered ) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !tsfRegex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !tsfRegex.exactTest.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && + !tsf.matchType( c, indx ) ); + } + } + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( debug ) { + console.log( 'Filter >> Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + } + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { + // replace accents + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + } + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); + } + + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && tsfRegex.child.test( txt ); + // skip child rows & already filtered rows + if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) { + continue; + } + + data.$row = $rows.eq( rowIndex ); + data.rowIndex = rowIndex; + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // so, if 'table.config.widgetOptions.filter_childRows' is true and there is + // a match anywhere in the child row, then it will make the row visible + // checked here so the option can be changed dynamically + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( ' ' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } + + showRow = false; + showParent = tsf.processRow( c, data, vars ); + $row = rowData.$row; + + // don't pass reference to val + val = showParent ? true : false; + childRow = rowData.$row.filter( ':gt(0)' ); + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + // if only showing resulting child row, only include parent + $row = $row.eq( 0 ); + } + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + val = tsf.processRow( c, data, vars ); + // use OR comparison on child rows + showRow = showRow || val; + if ( !wo.filter_childWithSibs && val ) { + childRow.eq( indx ).removeClass( wo.filter_filteredRow ); + } + } + } + // keep parent row match even if no child matches... see #1020 + showRow = showRow || showParent; + } else { + showRow = val; + } + $row + .toggleClass( wo.filter_filteredRow, !showRow )[0] + .display = showRow ? '' : 'none'; + } + } + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; + c.totalRows += $rows.length; + ts.processTbody( table, $tbody, false ); + } + // lastCombinedFilter is no longer used internally + c.lastCombinedFilter = storedFilters.join(''); // save last search + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); + } + if ( debug ) { + console.log( 'Filter >> Completed search' + ts.benchmark(time) ); + } + if ( wo.filter_initialized ) { + c.$table.triggerHandler( 'filterBeforeEnd', c ); + c.$table.triggerHandler( 'filterEnd', c ); + } + setTimeout( function() { + ts.applyWidget( c.table ); // make sure zebra widget is applied + }, 0 ); + }, + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; + var c = table.config, + wo = c.widgetOptions, + arry = false, + source = wo.filter_selectSource, + last = c.$table.data( 'lastSearch' ) || [], + fxn = typeof source === 'function' ? true : ts.getColumnData( table, source, column ); + + if ( onlyAvail && last[column] !== '' ) { + onlyAvail = false; + } + + // filter select source option + if ( fxn === true ) { + // OVERALL source + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '' ) >= 0 ) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ( $.isArray( fxn ) ) { + arry = fxn; + } else if ( $.type( source ) === 'object' && fxn ) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn( table, column, onlyAvail ); + // abort - updating the selects from an external method + if (arry === null) { + return null; + } + } + if ( arry === false ) { + // fall back to original method + arry = tsf.getOptions( table, column, onlyAvail ); + } + + return tsf.processOptions( table, column, arry ); + + }, + processOptions: function( table, column, arry ) { + if ( !$.isArray( arry ) ) { + return false; + } + table = $( table )[0]; + var cts, txt, indx, len, parsedTxt, str, + c = table.config, + validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + direction = validColumn ? c.$headerIndexed[ column ].hasClass( 'filter-select-sort-desc' ) : false, + parsed = []; + // get unique elements and sort the list + // if $.tablesorter.sortText exists ( not in the original tablesorter ), + // then natural sort the list otherwise use a basic sort + arry = $.grep( arry, function( value, indx ) { + if ( value.text ) { + return true; + } + return $.inArray( value, arry ) === indx; + }); + if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; + // check for object + str = txt.text ? txt.text : txt; + // sortNatural breaks if you don't pass it strings + parsedTxt = ( validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( str, table, [], column ) || str ).toString(); + parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt; + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + if ( txt.text ) { + txt.parsed = parsedTxt; + parsed[ parsed.length ] = txt; + } else { + parsed[ parsed.length ] = { + text : txt, + // check parser length - fixes #934 + parsed : parsedTxt + }; + } + } + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort( function( a, b ) { + var x = direction ? b.parsed : a.parsed, + y = direction ? a.parsed : b.parsed; + if ( validColumn && typeof cts === 'function' ) { + // custom OVERALL text sorter + return cts( x, y, true, column, table ); + } else if ( validColumn && typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { + // fall back to natural sort + return ts.sortNatural( x, y ); + } + // using an older version! do a basic sort + return true; + }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for ( indx = 0; indx < len; indx++ ) { + arry[ arry.length ] = parsed[indx]; + } + return arry; + } + }, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, indx, child, childLen, + c = table.config, + wo = c.widgetOptions, + arry = []; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; + // check if has class filtered + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } + // get non-normalized cell content + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ column ]; + // child row parsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; + for ( indx = 0; indx < childLen; indx++ ) { + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ]; + } + } + } else { + // get raw cached data instead of content directly from the cells + arry[ arry.length ] = cache.normalized[ rowIndex ][ c.columns ].raw[ column ]; + // child row unparsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; + for ( indx = 1; indx < childLen; indx++ ) { + child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); + arry[ arry.length ] = '' + ts.getElementText( c, child, column ); + } + } + } + } + } + return arry; + }, + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } + + var indx, val, txt, t, $filters, $filter, option, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '', + // Get curent filter value + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = tsf.getOptionSource( table, column, onlyAvail ); + // abort, selects are updated by an external method + if (arry === null) { + return; + } + } + + if ( $.isArray( arry ) ) { + // build option list + for ( indx = 0; indx < arry.length; indx++ ) { + option = arry[ indx ]; + if ( option.text ) { + // OBJECT!! add data-function-name in case the value is set in filter_functions + option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value; + + // support jQuery < v1.8, otherwise the below code could be shortened to + // options += $( '