diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..bc93d74 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +# Description + +- Related issues: + - # + +# Changes in this PR + +# How has this been tested? + +# Checklist +- [ ] This PR follows the coding-style of this project +- [ ] I have tested these changes +- [ ] I have commented hard-to-understand codes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..648deb9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +name: CI + +on: pull_request + +jobs: + black: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Upgrade pip + run: pip install --upgrade pip + - name: Install black + run: pip install --upgrade black==23.1.0 + - name: Run black + run: black --check . + + isort: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Upgrade pip + run: pip install --upgrade pip + - name: Install isort + run: pip install --upgrade isort==5.12.0 + - name: Run isort + working-directory: ./cleval + run: isort --profile black --check . + + pytest: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Upgrade pip + run: pip install --upgrade pip && pip install -U setuptools wheel + - name: Update apt + run: sudo apt update + - name: Install pre-requirements + run: sudo apt install -y libyajl2 libyajl-dev libleveldb-dev libgl1-mesa-glx libglib2.0-0 + - name: Install cleval + run: pip install six && pip install --force-reinstall --no-cache-dir cleval opencv-python-headless + - name: Install pytest + run: pip install --upgrade pytest + - name: Run pytest + run: pytest diff --git a/.gitignore b/.gitignore index 533eeb6..0a3b9f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,15 @@ __pycache__/ -venv -debug* -tmp* .vscode .DS_Store .idea -gt/image* +output/ +.pytest_cache +.mypy_cache +build/ +dist/ +*.egg-info/ + +venv +debug* +tmp* +profile.txt diff --git a/README.md b/README.md index 0afa3a0..994ea1d 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,15 @@ Official implementation of CLEval | [paper](https://arxiv.org/abs/2006.06244) ## Overview We propose a Character-Level Evaluation metric (CLEval). To perform fine-grained assessment of the results, *instance matching* process handles granularity difference and *scoring process* conducts character-level evaluation. Please refer to the paper for more details. This code is based on [ICDAR15 official evaluation code](http://rrc.cvc.uab.es/). +### 2023.10.16 Huge Update +- **Much More Faster Version** of CLEval has been Uploaded!! +- Support CLI +- Support torchmetric +- Support scale-wise evaluation -### Simplified Method Description -![Explanation](screenshots/explanation.gif) -## Notification -* 15 Jun, 2020 | initial release - - the reported evaluation results in our paper is measured by setting the ```CASE_SENSITIVE``` option as ```False```. +### Simplified Method Description +![Explanation](resources/screenshots/explanation.gif) ## Supported annotation types * **LTRB**(xmin, ymin, xmax, ymax) @@ -24,51 +26,83 @@ We propose a Character-Level Evaluation metric (CLEval). To perform fine-grained * TotalText [Link](https://github.com/cs-chan/Total-Text-Dataset) * Any other datasets that have a similar format with the datasets mentioned above -## Getting started -### Clone repository -``` -git clone https://github.com/clovaai/CLEval.git +## Installation + +### Build from pip +download from Clova OCR pypi +```bash +$ pip install cleval ``` -### Requirements -* python 3.x -* see requirements.txt file to check package dependency. To install, command +or build with url +```bash +$ pip install git+https://github.com/clovaai/CLEval.git --user ``` -pip3 install -r requirements.txt + +### Build from source + +```bash +$ git clone https://github.com/clovaai/CLEval.git +$ cd cleval +$ python setup.py install --user ``` -## Instructions for the standalone scripts -### Detection evaluation +## How to use +You can replace `cleval` with `PYTHONPATH=$PWD python cleval/main.py` for evaluation using source. +```bash +$ PYTHONPATH=$PWD python cleval/main.py -g=gt/gt_IC13.zip -s=[result.zip] --BOX_TYPE=LTRB ``` -python script.py -g=gt/gt_IC13.zip -s=[result.zip] --BOX_TYPE=LTRB # IC13 -python script.py -g=gt/gt_IC15.zip -s=[result.zip] # IC15 -python script.py -g=gt/gt_TotalText.zip -s=[result.zip] --BOX_TYPE=POLY # TotalText + +### Detection evaluation (CLI) +```bash +$ cleval -g=gt/gt_IC13.zip -s=[result.zip] --BOX_TYPE=LTRB # IC13 +$ cleval -g=gt/gt_IC15.zip -s=[result.zip] # IC15 +$ cleval -g=gt/gt_TotalText.zip -s=[result.zip] --BOX_TYPE=POLY # TotalText ``` * Notes * The default value of ```BOX_TYPE``` is set to ```QUAD```. It can be explicitly set to ```--BOX_TYPE=QUAD``` when running evaluation on IC15 dataset. * Add ```--TANSCRIPTION``` option if the result file contains transcription. * Add ```--CONFIDENCES``` option if the result file contains confidence. -### End-to-end evaluation -``` -python script.py -g=gt/gt_IC13.zip -s=[result.zip] --E2E --BOX_TYPE=LTRB # IC13 -python script.py -g=gt/gt_IC15.zip -s=[result.zip] --E2E # IC15 -python script.py -g=gt/gt_TotalText.zip -s=[result.zip] --E2E --BOX_TYPE=POLY # TotalText +### End-to-end evaluation (CLI) +```bash +$ cleval -g=gt/gt_IC13.zip -s=[result.zip] --E2E --BOX_TYPE=LTRB # IC13 +$ cleval -g=gt/gt_IC15.zip -s=[result.zip] --E2E # IC15 +$ cleval -g=gt/gt_TotalText.zip -s=[result.zip] --E2E --BOX_TYPE=POLY # TotalText ``` * Notes * Adding ```--E2E``` also automatically adds ```--TANSCRIPTION``` option. Make sure that the transcriptions are included in the result file. * Add ```--CONFIDENCES``` option if the result file contains confidence. +### TorchMetric +```python +from cleval import CLEvalMetric +metric = CLEvalMetric() + +for gt, det in zip(gts, dets): + # your fancy algorithm + # ... + # gt_quads = ... + # det_quads = ... + # ... + _ = metric(det_quads, gt_quads, det_letters, gt_letters, gt_is_dcs) + +metric_out = metric.compute() +metric.reset() +``` +### Profiling +```bash +$ cleval -g=resources/test_data/gt/gt_eval_doc_v1_kr_single.zip -s=resources/test_data/pred/res_eval_doc_v1_kr_single.zip --E2E -v --DEBUG --PPROFILE > profile.txt +$ PYTHONPATH=$PWD python cleval/main.py -g resources/test_data/gt/dummy_dataset_val.json -s resources/test_data/pred/dummy_dataset_val.json --SCALE_WISE --DOMAIN_WISE --ORIENTATION --E2E --ORIENTATION -v --PROFILE --DEBUG > profile.txt +``` -### Paramter list - +| -o | ```string``` | | path to save per-sample result file 'results.zip' | | name | type | default | description | | ---- | ---- | ------- | ---- | @@ -79,30 +113,8 @@ python script.py -g=gt/gt_TotalText.zip -s=[result.zip] --E2E --BOX_TYPE=POLY | --CASE_SENSITIVE | ```boolean``` | ```True``` | set True to evaluate case-sensitively. (only used in end-to-end evaluation) | * Note : Please refer to ```arg_parser.py``` file for additional parameters and default settings used internally. -## Instructions for the webserver - -### Procedure -1. Compress the GT file of the dataset you want to evaluate into ```gt.zip``` file and the image files into ```images.zip```. -2. Copy the two files to the ```./gt/``` directory. -3. Run web.py with ```BOX_TYPE``` option. -``` -python web.py --BOX_TYPE=[LTRB,QUAD,POLY] --PORT=8080 -``` - -### Paramters for webserver -| name | type | default | description | -| ---- | ---- | ------- | ---- | -| --BOX_TYPE | ```string``` | ```QUAD``` | annotation type of box (LTRB, QUAD, POLY) | -| --PORT | ```integer``` | ```8080``` | port number for web visualization | - - -### Web Server screenshots - - - -## TODO -- [ ] Support to run the webserver with the designated GT and image files -- [ ] Calculate the length of text based on grapheme for Mulit-lingual dataset +* Note : For scalewise evaluation, we measure the ratio of the shorter length (text height) of the text-box to the longer length of the image. +Through this, evaluation for each ratio can be performed. To adjust the scales, please use SCALE_BINS argument. ## Citation ``` @@ -118,7 +130,6 @@ python web.py --BOX_TYPE=[LTRB,QUAD,POLY] --PORT=8080 CLEval has been proposed to make fair evaluation in the OCR community, so we want to hear from many researchers. We welcome any feedbacks to our metric, and appreciate pull requests if you have any comments or improvements. ## License - ``` Copyright (c) 2020-present NAVER Corp. @@ -140,3 +151,21 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + +### Contribute +Please use pre-commit which uses Black and Isort. +``` +$ pip install pre-commit +$ pre-commit install +``` + +##### Step By Step +1. Write an issue. +2. Match code style (black, isort) +3. Wirte test code. +4. Delete branch after Squash&Merge. + +Required Approve: 1 + +## Code Maintainer +- Donghyun Kim (artit.anthony@gmail.com) diff --git a/arg_parser.py b/arg_parser.py deleted file mode 100644 index 474e772..0000000 --- a/arg_parser.py +++ /dev/null @@ -1,38 +0,0 @@ -import argparse - -def str2bool(v): - return v.lower() in ("yes", "y", "true", "t", "1") - -parser = argparse.ArgumentParser(description="test global argument parser") - -# script parameters -parser.add_argument('-g', '--GT_PATH', default='gt/gt.zip', help="Path of the Ground Truth file.") -parser.add_argument('-s', '--SUBMIT_PATH', default='submit.zip', help="Path of your method's results file.") - -# webserver parameters -parser.add_argument('-o', '--OUTPUT_PATH', default='output/', help="Path to a directory where to copy the file that contains per-sample results.") -parser.add_argument('-p', '--PORT', default=8080, help='port number to show') - -# result format related parameters -parser.add_argument('--BOX_TYPE', default='QUAD', choices=['LTRB', 'QUAD', 'POLY']) -parser.add_argument('--TRANSCRIPTION', action='store_true') -parser.add_argument('--CONFIDENCES', action='store_true') -parser.add_argument('--CRLF', action='store_true') - -# end-to-end related parameters -parser.add_argument('--E2E', action='store_true') -parser.add_argument('--CASE_SENSITIVE', default=True, type=str2bool) -parser.add_argument('--RS', default=True, type=str2bool) - -# evaluation related parameters -parser.add_argument('--AREA_PRECISION_CONSTRAINT', type=float, default=0.5) -parser.add_argument('--GRANULARITY_PENALTY_WEIGHT', type=float, default=1.0) -parser.add_argument('--VERTICAL_ASPECT_RATIO_THRES', default=2.0) - -# other parameters -parser.add_argument('-t', '--NUM_WORKERS', default=2, type=int, help='number of threads to use') -parser.add_argument('--GT_SAMPLE_NAME_2_ID', default='([0-9]+)') -parser.add_argument('--DET_SAMPLE_NAME_2_ID', default='([0-9]+)') -parser.add_argument('--PER_SAMPLE_RESULTS', default=True, type=str2bool) - -PARAMS = parser.parse_args() diff --git a/box_types.py b/box_types.py deleted file mode 100644 index 39e04d8..0000000 --- a/box_types.py +++ /dev/null @@ -1,606 +0,0 @@ -import abc -import numpy as np -import cv2 -import Polygon as polygon3 -from shapely.geometry import Polygon as shapely_poly -import math -from arg_parser import PARAMS - -MAX_FIDUCIAL_POINTS = 50 - - -def get_midpoints(p1, p2): - return (p1[0] + p2[0])/2, (p1[1] + p2[1])/2 - - -def point_distance(p1, p2): - distx = math.fabs(p1[0] - p2[0]) - disty = math.fabs(p1[1] - p2[1]) - return math.sqrt(distx * distx + disty * disty) - - -def point_angle(p1, p2): - return (np.arctan2(p1[0]-p2[0], p1[1]-p2[1]) * 180 / np.pi + 360) % 360 - - -def corner_continuous_check(index1, index2, index3, index4): - pass - - -class Box(metaclass=abc.ABCMeta): - def __init__(self, points, confidence, transcription): - self.points = points - self.confidence = confidence - self.transcription = transcription - self.is_dc = transcription == "###" - - @abc.abstractmethod - def __and__(self, other) -> float: - """Returns intersection between two objects""" - pass - - @abc.abstractmethod - def subtract(self, other): - """polygon subtraction""" - pass - - @abc.abstractmethod - def center(self): - pass - - @abc.abstractmethod - def center_distance(self, other): - """center distance between each box""" - - @abc.abstractmethod - def diagonal_length(self) -> float: - """Returns diagonal length for box-level""" - pass - - @abc.abstractmethod - def is_inside(self, x, y) -> bool: - """Returns point (x, y) is inside polygon.""" - pass - - @abc.abstractmethod - def make_polygon_obj(self): - # TODO: docstring 좀 더 자세히 적기 - """Make polygon object to calculate for future""" - pass - - @abc.abstractmethod - def pseudo_character_center(self) -> list: - """get character level boxes for TedEval pseudo center""" - pass - - -class QUAD(Box): - """Points should be x1,y1,...,x4,y4 (8 points) format""" - def __init__(self, points, confidence=0.0, transcription=""): - super(QUAD, self).__init__(points, confidence, transcription) - self.polygon = self.make_polygon_obj() - if self.is_dc: - self.transcription = "#" * self.pseudo_transcription_length() - - def __and__(self, other) -> float: - """Get intersection between two area""" - poly_intersect = self.polygon & other.polygon - if len(poly_intersect) == 0: - return 0.0 - return poly_intersect.area() - - def subtract(self, other): - self.polygon = self.polygon - other.polygon - - def center(self): - return self.polygon.center() - - def center_distance(self, other): - return point_distance(self.center(), other.center()) - - def area(self): - return self.polygon.area() - - def __or__(self, other): - return self.polygon.area() + other.polygon.area() - (self & other) - - def make_polygon_obj(self): - point_matrix = np.empty((4, 2), np.int32) - point_matrix[0][0] = int(self.points[0]) - point_matrix[0][1] = int(self.points[1]) - point_matrix[1][0] = int(self.points[2]) - point_matrix[1][1] = int(self.points[3]) - point_matrix[2][0] = int(self.points[4]) - point_matrix[2][1] = int(self.points[5]) - point_matrix[3][0] = int(self.points[6]) - point_matrix[3][1] = int(self.points[7]) - return polygon3.Polygon(point_matrix) - - def aspect_ratio(self): - top_side = point_distance((self.points[0], self.points[1]), (self.points[2], self.points[3])) - right_side = point_distance((self.points[2], self.points[3]), (self.points[4], self.points[5])) - bottom_side = point_distance((self.points[4], self.points[5]), (self.points[6], self.points[7])) - left_side = point_distance((self.points[6], self.points[7]), (self.points[0], self.points[1])) - avg_hor = (top_side + bottom_side) / 2 - avg_ver = (right_side + left_side) / 2 - - return (avg_ver + 1e-5) / (avg_hor + 1e-5) - - def pseudo_transcription_length(self): - return min(round(0.5+(max(self.aspect_ratio(), 1/self.aspect_ratio()))), 10) - - def pseudo_character_center(self): - chars = list() - length = len(self.transcription) - aspect_ratio = self.aspect_ratio() - - if length == 0: - return chars - - if aspect_ratio < PARAMS.VERTICAL_ASPECT_RATIO_THRES: - left_top = self.points[0], self.points[1] - right_top = self.points[2], self.points[3] - right_bottom = self.points[4], self.points[5] - left_bottom = self.points[6], self.points[7] - else: - left_top = self.points[6], self.points[7] - right_top = self.points[0], self.points[1] - right_bottom = self.points[2], self.points[3] - left_bottom = self.points[4], self.points[5] - - p1 = get_midpoints(left_top, left_bottom) - p2 = get_midpoints(right_top, right_bottom) - - unit_x = (p2[0] - p1[0]) / length - unit_y = (p2[1] - p1[1]) / length - - for i in range(length): - x = p1[0] + unit_x / 2 + unit_x * i - y = p1[1] + unit_y / 2 + unit_y * i - chars.append((x, y)) - return chars - - def diagonal_length(self) -> float: - left_top = self.points[0], self.points[1] - right_top = self.points[2], self.points[3] - right_bottom = self.points[4], self.points[5] - left_bottom = self.points[6], self.points[7] - diag1 = point_distance(left_top, right_bottom) - diag2 = point_distance(right_top, left_bottom) - return (diag1 + diag2) / 2 - - def is_inside(self, x, y) -> bool: - return self.polygon.isInside(x, y) - - -class POLY(Box): - """Points should be x1,y1,...,xn,yn (2*n points) format""" - def __init__(self, points, confidence=0.0, transcription=""): - super(POLY, self).__init__(points, confidence, transcription) - self.num_points = len(self.points) // 2 - self.polygon = self.make_polygon_obj() - self._aspect_ratio = self.make_aspect_ratio() - if self.is_dc: - self.transcription = "#" * self.pseudo_transcription_length() - - def __and__(self, other): - """Get intersection between two area""" - poly_intersect = self.polygon.intersection(other.polygon) - return poly_intersect.area - - def subtract(self, other): - """get substraction""" - self.polygon = self.polygon.difference(self.polygon.intersection(other.polygon)) - - def __or__(self, other): - return 1.0 - - def area(self): - return self.polygon.area - - def center(self): - return self.polygon.centroid.coords[0] - - def center_distance(self, other): - try: - return point_distance(self.center(), other.center()) - except: - return 0.0001 - - def diagonal_length(self): - left_top = self.points[0], self.points[1] - right_top = self.points[self.num_points-2], self.points[self.num_points-1] - right_bottom = self.points[self.num_points], self.points[self.num_points+1] - left_bottom = self.points[self.num_points*2-2], self.points[self.num_points*2-1] - - diag1 = point_distance(left_top, right_bottom) - diag2 = point_distance(right_top, left_bottom) - - return (diag1 + diag2) / 2 - - def is_inside(self, x, y) -> bool: - return self.polygon.contains(Point(x, y)) - - def check_corner_points_are_continuous(self, lt, rt, rb, lb): - count = 0 - while lt != rt: - lt = (lt+1) % self.num_points - count += 1 - - while rb != lb: - rb = (rb+1) % self.num_points - count += 1 - - return True - - def get_four_max_distance_from_center(self): - center_x, center_y = self.center() - distance_from_center = list() - point_x = self.points[0::2] - point_y = self.points[1::2] - - for px, py in zip(point_x, point_y): - distance_from_center.append(point_distance((center_x, center_y), (px, py))) - - distance_idx_max_order = np.argsort(distance_from_center)[::-1] - return distance_idx_max_order[:4] - - def make_polygon_obj(self): - point_x = self.points[0::2] - point_y = self.points[1::2] - # in TotalText dataset, there there are under 4 points annotation for Polygon shape - # so, we have to deal with it - - # if points are given 3, fill last quad points with left bottom coordinates - if len(point_x) == len(point_y) == 3: - point_x.append(point_x[0]) - point_y.append(point_y[2]) - self.points.append(point_x[0]) - self.points.append(point_y[2]) - self.num_points = len(self.points) // 2 - # if points are given 2, copy value 2 times - elif len(point_x) == len(point_y) == 2: - point_x *= 2 - point_y *= 2 - self.points.append(point_x[1]) - self.points.append(point_y[0]) - self.points.append(point_x[0]) - self.points.append(point_y[1]) - self.num_points = len(self.points) // 2 - # if points are given 1, copy value 4 times - elif len(point_x) == len(point_y) == 1: - point_x *= 4 - point_y *= 4 - for _ in range(3): - self.points.append(point_x[0]) - self.points.append(point_x[0]) - self.num_points = len(self.points) // 2 - return shapely_poly(np.stack([point_x, point_y], axis=1)).buffer(0) - - def aspect_ratio(self): - return self._aspect_ratio - - def pseudo_transcription_length(self): - return min(round(0.5+ (max(self._aspect_ratio, 1/self._aspect_ratio))), 10) - - def make_aspect_ratio(self): - np.array(np.reshape(self.points, [-1, 2])) - rect = cv2.minAreaRect(np.array(np.reshape(self.points, [-1, 2]), dtype=np.float32)) - width = rect[1][0] - height = rect[1][1] - - width += 1e-6 - height += 1e-6 - - return min(10, height/width) + 1e+5 - - def pseudo_character_center(self): - chars = list() - length = len(self.transcription) - - # Prepare polygon line estimation with interpolation - point_x = self.points[0::2] - point_y = self.points[1::2] - points_x_top = point_x[:self.num_points//2] - points_x_bottom = point_x[self.num_points//2:] - points_y_top = point_y[:self.num_points//2] - points_y_bottom = point_y[self.num_points//2:] - - # reverse bottom point order from left to right - points_x_bottom = points_x_bottom[::-1] - points_y_bottom = points_y_bottom[::-1] - - num_interpolation_section = (self.num_points//2) - 1 - num_points_to_interpolate = length - - new_point_x_top, new_point_x_bottom = list(), list() - new_point_y_top, new_point_y_bottom = list(), list() - - for sec_idx in range(num_interpolation_section): - start_x_top, end_x_top = points_x_top[sec_idx], points_x_top[sec_idx+1] - start_y_top, end_y_top = points_y_top[sec_idx], points_y_top[sec_idx+1] - start_x_bottom, end_x_bottom = points_x_bottom[sec_idx], points_x_bottom[sec_idx + 1] - start_y_bottom, end_y_bottom = points_y_bottom[sec_idx], points_y_bottom[sec_idx + 1] - - diff_x_top = (end_x_top - start_x_top) / num_points_to_interpolate - diff_y_top = (end_y_top - start_y_top) / num_points_to_interpolate - diff_x_bottom = (end_x_bottom - start_x_bottom) / num_points_to_interpolate - diff_y_bottom = (end_y_bottom - start_y_bottom) / num_points_to_interpolate - - new_point_x_top.append(start_x_top) - new_point_x_bottom.append(start_x_bottom) - new_point_y_top.append(start_y_top) - new_point_y_bottom.append(start_y_bottom) - - for num_pt in range(1, num_points_to_interpolate): - new_point_x_top.append(int(start_x_top + diff_x_top * num_pt)) - new_point_x_bottom.append(int(start_x_bottom + diff_x_bottom * num_pt)) - new_point_y_top.append(int(start_y_top + diff_y_top * num_pt)) - new_point_y_bottom.append(int(start_y_bottom + diff_y_bottom * num_pt)) - new_point_x_top.append(points_x_top[-1]) - new_point_y_top.append(points_y_top[-1]) - new_point_x_bottom.append(points_x_bottom[-1]) - new_point_y_bottom.append(points_y_bottom[-1]) - - len_section_for_single_char = (len(new_point_x_top)-1) / len(self.transcription) - print(self.num_points) - print(len(self.transcription)) - - for c in range(len(self.transcription)): - print(len(new_point_x_top), c, len_section_for_single_char) - center_x = (new_point_x_top[int(c*len_section_for_single_char)] + - new_point_x_top[int((c+1)*len_section_for_single_char)] + - new_point_x_bottom[int(c*len_section_for_single_char)] + - new_point_x_bottom[int((c+1)*len_section_for_single_char)]) / 4 - - center_y = (new_point_y_top[int(c*len_section_for_single_char)] + - new_point_y_top[int((c+1)*len_section_for_single_char)] + - new_point_y_bottom[int(c*len_section_for_single_char)] + - new_point_y_bottom[int((c+1)*len_section_for_single_char)]) / 4 - - chars.append((center_x, center_y)) - return chars - - # def pseudo_character_center(self): - # chars = list() - - # # TODO: write comment more fancier(?) - # # Prepare polygon line estimation with interpolation - - # point_x = self.points[0::2] - # point_y = self.points[1::2] - # points_x_top = point_x[:self.num_points//2] - # points_x_bottom = point_x[self.num_points//2:] - # points_y_top = point_y[:self.num_points//2] - # points_y_bottom = point_y[self.num_points//2:] - - # points_x_bottom = points_x_bottom[::-1] - # points_y_bottom = points_y_bottom[::-1] - # # print(points_x_top, points_x_bottom, points_y_top, points_y_bottom) - - # num_interpolation_section = (self.num_points//2) - 1 - # num_points_to_interpolate = MAX_FIDUCIAL_POINTS // num_interpolation_section - - # # TODO: using computer vision? - # new_point_x_top, new_point_x_bottom = list(), list() - # new_point_y_top, new_point_y_bottom = list(), list() - - # for sec_idx in range(num_interpolation_section): - # start_x_top, end_x_top = points_x_top[sec_idx], points_x_top[sec_idx+1] - # start_y_top, end_y_top = points_y_top[sec_idx], points_y_top[sec_idx+1] - # start_x_bottom, end_x_bottom = points_x_bottom[sec_idx], points_x_bottom[sec_idx + 1] - # start_y_bottom, end_y_bottom = points_y_bottom[sec_idx], points_y_bottom[sec_idx + 1] - - # diff_x_top = (end_x_top - start_x_top) / num_points_to_interpolate - # diff_y_top = (end_y_top - start_y_top) / num_points_to_interpolate - # diff_x_bottom = (end_x_bottom - start_x_bottom) / num_points_to_interpolate - # diff_y_bottom = (end_y_bottom - start_y_bottom) / num_points_to_interpolate - - # new_point_x_top.append(start_x_top) - # new_point_x_bottom.append(start_x_bottom) - # new_point_y_top.append(start_y_top) - # new_point_y_bottom.append(start_y_bottom) - - # for num_pt in range(1, num_points_to_interpolate): - # new_point_x_top.append(int(start_x_top + diff_x_top * num_pt)) - # new_point_x_bottom.append(int(start_x_bottom + diff_x_bottom * num_pt)) - # new_point_y_top.append(int(start_y_top + diff_y_top * num_pt)) - # new_point_y_bottom.append(int(start_y_bottom + diff_y_bottom * num_pt)) - - # len_section_for_single_char = len(new_point_x_top) / len(self.transcription) - - # for c in range(len(self.transcription)): - # center_x = (new_point_x_top[int(c*len_section_for_single_char)] + - # new_point_x_top[min(int((c+1)*len_section_for_single_char), len(new_point_x_top)-1)] + - # new_point_x_bottom[int(c*len_section_for_single_char)] + - # new_point_x_bottom[min(int((c+1)*len_section_for_single_char), len(new_point_x_top)-1)]) / 4 - - # center_y = (new_point_y_top[int(c*len_section_for_single_char)] + - # new_point_y_top[min(int((c+1)*len_section_for_single_char), len(new_point_x_top)-1)] + - # new_point_y_bottom[int(c*len_section_for_single_char)] + - # new_point_y_bottom[min(int((c+1)*len_section_for_single_char), len(new_point_x_top)-1)]) / 4 - - # chars.append((center_x, center_y)) - # return chars - - # def pseudo_character_center_using_curve_fitting(self): - # if self.num_points == 4: - # return self.pseudo_character_center_prev() - # - # chars = list() - # - # point_x = self.points[0::2] - # point_y = self.points[1::2] - # - # center_x, center_y = self.center() - # - # # calculate distance between each point & center - # distance = [] - # for px, py in zip(point_x, point_y): - # distance.append(point_distance((center_x, center_y), (px, py))) - # - # # filter 4 points with maximum distance (to find each corner points) - # distance_max_idx_order = np.argsort(distance)[::-1][:4] - # - # # distinguish left/right point by x coordinate difference with center point - # left_candidate_indices = [] - # right_candidate_indices = [] - # - # for idx in distance_max_idx_order: - # if point_x[idx] < center_x: - # left_candidate_indices.append(idx) - # else: - # right_candidate_indices.append(idx) - # - # # there should be 2: 2 points for each left/right location - # print(self.transcription, left_candidate_indices, right_candidate_indices) - # - # # case - # # find minimum distance point with single left/right points, and append it to left/right candidate - # if len(left_candidate_indices) == 0 or len(right_candidate_indices) == 0: - # left_candidate_indices = [0, self.num_points-1] - # right_candidate_indices = [self.num_points//2-1, self.num_points//2] - # - # if not len(left_candidate_indices) == len(right_candidate_indices): - # distance_between_corner = list() - # if len(left_candidate_indices) == 1: - # for right_idx in right_candidate_indices: - # distance_between_corner.append(point_distance((point_x[right_idx], point_y[right_idx]), - # (point_x[left_candidate_indices[0]], - # point_y[left_candidate_indices[0]]))) - # elif len(right_candidate_indices) == 1: - # for left_idx in left_candidate_indices: - # distance_between_corner.append(point_distance((point_x[left_idx], point_y[left_idx]), - # (point_x[right_candidate_indices[0]], - # point_y[right_candidate_indices[0]]))) - # - # min_distance_point_idx = np.argsort(distance_between_corner)[0] - # if len(left_candidate_indices) == 1: - # left_candidate_indices.append(right_candidate_indices[min_distance_point_idx]) - # right_candidate_indices.pop(min_distance_point_idx) - # else: - # right_candidate_indices.append(left_candidate_indices[min_distance_point_idx]) - # left_candidate_indices.pop(min_distance_point_idx) - # - # # distinguish top / bottom points by y coordinate difference compared with each left/right points - # if point_angle((point_x[left_candidate_indices[0]], point_y[left_candidate_indices[0]]), (center_x, center_y)) <\ - # point_angle((point_x[left_candidate_indices[1]], point_y[left_candidate_indices[1]]), (center_x, center_y)): - # left_top_idx = left_candidate_indices[0] - # left_bottom_idx = left_candidate_indices[1] - # else: - # left_top_idx = left_candidate_indices[1] - # left_bottom_idx = left_candidate_indices[0] - # - # if point_angle((point_x[right_candidate_indices[0]], point_y[right_candidate_indices[0]]), (center_x, center_y)) > \ - # point_angle((point_x[right_candidate_indices[1]], point_y[right_candidate_indices[1]]), (center_x, center_y)): - # right_top_idx = right_candidate_indices[0] - # right_bottom_idx = right_candidate_indices[1] - # else: - # right_top_idx = right_candidate_indices[1] - # right_bottom_idx = right_candidate_indices[0] - # - # # if point_y[left_candidate_indices[0]] < point_y[left_candidate_indices[1]]: - # # left_top_idx = left_candidate_indices[0] - # # left_bottom_idx = left_candidate_indices[1] - # # else: - # # left_top_idx = left_candidate_indices[1] - # # left_bottom_idx = left_candidate_indices[0] - # # - # # if point_y[right_candidate_indices[0]] < point_y[right_candidate_indices[1]]: - # # right_top_idx = right_candidate_indices[0] - # # right_bottom_idx = right_candidate_indices[1] - # # else: - # # right_top_idx = right_candidate_indices[1] - # # right_bottom_idx = right_candidate_indices[0] - # - # top_x, top_y = list(), list() - # bottom_x, bottom_y = list(), list() - # top_start = left_top_idx - # top_end = right_top_idx - # bottom_start = right_bottom_idx - # bottom_end = left_bottom_idx - # - # self.check_corner_points_are_continuous(left_top_idx, right_top_idx, right_bottom_idx, left_bottom_idx) - # - # while top_start != top_end: - # top_x.append(point_x[top_start]) - # top_y.append(point_y[top_start]) - # top_start = (top_start+1) % self.num_points - # top_x.append(point_x[top_end]) - # top_y.append(point_y[top_end]) - # - # while bottom_start != bottom_end: - # bottom_x.append(point_x[bottom_start]) - # bottom_y.append(point_y[bottom_start]) - # bottom_start = (bottom_start+1) % self.num_points - # - # bottom_x.append(point_x[bottom_end]) - # bottom_y.append(point_y[bottom_end]) - # - # # reverse bottom points to make it left -> right order - # bottom_x = bottom_x[::-1] - # bottom_y = bottom_y[::-1] - # - # # pseudo interpolation for more accurate polynomial estimation - # new_point_x_top, new_point_x_bottom = list(), list() - # new_point_y_top, new_point_y_bottom = list(), list() - # - # NUM_POINTS_TO_FILL = 3 - # - # new_point_x_top.append(top_x[0]) - # new_point_y_top.append(top_y[0]) - # for sec_idx in range(len(top_x)-1): - # start_top_x, end_top_x = top_x[sec_idx], top_x[sec_idx+1] - # start_top_y, end_top_y = top_y[sec_idx], top_y[sec_idx+1] - # - # diff_x_top = (end_top_x - start_top_x) / NUM_POINTS_TO_FILL - # diff_y_top = (end_top_y - start_top_y) / NUM_POINTS_TO_FILL - # - # new_point_x_top.append(start_top_x) - # new_point_y_top.append(start_top_y) - # - # for num_pt in range(1, NUM_POINTS_TO_FILL+1): - # new_point_x_top.append(int(start_top_x + diff_x_top * num_pt)) - # new_point_y_top.append(int(start_top_y + diff_y_top * num_pt)) - # - # new_point_x_bottom.append(bottom_x[0]) - # new_point_y_bottom.append(bottom_y[0]) - # for sec_idx in range(len(bottom_x) - 1): - # start_bottom_x, end_bottom_x = bottom_x[sec_idx], bottom_x[sec_idx + 1] - # start_bottom_y, end_bottom_y = bottom_y[sec_idx], bottom_y[sec_idx + 1] - # - # diff_x_top = (end_bottom_x - start_bottom_x) / NUM_POINTS_TO_FILL - # diff_y_top = (end_bottom_y - start_bottom_y) / NUM_POINTS_TO_FILL - # - # new_point_x_bottom.append(start_bottom_x) - # new_point_y_bottom.append(start_bottom_y) - # - # for num_pt in range(1, NUM_POINTS_TO_FILL + 1): - # new_point_x_bottom.append(int(start_bottom_x + diff_x_top * num_pt)) - # new_point_y_bottom.append(int(start_bottom_y + diff_y_top * num_pt)) - # - # # do polynomial curve fitting - # top_curve = np.poly1d(np.polyfit(np.array(new_point_x_top), np.array(new_point_y_top), 2)) - # bottom_curve = np.poly1d(np.polyfit(np.array(new_point_x_bottom), np.array(new_point_y_bottom), 2)) - # - # # # do polynomial curve fitting - # # top_curve = np.poly1d(np.polyfit(np.array(top_x), np.array(top_y), 2)) - # # bottom_curve = np.poly1d(np.polyfit(np.array(bottom_x), np.array(bottom_y), 2)) - # - # # point interpolation - # interpolated_top_x = np.linspace(top_x[0], top_x[-1], len(self.transcription)+1) - # interpolated_bottom_x = np.linspace(bottom_x[0], bottom_x[-1], len(self.transcription)+1) - # - # # estimate middle point from top/bottom polynomial - # interpolated_top_y = top_curve(interpolated_top_x) - # interpolated_bottom_y = bottom_curve(interpolated_bottom_x) - # middle_curve_point_x = (interpolated_top_x + interpolated_bottom_x) / 2 - # middle_curve_point_y = (interpolated_top_y + interpolated_bottom_y) / 2 - # - # # fill character center list from middle curve information - # for c in range(len(self.transcription)): - # chars.append(((middle_curve_point_x[c] + middle_curve_point_x[c+1])/2, - # (middle_curve_point_y[c] + middle_curve_point_y[c+1])/2)) - # - # return chars - diff --git a/cleval/__init__.py b/cleval/__init__.py new file mode 100644 index 0000000..a6f768e --- /dev/null +++ b/cleval/__init__.py @@ -0,0 +1,5 @@ +from .torchmetric import CLEvalMetric + +__version__ = ["0.1.1"] + +__all__ = ["CLEvalMetric"] diff --git a/cleval/arg_parser.py b/cleval/arg_parser.py new file mode 100644 index 0000000..b2eab9d --- /dev/null +++ b/cleval/arg_parser.py @@ -0,0 +1,89 @@ +import argparse +import os + +from cleval.utils import cpu_count + + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ("yes", "true", "t", "y", "1"): + return True + elif v.lower() in ("no", "false", "f", "n", "0"): + return False + else: + raise argparse.ArgumentTypeError("Boolean value expected.") + + +def get_params(): + parser = argparse.ArgumentParser(description="test global argument parser") + + # script parameters + parser.add_argument("-g", "--GT_PATHS", nargs="+", help="Path of the Ground Truth files.") + parser.add_argument("-s", "--SUBMIT_PATHS", nargs="+", help="Path of your method's results file.") + + # webserver parameters + parser.add_argument( + "-o", + "--OUTPUT_PATH", + default="output/", + help="Path to a directory where to copy the file that" " contains per-sample results.", + ) + parser.add_argument("--DUMP_SAMPLE_RESULT", action="store_true") + parser.add_argument("-p", "--PORT", default=8080, help="port number to show") + + # result format related parameters + parser.add_argument("--BOX_TYPE", default="QUAD", choices=["LTRB", "QUAD", "POLY"]) + parser.add_argument("--TRANSCRIPTION", action="store_true") + parser.add_argument("--CONFIDENCES", action="store_true") + parser.add_argument("--CRLF", action="store_true") + + # end-to-end related parameters + parser.add_argument("--E2E", action="store_true") + parser.add_argument("--CASE_SENSITIVE", default=True, type=str2bool) + parser.add_argument("--RECOG_SCORE", default=True, type=str2bool) + + # evaluation related parameters + parser.add_argument("--AREA_PRECISION_CONSTRAINT", type=float, default=0.3) + parser.add_argument("--RECALL_GRANULARITY_PENALTY_WEIGHT", type=float, default=1.0) + parser.add_argument("--PRECISION_GRANULARITY_PENALTY_WEIGHT", type=float, default=1.0) + parser.add_argument("--VERTICAL_ASPECT_RATIO_THRESH", default=0.5) + + # orientation evaluation + parser.add_argument("--ORIENTATION", action="store_true") + + # scale-wise evaluation ( + parser.add_argument("--SCALE_WISE", action="store_true") # scale-wise evaluation + parser.add_argument("--SCALE_BINS", default=(0.0, 0.005, 0.01, 0.015, 0.02, 0.025, 0.1, 0.5, 1.0)) + + # other parameters + parser.add_argument("-t", "--NUM_WORKERS", default=-1, type=int, help="number of threads to use") + parser.add_argument( + "-v", + "--VERBOSE", + default=False, + action="store_true", + help="print evaluation progress or not", + ) + parser.add_argument("--DEBUG", action="store_true") + parser.add_argument("--PROFILE", action="store_true") + + args = parser.parse_args() + assert len(args.GT_PATHS) == len(args.SUBMIT_PATHS) == 1 + + if args.NUM_WORKERS == -1: + args.NUM_WORKERS = cpu_count() + + # We suppose there always exist transcription information on end-to-end evaluation + if args.E2E: + args.TRANSCRIPTION = True + + os.makedirs(args.OUTPUT_PATH, exist_ok=True) + return args + + +if __name__ == "__main__": + from pprint import pprint + + args = get_params() + pprint(args) diff --git a/cleval/box_types.py b/cleval/box_types.py new file mode 100644 index 0000000..6c43fcc --- /dev/null +++ b/cleval/box_types.py @@ -0,0 +1,386 @@ +import abc +import math + +import cv2 +import numpy as np +import Polygon as polygon3 +from shapely.geometry import Point +from shapely.geometry import Polygon as shapely_poly + +MAX_FIDUCIAL_POINTS = 50 + + +def get_midpoints(p1, p2): + return (p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2 + + +def point_distance(p1, p2): + distx = math.fabs(p1[0] - p2[0]) + disty = math.fabs(p1[1] - p2[1]) + return math.sqrt(distx * distx + disty * disty) + + +class Box(metaclass=abc.ABCMeta): + def __init__( + self, + points, + confidence, + transcription, + orientation=None, + is_dc=None, + ): + self.points = points + self.confidence = confidence + self.transcription = transcription + self.orientation = orientation + self.is_dc = transcription == "###" if is_dc is None else is_dc + + @abc.abstractmethod + def __and__(self, other) -> float: + """Returns intersection between two objects""" + pass + + @abc.abstractmethod + def subtract(self, other): + """polygon subtraction""" + pass + + @abc.abstractmethod + def center(self): + pass + + @abc.abstractmethod + def center_distance(self, other): + """center distance between each box""" + + @abc.abstractmethod + def diagonal_length(self) -> float: + """Returns diagonal length for box-level""" + pass + + @abc.abstractmethod + def is_inside(self, x, y) -> bool: + """Returns point (x, y) is inside polygon.""" + pass + + @abc.abstractmethod + def make_polygon_obj(self): + # TODO: docstring 좀 더 자세히 적기 + """Make polygon object to calculate for future""" + pass + + @abc.abstractmethod + def pseudo_character_center(self, *args) -> list: + """get character level boxes for TedEval pseudo center""" + pass + + +class QUAD(Box): + """Points should be x1,y1,...,x4,y4 (8 points) format""" + + def __init__( + self, + points, + confidence=0.0, + transcription="", + orientation=None, + is_dc=None, + scale=None, + ): + super().__init__(points, confidence, transcription, orientation, is_dc) + self.polygon = self.make_polygon_obj() + self.scale = scale + if self.is_dc: + self.transcription = "#" * self.pseudo_transcription_length() + + def __and__(self, other) -> float: + """Get intersection between two area""" + poly_intersect = self.polygon & other.polygon + if len(poly_intersect) == 0: + return 0.0 + return poly_intersect.area() + + def subtract(self, other): + self.polygon = self.polygon - other.polygon + + def center(self): + return self.polygon.center() + + def center_distance(self, other): + return point_distance(self.center(), other.center()) + + def area(self): + return self.polygon.area() + + def __or__(self, other): + return self.polygon.area() + other.polygon.area() - (self & other) + + def make_polygon_obj(self): + point_matrix = np.empty((4, 2), np.int32) + point_matrix[0][0] = int(self.points[0]) + point_matrix[0][1] = int(self.points[1]) + point_matrix[1][0] = int(self.points[2]) + point_matrix[1][1] = int(self.points[3]) + point_matrix[2][0] = int(self.points[4]) + point_matrix[2][1] = int(self.points[5]) + point_matrix[3][0] = int(self.points[6]) + point_matrix[3][1] = int(self.points[7]) + return polygon3.Polygon(point_matrix) + + def aspect_ratio(self): + top_side = point_distance((self.points[0], self.points[1]), (self.points[2], self.points[3])) + right_side = point_distance((self.points[2], self.points[3]), (self.points[4], self.points[5])) + bottom_side = point_distance((self.points[4], self.points[5]), (self.points[6], self.points[7])) + left_side = point_distance((self.points[6], self.points[7]), (self.points[0], self.points[1])) + avg_hor = (top_side + bottom_side) / 2 + avg_ver = (right_side + left_side) / 2 + + return (avg_hor + 1e-5) / (avg_ver + 1e-5) + + def pseudo_transcription_length(self): + return min(round(0.5 + (max(self.aspect_ratio(), 1 / self.aspect_ratio()))), 10) + + def pseudo_character_center(self, vertical_aspect_ratio_threshold): + chars = list() + length = len(self.transcription) + aspect_ratio = self.aspect_ratio() + + if length == 0: + return chars + + if aspect_ratio >= vertical_aspect_ratio_threshold: + left_top = self.points[0], self.points[1] + right_top = self.points[2], self.points[3] + right_bottom = self.points[4], self.points[5] + left_bottom = self.points[6], self.points[7] + else: + left_top = self.points[6], self.points[7] + right_top = self.points[0], self.points[1] + right_bottom = self.points[2], self.points[3] + left_bottom = self.points[4], self.points[5] + + p1 = get_midpoints(left_top, left_bottom) + p2 = get_midpoints(right_top, right_bottom) + + unit_x = (p2[0] - p1[0]) / length + unit_y = (p2[1] - p1[1]) / length + + for i in range(length): + x = p1[0] + unit_x / 2 + unit_x * i + y = p1[1] + unit_y / 2 + unit_y * i + chars.append((x, y)) + return chars + + def diagonal_length(self) -> float: + left_top = self.points[0], self.points[1] + right_top = self.points[2], self.points[3] + right_bottom = self.points[4], self.points[5] + left_bottom = self.points[6], self.points[7] + diag1 = point_distance(left_top, right_bottom) + diag2 = point_distance(right_top, left_bottom) + return (diag1 + diag2) / 2 + + def is_inside(self, x, y) -> bool: + return self.polygon.isInside(x, y) + + +class POLY(Box): + """Points should be x1,y1,...,xn,yn (2*n points) format""" + + def __init__(self, points, confidence=0.0, transcription="", orientation=None, is_dc=None): + super().__init__(points, confidence, transcription, orientation, is_dc) + self.num_points = len(self.points) // 2 + self.polygon = self.make_polygon_obj() + self._aspect_ratio = self.make_aspect_ratio() + if self.is_dc: + self.transcription = "#" * self.pseudo_transcription_length() + + def __and__(self, other): + """Get intersection between two area""" + poly_intersect = self.polygon.intersection(other.polygon) + return poly_intersect.area + + def subtract(self, other): + """get substraction""" + self.polygon = self.polygon.difference(self.polygon.intersection(other.polygon)) + + def __or__(self, other): + return 1.0 + + def area(self): + return self.polygon.area + + def center(self): + return self.polygon.centroid.coords[0] + + def center_distance(self, other): + try: + return point_distance(self.center(), other.center()) + except: + return 0.0001 + + def diagonal_length(self): + left_top = self.points[0], self.points[1] + right_top = self.points[self.num_points - 2], self.points[self.num_points - 1] + right_bottom = self.points[self.num_points], self.points[self.num_points + 1] + left_bottom = ( + self.points[self.num_points * 2 - 2], + self.points[self.num_points * 2 - 1], + ) + + diag1 = point_distance(left_top, right_bottom) + diag2 = point_distance(right_top, left_bottom) + + return (diag1 + diag2) / 2 + + def is_inside(self, x, y) -> bool: + return self.polygon.contains(Point(x, y)) + + def check_corner_points_are_continuous(self, lt, rt, rb, lb): + counter = 0 + while lt != rt: + lt = (lt + 1) % self.num_points + counter += 1 + + while rb != lb: + rb = (rb + 1) % self.num_points + counter += 1 + + return True + + def get_four_max_distance_from_center(self): + center_x, center_y = self.center() + distance_from_center = list() + point_x = self.points[0::2] + point_y = self.points[1::2] + + for px, py in zip(point_x, point_y): + distance_from_center.append(point_distance((center_x, center_y), (px, py))) + + distance_idx_max_order = np.argsort(distance_from_center)[::-1] + return distance_idx_max_order[:4] + + def make_polygon_obj(self): + point_x = self.points[0::2] + point_y = self.points[1::2] + # In TotalText dataset, there are under 4 points annotation for Polygon shape. + # so, we have to deal with it + + # if points are given 3, fill last quad points with left bottom coordinates + if len(point_x) == len(point_y) == 3: + point_x.append(point_x[0]) + point_y.append(point_y[2]) + self.points.append(point_x[0]) + self.points.append(point_y[2]) + self.num_points = len(self.points) // 2 + + # if points are given 2, copy value 2 times + elif len(point_x) == len(point_y) == 2: + point_x *= 2 + point_y *= 2 + self.points.append(point_x[1]) + self.points.append(point_y[0]) + self.points.append(point_x[0]) + self.points.append(point_y[1]) + self.num_points = len(self.points) // 2 + + # if points are given 1, copy value 4 times + elif len(point_x) == len(point_y) == 1: + point_x *= 4 + point_y *= 4 + for _ in range(3): + self.points.append(point_x[0]) + self.points.append(point_x[0]) + self.num_points = len(self.points) // 2 + return shapely_poly(np.stack([point_x, point_y], axis=1)).buffer(0) + + def aspect_ratio(self): + return self._aspect_ratio + + def pseudo_transcription_length(self): + return min(round(0.5 + (max(self._aspect_ratio, 1 / self._aspect_ratio))), 10) + + def make_aspect_ratio(self): + np.array(np.reshape(self.points, [-1, 2])) + rect = cv2.minAreaRect(np.array(np.reshape(self.points, [-1, 2]), dtype=np.float32)) + width = rect[1][0] + height = rect[1][1] + + width += 1e-6 + height += 1e-6 + + return min(10, height / width) + 1e5 + + def pseudo_character_center(self): + chars = list() + length = len(self.transcription) + + # Prepare polygon line estimation with interpolation + point_x = self.points[0::2] + point_y = self.points[1::2] + points_x_top = point_x[: self.num_points // 2] + points_x_bottom = point_x[self.num_points // 2 :] + points_y_top = point_y[: self.num_points // 2] + points_y_bottom = point_y[self.num_points // 2 :] + + # reverse bottom point order from left to right + points_x_bottom = points_x_bottom[::-1] + points_y_bottom = points_y_bottom[::-1] + + num_interpolation_section = (self.num_points // 2) - 1 + num_points_to_interpolate = length + + new_point_x_top, new_point_x_bottom = list(), list() + new_point_y_top, new_point_y_bottom = list(), list() + + for sec_idx in range(num_interpolation_section): + start_x_top, end_x_top = points_x_top[sec_idx], points_x_top[sec_idx + 1] + start_y_top, end_y_top = points_y_top[sec_idx], points_y_top[sec_idx + 1] + start_x_bottom, end_x_bottom = ( + points_x_bottom[sec_idx], + points_x_bottom[sec_idx + 1], + ) + start_y_bottom, end_y_bottom = ( + points_y_bottom[sec_idx], + points_y_bottom[sec_idx + 1], + ) + + diff_x_top = (end_x_top - start_x_top) / num_points_to_interpolate + diff_y_top = (end_y_top - start_y_top) / num_points_to_interpolate + diff_x_bottom = (end_x_bottom - start_x_bottom) / num_points_to_interpolate + diff_y_bottom = (end_y_bottom - start_y_bottom) / num_points_to_interpolate + + new_point_x_top.append(start_x_top) + new_point_x_bottom.append(start_x_bottom) + new_point_y_top.append(start_y_top) + new_point_y_bottom.append(start_y_bottom) + + for num_pt in range(1, num_points_to_interpolate): + new_point_x_top.append(int(start_x_top + diff_x_top * num_pt)) + new_point_x_bottom.append(int(start_x_bottom + diff_x_bottom * num_pt)) + new_point_y_top.append(int(start_y_top + diff_y_top * num_pt)) + new_point_y_bottom.append(int(start_y_bottom + diff_y_bottom * num_pt)) + new_point_x_top.append(points_x_top[-1]) + new_point_y_top.append(points_y_top[-1]) + new_point_x_bottom.append(points_x_bottom[-1]) + new_point_y_bottom.append(points_y_bottom[-1]) + + len_section_for_single_char = (len(new_point_x_top) - 1) / len(self.transcription) + + for c in range(len(self.transcription)): + center_x = ( + new_point_x_top[int(c * len_section_for_single_char)] + + new_point_x_top[int((c + 1) * len_section_for_single_char)] + + new_point_x_bottom[int(c * len_section_for_single_char)] + + new_point_x_bottom[int((c + 1) * len_section_for_single_char)] + ) / 4 + + center_y = ( + new_point_y_top[int(c * len_section_for_single_char)] + + new_point_y_top[int((c + 1) * len_section_for_single_char)] + + new_point_y_bottom[int(c * len_section_for_single_char)] + + new_point_y_bottom[int((c + 1) * len_section_for_single_char)] + ) / 4 + + chars.append((center_x, center_y)) + return chars diff --git a/cleval/data.py b/cleval/data.py new file mode 100644 index 0000000..78e25a7 --- /dev/null +++ b/cleval/data.py @@ -0,0 +1,152 @@ +from dataclasses import dataclass, field +from typing import Dict, List, Union + +from cleval.utils import harmonic_mean + + +class MatchReleation: + ONE_TO_ONE = "one-to-one" + MANY_TO_ONE = "many-to-one" + ONE_TO_MANY = "one-to-many" + + +@dataclass +class CoreStats: + recall: float = 0.0 + precision: float = 0.0 + hmean: float = 0.0 + + num_char_gt: int = 0 # TotalNum for Recall + num_char_det: int = 0 # TotalNum for Precisiion + gran_score_recall: float = 0.0 + num_char_tp_recall: int = 0 + gran_score_precision: float = 0.0 + num_char_tp_precision: int = 0 + + num_char_fp: int = 0 # false positive + + +@dataclass +class MatchResult: + gt_ids: List[int] + det_ids: List[int] + match_relation: str # from MatchRelation + + det: CoreStats = field(default_factory=CoreStats) + e2e: CoreStats = field(default_factory=CoreStats) + + +@dataclass +class Point: + x: int + y: int + + +@dataclass +class GTBoxResult: + id: int + points: List[Point] + pccs: List[Point] + orientation: Union[None, str] + letters: str + is_dc: bool + + +@dataclass +class DetBoxResult: + id: int + points: List[Point] + orientation: Union[None, str] + letters: str + + +@dataclass +class Stats: + det: CoreStats = field(default_factory=CoreStats) + e2e: CoreStats = field(default_factory=CoreStats) + + # split-merge cases + num_splitted: int = 0 + num_merged: int = 0 + num_char_overlapped: int = 0 + + # orientation evaluation + ori_acc: float = 0.0 + num_ori_total: int = 0 + num_ori_correct: int = 0 + + +@dataclass +class SampleResult: + matches: List[MatchResult] + gts: List[GTBoxResult] + preds: List[DetBoxResult] + stats: Stats = field(default_factory=Stats) + image_id: Union[int, None] = None + + +@dataclass +class GlobalResult: + """Object that holds each record of all samples.""" + + dataset_inform: Dict = field(default_factory=dict) + sample_results: List[SampleResult] = field(default_factory=list) + stats: Stats = field(default_factory=Stats) + + +def accumulate_result( + global_res: GlobalResult, + sample_res: SampleResult, + is_e2e: bool, + dump_sample_res: bool = False, +): + if dump_sample_res: + global_res.sample_results.append(sample_res) + accumulate_stats(global_res.stats, sample_res.stats, is_e2e) + + +def accumulate_stats(stats1: Stats, stats2: Stats, is_e2e: bool): + """Accumulate core stats exclude ori_acc.""" + stats1.num_splitted += stats2.num_splitted + stats1.num_merged += stats2.num_merged + stats1.num_char_overlapped += stats2.num_char_overlapped + stats1.num_ori_total += stats2.num_ori_total + stats1.num_ori_correct += stats2.num_ori_correct + + accumulate_core_stats(stats1.det, stats2.det) + if is_e2e: + accumulate_core_stats(stats1.e2e, stats2.e2e) + + +def accumulate_core_stats(stats1: CoreStats, stats2: CoreStats): + """Accumulate core stats exclude recall, precision, and hmean.""" + stats1.num_char_gt += stats2.num_char_gt + stats1.num_char_det += stats2.num_char_det + stats1.gran_score_recall += stats2.gran_score_recall + stats1.num_char_tp_recall += stats2.num_char_tp_recall + stats1.gran_score_precision += stats2.gran_score_precision + stats1.num_char_tp_precision += stats2.num_char_tp_precision + stats1.num_char_fp += stats2.num_char_fp + + +def calculate_global_rph(res: GlobalResult, is_e2e: bool): + calculate_rph(res.stats.det) + if is_e2e: + calculate_rph(res.stats.e2e) + + +def calculate_rph(stats: CoreStats): + total_gt = stats.num_char_gt + total_det = stats.num_char_det + tp_gt = stats.num_char_tp_recall + gran_gt = stats.gran_score_recall + tp_det = stats.num_char_tp_precision + gran_det = stats.gran_score_precision + + # Sample Score : Character correct length - Granularity Penalty + recall = 0.0 if total_gt == 0 else max(0.0, tp_gt - gran_gt) / total_gt + precision = 0.0 if total_det == 0 else max(0.0, tp_det - gran_det) / total_det + hmean = harmonic_mean(recall, precision) + stats.recall = recall + stats.precision = precision + stats.hmean = hmean diff --git a/cleval/eval_functions.py b/cleval/eval_functions.py new file mode 100644 index 0000000..a9b26cf --- /dev/null +++ b/cleval/eval_functions.py @@ -0,0 +1,567 @@ +from dataclasses import dataclass +from typing import List + +import numpy as np +from numba import njit +from numpy.typing import NDArray + +from cleval.data import ( + DetBoxResult, + GTBoxResult, + MatchReleation, + MatchResult, + Point, + SampleResult, +) +from cleval.utils import harmonic_mean, lcs + + +@dataclass +class EvalMaterial: + """EvalMaterial Dataclass + These are used for calculating eval results. + """ + + gt_pcc_points: List[List] # [gt_idx][pcc_idx] nested list which has variable length + pcc_mat_list: List[NDArray] # list of pcc_mat which has (len_det, len_pcc) shape. + pcc_mat_sum: NDArray[np.int16] # (len_gt, len_det) + ap_mat: NDArray[np.float32] # (len_gt, len_det) + ap_mat_binary: NDArray[bool] # (len_gt, len_det) + ap_constraint: float + gt_valid_indices: set + det_valid_indices: set + len_gt: int + len_det: int + + +def evaluation(args, gt_boxes, det_boxes, scale_range=(0.0, 1.0)): + """main evaluation function + + Notes: + Abbreviations for variable names. + - ap: area precision (not average precision) + - thresh: threshold + - pcc: pseudo char center + - mat: matrix + - res: result + - dc: don't care + - fp: false positive + - tran: transcription + """ + # prepare gt, det + gt_dc_indices, gt_pcc_points = prepare_gt( + gt_boxes, args.CASE_SENSITIVE, args.VERTICAL_ASPECT_RATIO_THRESH, scale_range + ) + prepare_det(det_boxes, args.CASE_SENSITIVE) + len_gt = len(gt_boxes) + len_det = len(det_boxes) + + # calc area_precision + ap_constraint = args.AREA_PRECISION_CONSTRAINT + ap_mat, ap_mat_binary = calc_area_precision(gt_boxes, det_boxes, ap_constraint) + + # calc pcc inclusion + pcc_mat_list, pcc_mat_sum = calc_pcc_inclusion(det_boxes, gt_pcc_points) + + # prepare valid indices + det_dc_indices = get_det_dc_indices(gt_dc_indices, pcc_mat_sum, ap_mat, ap_mat_binary, ap_constraint, len_det) + gt_valid_indices = set(range(len_gt)) - gt_dc_indices + det_valid_indices = set(range(len_det)) - det_dc_indices + + # construct eval material + eval_material = EvalMaterial( + gt_pcc_points, + pcc_mat_list, + pcc_mat_sum, + ap_mat, + ap_mat_binary, + ap_constraint, + gt_valid_indices, + det_valid_indices, + len_gt, + len_det, + ) + + # Matching process + match_mat, match_results = calc_match_matrix(eval_material) + + # Prepare sample_result + gt_results, det_results = get_box_results(gt_boxes, gt_pcc_points, det_boxes) + sample_res = SampleResult(match_results, gt_results, det_results) + + # Evaluation Process + eval_det(args, sample_res, gt_boxes, det_boxes, eval_material, match_mat) + + if args.E2E: + eval_e2e(args, sample_res, gt_boxes, det_boxes, eval_material, match_mat) + + if args.ORIENTATION: + eval_orientation(sample_res, gt_boxes, det_boxes, gt_valid_indices, match_mat) + + return sample_res + + +def prepare_gt(gt_boxes, is_case_sensitive, vertical_aspect_ratio_thresh, scale_range): + """prepare ground-truth boxes in evaluation format.""" + gt_dc_indices = set() # fast check via using set (hash-table) + gt_pcc_points = [] + for gt_idx, gt_box in enumerate(gt_boxes): + if not is_case_sensitive: + gt_box.transcription = gt_box.transcription.upper() + + if gt_box.is_dc or (gt_box.scale is not None and not scale_range[0] <= gt_box.scale <= scale_range[1]): + gt_dc_indices.add(gt_idx) + gt_pcc_point = gt_box.pseudo_character_center(vertical_aspect_ratio_thresh) + gt_pcc_points.append(gt_pcc_point) + + # subtract overlapping gt area from don't care boxes + # Area(Don't care) - Area(Ground Truth): + for dc_idx in gt_dc_indices: + for idx in range(len(gt_boxes)): + if idx in gt_dc_indices: + continue + if gt_boxes[idx] & gt_boxes[dc_idx] > 0: + # TODO: Consider PCC exclusion for area overlapped with don't care. + gt_boxes[dc_idx].subtract(gt_boxes[idx]) + return gt_dc_indices, gt_pcc_points + + +def prepare_det(det_boxes, is_case_sensitive): + """prepare detection results in evaluation format.""" + for det_idx, det_box in enumerate(det_boxes): + if not is_case_sensitive: + det_box.transcription = det_box.transcription.upper() + + +def calc_area_precision(gt_boxes, det_boxes, ap_constraint): + """calculate area precision between each GTbox and DETbox + Args: + gt_boxes(List[Box]): list of gt boxes + det_boxes(List[Box]): list of det boxes + ap_constraint(float): area precision contstraint + + Returns: + ap_mat(NDArray[float32]): area precision matrix + ap_mat_binary(NDArray[bool]): boolean mat that area precision >= ap_constraint + + """ + ap_mat = np.zeros([len(gt_boxes), len(det_boxes)], dtype=np.float32) + + for gt_idx, gt_box in enumerate(gt_boxes): + for det_idx, det_box in enumerate(det_boxes): + intersected_area = gt_box & det_box + det_area = det_box.area() + if det_area > 0.0: + ap_mat[gt_idx, det_idx] = intersected_area / det_area + ap_mat_binary = ap_mat >= ap_constraint + return ap_mat, ap_mat_binary + + +def calc_pcc_inclusion(det_boxes, gt_pcc_points): + """fill PCC counting matrix by iterating each GTbox and DETbox""" + len_gt = len(gt_pcc_points) + len_det = len(det_boxes) + pcc_mat_list = [] + pcc_mat_sum = np.zeros((len_gt, len_det), dtype=np.int16) + + for gt_idx, gt_word_pccs in enumerate(gt_pcc_points): + len_pcc = len(gt_word_pccs) + pcc_mat = np.zeros((len_det, len_pcc), dtype=bool) + + for det_idx, det_box in enumerate(det_boxes): + for pcc_idx, pcc_point in enumerate(gt_word_pccs): + if det_box.is_inside(pcc_point[0], pcc_point[1]): + pcc_mat[det_idx, pcc_idx] = True + pcc_mat_sum[gt_idx, det_idx] += 1 + + pcc_mat_list.append(pcc_mat) + return pcc_mat_list, pcc_mat_sum + + +def get_det_dc_indices(gt_dc_indices, pcc_mat_sum, ap_mat, ap_mat_binary, ap_constraint, len_det): + """Filter detection Don't care boxes""" + det_dc_indices = set() + if len(gt_dc_indices) > 0: + for det_idx in range(len_det): + ap_sum = 0 + for gt_idx in gt_dc_indices: + if ap_mat_binary[gt_idx, det_idx]: + det_dc_indices.add(det_idx) + break + if pcc_mat_sum[gt_idx, det_idx] > 0: + ap_sum += ap_mat[gt_idx, det_idx] + if ap_sum >= ap_constraint: + det_dc_indices.add(det_idx) + return det_dc_indices + + +def calc_match_matrix(eval_material): + """Calculate match matrix with PCC counting matrix information.""" + em = eval_material + match_results = [] + match_mat = np.zeros([em.len_gt, em.len_det], dtype=bool) + + # one-to-one match + for gt_idx in em.gt_valid_indices: + for det_idx in em.det_valid_indices: + is_matched = one_to_one_match(em.pcc_mat_sum, gt_idx, det_idx, em.ap_mat_binary, em.len_gt, em.len_det) + if is_matched: + match_result = MatchResult( + gt_ids=[gt_idx], + det_ids=[det_idx], + match_relation=MatchReleation.ONE_TO_ONE, + ) + match_results.append(match_result) + + # one-to-many match + for gt_idx in em.gt_valid_indices: + det_valid_indices_np = np.array(list(em.det_valid_indices), dtype=np.int16) + is_matched, matched_det_indices = one_to_many_match( + em.pcc_mat_sum, gt_idx, em.ap_mat_binary, det_valid_indices_np + ) + if is_matched: + match_result = MatchResult( + gt_ids=[gt_idx], + det_ids=matched_det_indices, + match_relation=MatchReleation.ONE_TO_MANY, + ) + match_results.append(match_result) + + # many-to-one match + for det_idx in em.det_valid_indices: + gt_valid_indices_np = np.array(list(em.gt_valid_indices), dtype=np.int16) + is_matched, matched_gt_indices = many_to_one_match( + em.pcc_mat_sum, det_idx, em.ap_mat, em.ap_constraint, gt_valid_indices_np + ) + if is_matched: + match_result = MatchResult( + gt_ids=matched_gt_indices, + det_ids=[det_idx], + match_relation=MatchReleation.MANY_TO_ONE, + ) + match_results.append(match_result) + + for match_result in match_results: + match_mat[match_result.gt_ids, match_result.det_ids] = True + + # clear pcc count flag for not matched pairs + for gt_idx in range(em.len_gt): + for det_idx in range(em.len_det): + if match_mat[gt_idx, det_idx]: + continue + for pcc_idx in range(len(em.gt_pcc_points[gt_idx])): + em.pcc_mat_sum[gt_idx, det_idx] -= em.pcc_mat_list[gt_idx][det_idx, pcc_idx] + em.pcc_mat_list[gt_idx][det_idx, pcc_idx] = 0 + return match_mat, match_results + + +@njit +def one_to_one_match(pcc_mat_sum, gt_idx, det_idx, ap_mat_binary, len_gt, len_det): + """One-to-One match condition""" + match_counter = 0 + for i in range(len_det): + if ap_mat_binary[gt_idx, i] and pcc_mat_sum[gt_idx, i] > 0: + match_counter += 1 + if match_counter >= 2: + break + if match_counter != 1: + return False + + match_counter = 0 + for i in range(len_gt): + if ap_mat_binary[i, det_idx] and pcc_mat_sum[i, det_idx] > 0: + match_counter += 1 + if match_counter >= 2: + break + if match_counter != 1: + return False + + if ap_mat_binary[gt_idx, det_idx] and pcc_mat_sum[gt_idx, det_idx] > 0: + return True + return False + + +@njit +def one_to_many_match(pcc_mat_sum, gt_idx, ap_mat_binary, det_valid_indices): + """One-to-Many match condition""" + many_sum = 0 + matched_det_indices = [] + for det_idx in det_valid_indices: + if ap_mat_binary[gt_idx, det_idx] and pcc_mat_sum[gt_idx, det_idx] > 0: + many_sum += pcc_mat_sum[gt_idx, det_idx] + matched_det_indices.append(det_idx) + + if many_sum > 0 and len(matched_det_indices) >= 2: + return True, matched_det_indices + else: + return False, matched_det_indices + + +@njit +def many_to_one_match(pcc_mat_sum, det_idx, ap_mat, ap_constraint, gt_valid_indices): + """Many-to-One match condition""" + many_sum = 0 + matched_gt_indices = [] + for gt_idx in gt_valid_indices: + if pcc_mat_sum[gt_idx, det_idx] > 0: + many_sum += ap_mat[gt_idx, det_idx] + matched_gt_indices.append(gt_idx) + if many_sum >= ap_constraint and len(matched_gt_indices) >= 2: + return True, matched_gt_indices + else: + return False, matched_gt_indices + + +def get_box_results(gt_boxes, gt_pcc_points, det_boxes): + gt_results = [] + for gt_idx, gt_box in enumerate(gt_boxes): + gt = GTBoxResult( + id=gt_idx, + points=__points_to_result(gt_box.points), + pccs=__pccs_to_result(gt_pcc_points[gt_idx]), + orientation=gt_box.orientation, + letters=gt_box.transcription, + is_dc=gt_box.is_dc, + ) + gt_results.append(gt) + + det_results = [] + for det_idx, det_box in enumerate(det_boxes): + det = DetBoxResult( + id=det_idx, + points=__points_to_result(det_box.points), + orientation=det_box.orientation, + letters=det_box.transcription, + ) + det_results.append(det) + + return gt_results, det_results + + +def __points_to_result(points): + points = np.array(points, dtype=np.int16).reshape(-1, 2) + new_points = [Point(int(round(pt[0])), int(round(pt[1]))) for pt in points] + return new_points + + +def __pccs_to_result(pcc_points): + return [Point(int(round(pt[0])), int(round(pt[1]))) for pt in pcc_points] + + +def eval_det(args, sample_res, gt_boxes, det_boxes, eval_material, match_mat): + stats = sample_res.stats + em = eval_material + + # res_mat has +2 size for granuarity penalty and summation of matrix + res_mat = np.zeros([em.len_gt + 2, em.len_det + 2], dtype=np.float32) + + match_mat_gts_sum = match_mat.sum(axis=0) + match_mat_dets_sum = match_mat.sum(axis=1) + pcc_checked = [np.zeros(len(pccs), dtype=bool) for pccs in em.gt_pcc_points] + + # Precision score + for det_idx in em.det_valid_indices: + if match_mat_gts_sum[det_idx] > 0: + matched_gt_indices = np.where(match_mat[:, det_idx])[0] + if len(matched_gt_indices) > 1: + stats.num_merged += 1 + + for gt_idx in matched_gt_indices: + pcc_indices = np.where(em.pcc_mat_list[gt_idx][det_idx])[0] + for pcc_idx in pcc_indices: + if not pcc_checked[gt_idx][pcc_idx]: + pcc_checked[gt_idx][pcc_idx] = True + res_mat[-2, det_idx] += 1 # for total score + res_mat[gt_idx, det_idx] += 1 + else: + stats.num_char_overlapped += 1 + gran_weight = args.PRECISION_GRANULARITY_PENALTY_WEIGHT + res_mat[-1, det_idx] = get_gran_score(len(matched_gt_indices), gran_weight) + + # Recall score + for gt_idx in em.gt_valid_indices: + found_gt_chars = 0 + if match_mat_dets_sum[gt_idx] > 0: + matched_det_indices = np.where(match_mat[gt_idx] > 0)[0] + if len(matched_det_indices) > 1: + stats.num_splitted += 1 + + found_gt_chars = np.sum(pcc_checked[gt_idx]) + gran_weight = args.RECALL_GRANULARITY_PENALTY_WEIGHT + res_mat[gt_idx, -1] = get_gran_score(len(matched_det_indices), gran_weight) + res_mat[gt_idx, -2] = found_gt_chars + + # Calculate precision / recall + num_char_gt, num_char_det = get_num_total_char(gt_boxes, em.pcc_mat_sum, em.gt_valid_indices, em.det_valid_indices) + num_char_fp = get_num_fp_char(det_boxes, em.det_valid_indices, match_mat_gts_sum) + num_char_det += num_char_fp + extract_stats(sample_res.stats.det, num_char_fp, num_char_gt, num_char_det, res_mat) + + # Calculate match-wise eval out + if args.DUMP_SAMPLE_RESULT: + for match_res in sample_res.matches: + gt_ids = match_res.gt_ids + det_ids = match_res.det_ids + num_char_gt, num_char_det = get_num_total_char(gt_boxes, em.pcc_mat_sum, gt_ids, det_ids) + num_char_fp = get_num_fp_char(det_boxes, det_ids, match_mat_gts_sum) + num_char_det += num_char_fp + extract_stats(match_res.det, num_char_fp, num_char_gt, num_char_det, res_mat) + + +def get_num_total_char(gt_boxes, pcc_mat_sum, gt_valid_indices, det_valid_indices): + """get TotalNum for detection evaluation.""" + num_char_gt = 0 + num_char_det = 0 + for gt_idx, gt_box in enumerate(gt_boxes): + if gt_idx in gt_valid_indices: + num_char_gt += len(gt_box.transcription) + num_char_det += np.sum(pcc_mat_sum[gt_idx][list(det_valid_indices)]) + return num_char_gt, num_char_det + + +def get_num_fp_char(det_boxes, det_valid_indices, match_mat_gts_sum): + """get FalsePositive for detection evaluation.""" + fp_char_counts = 0 + for det_idx in det_valid_indices: + # no match with any GTs && not matched with don't care + if match_mat_gts_sum[det_idx] == 0: + fp_char_count = round(0.5 + 1 / (1e-5 + det_boxes[det_idx].aspect_ratio())) + fp_char_counts += min(fp_char_count, 10) + return fp_char_counts + + +def eval_e2e(args, sample_res, gt_boxes, det_boxes, eval_material, match_mat): + gt_trans = [box.transcription for box in gt_boxes] + det_trans = [box.transcription for box in det_boxes] + gt_trans_not_found = [box.transcription for box in gt_boxes] + det_trans_not_found = [box.transcription for box in det_boxes] + + em = eval_material + stats = sample_res.stats + + # +2 size for granuarity penalty and summation of matrix + res_mat = np.zeros([em.len_gt + 2, em.len_det + 2], dtype=np.float32) + + match_mat_gts_sum = match_mat.sum(axis=0) + match_mat_dets_sum = match_mat.sum(axis=1) + + # Recall score + for gt_idx in em.gt_valid_indices: + if match_mat_dets_sum[gt_idx] > 0: + matched_det_indices = np.where(match_mat[gt_idx])[0] + sorted_det_indices = sort_detbox_order_by_pcc( + gt_idx, matched_det_indices, em.gt_pcc_points, em.pcc_mat_list + ) + corrected_num_chars = lcs_elimination( + gt_trans, + gt_trans_not_found, + det_trans_not_found, + gt_idx, + sorted_det_indices, + ) + res_mat[gt_idx, -2] = corrected_num_chars + gran_weight = args.RECALL_GRANULARITY_PENALTY_WEIGHT + res_mat[gt_idx, -1] = get_gran_score(len(matched_det_indices), gran_weight) + + # Precision score + for det_idx in em.det_valid_indices: + if match_mat_gts_sum[det_idx] > 0: + matched_gt_indices = np.where(match_mat[:, det_idx])[0] + gran_weight = args.PRECISION_GRANULARITY_PENALTY_WEIGHT + res_mat[-1, det_idx] = get_gran_score(len(matched_gt_indices), gran_weight) + res_mat[-2, det_idx] = len(det_trans[det_idx]) - len(det_trans_not_found[det_idx]) + + num_char_det = sum([len(det_trans[i]) for i in em.det_valid_indices]) + num_char_fp = num_char_det - np.sum(res_mat[-2]) + extract_stats(stats.e2e, num_char_fp, stats.det.num_char_gt, num_char_det, res_mat) + + if args.DUMP_SAMPLE_RESULT: + for match_res in sample_res.matches: + det_ids = match_res.det_ids + num_char_det = sum([len(det_trans[i]) for i in det_ids]) + num_char_fp = num_char_det - np.sum(res_mat[-2][det_ids]) + num_char_gt = match_res.det.num_char_gt + extract_stats(match_res.e2e, num_char_fp, num_char_gt, num_char_det, res_mat) + + +def sort_detbox_order_by_pcc(gt_idx, matched_det_indices, gt_pcc_points, pcc_mat_list): + """sort detected box order by pcc information.""" + unordered = matched_det_indices.tolist() # deepcopy + ordered_indices = [] + + char_len = len(gt_pcc_points[gt_idx]) + for pcc_idx in range(char_len): + if len(unordered) == 1: + break + + for det_idx in unordered: + if pcc_mat_list[gt_idx][det_idx, pcc_idx]: + ordered_indices.append(det_idx) + unordered.remove(det_idx) + break + + ordered_indices.append(unordered[0]) + return ordered_indices + + +def lcs_elimination(gt_trans, gt_trans_not_found, det_trans_not_found, gt_idx, sorted_det_indices): + """longest common sequence elimination by sorted detection boxes""" + target_string = "".join(det_trans_not_found[i] for i in sorted_det_indices) + lcs_length, lcs_string = lcs(gt_trans[gt_idx], target_string) + + for char in lcs_string: + gt_trans_not_found[gt_idx] = gt_trans_not_found[gt_idx].replace(char, "", 1) + + for det_idx in sorted_det_indices: + det_tran = det_trans_not_found[det_idx] + if not det_tran.find(char) < 0: + det_trans_not_found[det_idx] = det_tran.replace(char, "", 1) + break + return lcs_length + + +def eval_orientation(sample_res, gt_boxes, det_boxes, gt_valid_indices, match_mat): + gt_query = [box.orientation for box in gt_boxes] + det_query = [box.orientation for box in det_boxes] + + match_mat_dets_sum = match_mat.sum(axis=1) + counter = 0 + num_ori_correct = 0 + stats = sample_res.stats + + for gt_idx in gt_valid_indices: + if match_mat_dets_sum[gt_idx] > 0: + matched_det_indices = np.where(match_mat[gt_idx])[0] + counter += 1 + count_size = 0 if len(matched_det_indices) else 1 / len(matched_det_indices) + for det_idx in matched_det_indices: + if gt_query[gt_idx] == det_query[det_idx]: + num_ori_correct += count_size + if counter != 0: + stats.num_ori_total = counter + stats.num_ori_correct = num_ori_correct + stats.ori_acc = num_ori_correct / counter + + +def extract_stats(core_stats, num_char_fp, num_char_gt, num_char_det, res_mat): + core_stats.num_char_fp = int(num_char_fp) + core_stats.num_char_gt = total_gt = int(num_char_gt) + core_stats.num_char_det = total_det = int(num_char_det) + core_stats.num_char_tp_recall = tp_gt = int(np.sum(res_mat[-2])) + core_stats.gran_score_recall = gran_gt = float(np.sum(res_mat[:, -1])) + core_stats.num_char_tp_precision = tp_det = int(np.sum(res_mat[-2])) + core_stats.gran_score_precision = gran_det = float(np.sum(res_mat[-1])) + + # Sample Score : Character correct length - Granularity Penalty + recall = 0.0 if total_gt == 0 else max(0.0, tp_gt - gran_gt) / total_gt + precision = 0.0 if total_det == 0 else max(0.0, tp_det - gran_det) / total_det + hmean = harmonic_mean(recall, precision) + core_stats.recall = recall + core_stats.precision = precision + core_stats.hmean = hmean + + +@njit +def get_gran_score(num_splitted, penalty_weight): + """get granularity penalty given number of how many splitted""" + return max(num_splitted - 1, 0) * penalty_weight diff --git a/cleval/main.py b/cleval/main.py new file mode 100644 index 0000000..f255c8f --- /dev/null +++ b/cleval/main.py @@ -0,0 +1,316 @@ +import os +import re +import time +from concurrent.futures import ProcessPoolExecutor, as_completed +from dataclasses import asdict +from pprint import pprint + +from tqdm import tqdm + +from cleval.arg_parser import get_params +from cleval.box_types import POLY, QUAD, Box +from cleval.data import GlobalResult, accumulate_result, calculate_global_rph +from cleval.eval_functions import evaluation +from cleval.utils import ( + convert_ltrb2quad, + decode_utf8, + dump_json, + load_zip_file, + ltrb_regex_match, + quad_regex_match, +) +from cleval.validation import ( + validate_data, + validate_min_max_bounds, + validate_point_inside_bounds, +) + + +def main(): + """Also used by cli""" + start_t = time.perf_counter() + args = get_params() + + if args.PROFILE: + assert args.DEBUG, "DEBUG mode should be turned on for PPROFILE." + import pprofile + + prof = pprofile.Profile() + with prof(): + res_dict = cleval(args) + prof.print_stats() + else: + res_dict = cleval(args) + end_t = time.perf_counter() + print(f"CLEval total duration...{end_t - start_t}s") + pprint(res_dict) + + +def cleval(args): + """This process validates a method, evaluates it. + If it succeeds, generates a ZIP file with a JSON entry for each sample. + """ + validate_data(args.GT_PATHS[0], args.SUBMIT_PATHS[0], args.CRLF) + + global_res = GlobalResult() + gt_zipfile = args.GT_PATHS[0] + submit_zipfile = args.SUBMIT_PATHS[0] + gt_files, det_files, file_indices = get_file_paths(gt_zipfile, submit_zipfile) + + with tqdm(total=len(gt_files), disable=not args.VERBOSE) as pbar: + pbar.set_description("Integrating results...") + if args.DEBUG or args.NUM_WORKERS <= 1: + for gt_file, det_file, file_idx in zip(gt_files, det_files, file_indices): + sample_res = eval_single(args, gt_file, det_file, file_idx) + accumulate_result(global_res, sample_res, args.E2E, args.DUMP_SAMPLE_RESULT) + pbar.update(1) + else: + futures = [] + executor = ProcessPoolExecutor(max_workers=args.NUM_WORKERS) + for gt_file, det_file, file_idx in zip(gt_files, det_files, file_indices): + future = executor.submit(eval_single, args, gt_file, det_file, file_idx) + futures.append(future) + + for future in as_completed(futures): + sample_res = future.result() + accumulate_result(global_res, sample_res, args.E2E, args.DUMP_SAMPLE_RESULT) + pbar.update(1) + + executor.shutdown() + + # Calculate global recall, precision, hmean after accumulate all sample-results. + calculate_global_rph(global_res, args.E2E) + + res_dict = {"all": asdict(global_res.stats)} + dump_path = os.path.join(args.OUTPUT_PATH, "results.json") + dump_json(dump_path, res_dict) + + if args.DUMP_SAMPLE_RESULT: + dump_path = os.path.join(args.OUTPUT_PATH, f"sample_wise.json") + dump_json(dump_path, asdict(global_res)) + + if args.VERBOSE: + pprint("Calculated!") + pprint(res_dict) + + return res_dict + + +def get_file_paths(gt_zipfile, submit_zipfile): + gt_zipfile_loaded = load_zip_file(gt_zipfile) + submission_zipfile_loaded = load_zip_file(submit_zipfile) + gt_files, det_files, file_indices = [], [], [] + + for file_idx in gt_zipfile_loaded: + gt_file = decode_utf8(gt_zipfile_loaded[file_idx]) + + if file_idx in submission_zipfile_loaded: + det_file = decode_utf8(submission_zipfile_loaded[file_idx]) + if det_file is None: + det_file = "" + else: + det_file = "" + + gt_files.append(gt_file) + det_files.append(det_file) + file_indices.append(file_idx) + return gt_files, det_files, file_indices + + +def eval_single(args, gt_file, det_file, file_id): + gt_boxes = parse_single_file(gt_file, args.CRLF, True, False, box_type=args.BOX_TYPE) + det_boxes = parse_single_file( + det_file, + args.CRLF, + args.TRANSCRIPTION, + args.CONFIDENCES, + box_type=args.BOX_TYPE, + ) + sample_res = evaluation(args, gt_boxes, det_boxes) + sample_res.img_id = file_id + return sample_res + + +def parse_single_file( + content, + has_crlf=True, + with_transcription=False, + with_confidence=False, + img_width=0, + img_height=0, + sort_by_confidences=True, + box_type="QUAD", +): + """Returns all points, confindences and transcriptions of a file in lists. + + valid line formats: + xmin,ymin,xmax,ymax,[confidence],[transcription] + x1,y1,x2,y2,x3,y3,x4,y4,[confidence],[transcription] + """ + result_boxes = [] + lines = content.split("\r\n" if has_crlf else "\n") + for line in lines: + line = line.replace("\r", "").replace("\n", "") + if line != "": + result_box = parse_values_from_single_line( + line, + with_transcription, + with_confidence, + img_width, + img_height, + box_type=box_type, + ) + result_boxes.append(result_box) + + if with_confidence and len(result_boxes) and sort_by_confidences: + result_boxes.sort(key=lambda x: x.confidence, reverse=True) + + return result_boxes + + +def parse_values_from_single_line( + line, + with_transcription=False, + with_confidence=False, + img_width=0, + img_height=0, + box_type="QUAD", +) -> Box: + """ + Validate the format of the line. + If the line is not valid an ValueError will be raised. + If maxWidth and maxHeight are specified, all points must be inside the image bounds. + Posible values are: + LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] + LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] + LTRB="POLY": x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] + + box_type: + - LTRB: add description + - QUAD: add description + - POLY: add description + + Returns values from a textline. Points , [Confidences], [Transcriptions] + """ + confidence = 0.0 + transcription = "" + + if box_type == "LTRB": + box_type = QUAD + num_points = 4 + m = ltrb_regex_match(line, with_transcription, with_confidence) + xmin = int(m.group(1)) + ymin = int(m.group(2)) + xmax = int(m.group(3)) + ymax = int(m.group(4)) + + validate_min_max_bounds(lower_val=xmin, upper_val=xmax) + validate_min_max_bounds(lower_val=ymin, upper_val=ymax) + + points = [float(m.group(i)) for i in range(1, (num_points + 1))] + points = convert_ltrb2quad(points) + + if img_width > 0 and img_height > 0: + validate_point_inside_bounds(xmin, ymin, img_width, img_height) + validate_point_inside_bounds(xmax, ymax, img_width, img_height) + + elif box_type == "QUAD": + box_type = QUAD + + num_points = 8 + m = quad_regex_match(line, with_transcription, with_confidence) + points = [float(m.group(i)) for i in range(1, (num_points + 1))] + + # validate_clockwise_points(points) + if img_width > 0 and img_height > 0: + validate_point_inside_bounds(points[0], points[1], img_width, img_height) + validate_point_inside_bounds(points[2], points[3], img_width, img_height) + validate_point_inside_bounds(points[4], points[5], img_width, img_height) + validate_point_inside_bounds(points[6], points[7], img_width, img_height) + + elif box_type == "POLY": + # TODO: TotalText GT보고 정하기 + # TODO: 이렇게 리턴하는 건 굉장히 위험 + splitted_line = line.split(",") + tmp_transcription = list() + + if with_transcription: + tmp_transcription.append(splitted_line.pop()) + while not len("".join(tmp_transcription)): + tmp_transcription.append(splitted_line.pop()) + + if with_confidence: + if len(splitted_line) % 2 != 0: + confidence = float(splitted_line.pop()) + points = [float(x) for x in splitted_line] + else: + backward_idx = len(splitted_line) - 1 + while backward_idx > 0: + if splitted_line[backward_idx].isdigit() and len(splitted_line) % 2 != 0: + break + tmp_transcription.append(splitted_line.pop()) + backward_idx -= 1 + confidence = float(splitted_line.pop()) + points = [float(x) for x in splitted_line] + else: + if len(splitted_line) % 2 == 0: + points = [float(x) for x in splitted_line] + else: + backward_idx = len(splitted_line) - 1 + while backward_idx > 0: + if splitted_line[backward_idx].isdigit(): + break + tmp_transcription.append(splitted_line.pop()) + backward_idx -= 1 + points = [float(x) for x in splitted_line] + + transcription = ",".join(tmp_transcription) + return POLY(points, confidence=confidence, transcription=transcription) + else: + raise RuntimeError(f"Something is wrong with configuration. Box Type: [{box_type}]") + + # QUAD or LTRB format + if with_confidence: + try: + confidence = float(m.group(num_points + 1)) + except ValueError: + raise ValueError("Confidence value must be a float") + + if with_transcription: + pos_transcription = num_points + (2 if with_confidence else 1) + transcription = m.group(pos_transcription) + m2 = re.match(r"^\s*\"(.*)\"\s*$", transcription) + + # Transcription with double quotes + # We extract the value and replace escaped characters + if m2 is not None: + transcription = m2.group(1).replace("\\\\", "\\").replace('\\"', '"') + + result_box = box_type(points, confidence=confidence, transcription=transcription) + return result_box + + +def parse_jylee_annot(quad, transcription, box_type): + assert box_type == "QUAD" + points = [ + quad["x1"], + quad["y1"], + quad["x2"], + quad["y2"], + quad["x3"], + quad["y3"], + quad["x4"], + quad["y4"], + ] + result_box = QUAD(points, confidence=0.0, transcription=transcription) + return result_box + + +def parse_clova_ocr(quad, transcription, box_type): + assert box_type == "QUAD" + result_box = QUAD(quad, confidence=0.0, transcription=transcription) + return result_box + + +if __name__ == "__main__": + main() diff --git a/cleval/torchmetric.py b/cleval/torchmetric.py new file mode 100644 index 0000000..843f3e0 --- /dev/null +++ b/cleval/torchmetric.py @@ -0,0 +1,298 @@ +""" +TODO: Support scalewise eval +TODO: Support orientation accuracy +""" + +import cv2 +import numpy as np +import torch +from torchmetrics import Metric + +from cleval.box_types import QUAD +from cleval.data import SampleResult +from cleval.eval_functions import evaluation + + +class Options: + def __init__( + self, + case_sensitive, + recall_gran_penalty, + precision_gran_penalty, + vertical_aspect_ratio_thresh, + ap_constraint, + ): + self.DUMP_SAMPLE_RESULT = False + self.E2E = (True,) # change in runtime. See update function. + self.ORIENTATION = False + self.CASE_SENSITIVE = case_sensitive + self.RECALL_GRANULARITY_PENALTY_WEIGHT = recall_gran_penalty + self.PRECISION_GRANULARITY_PENALTY_WEIGHT = precision_gran_penalty + self.VERTICAL_ASPECT_RATIO_THRESH = vertical_aspect_ratio_thresh + self.AREA_PRECISION_CONSTRAINT = ap_constraint + + +class CLEvalMetric(Metric): + full_state_update: bool = False + + def __init__( + self, + dist_sync_on_step=False, + case_sensitive=True, + recall_gran_penalty=1.0, + precision_gran_penalty=1.0, + vertical_aspect_ratio_thresh=0.5, + ap_constraint=0.3, + scale_wise=False, + scale_bins=(0.0, 0.005, 0.01, 0.015, 0.02, 0.025, 0.1, 0.5, 1.0), + scale_range=(0.0, 1.0), + ): + super().__init__(dist_sync_on_step=dist_sync_on_step) + self.options = Options( + case_sensitive, + recall_gran_penalty, + precision_gran_penalty, + vertical_aspect_ratio_thresh, + ap_constraint, + ) + self.scale_range = scale_range + + self.scalewise_metric = {} + if scale_wise: + bin_ranges = [scale_bins[i : i + 2] for i in range(len(scale_bins) - 1)] + for bin_range in bin_ranges: + self.scalewise_metric[bin_range] = CLEvalMetric( + dist_sync_on_step=dist_sync_on_step, + case_sensitive=case_sensitive, + recall_gran_penalty=recall_gran_penalty, + precision_gran_penalty=precision_gran_penalty, + vertical_aspect_ratio_thresh=vertical_aspect_ratio_thresh, + ap_constraint=ap_constraint, + scale_wise=False, + scale_range=bin_range, + ) + + # Detection + self.add_state("det_num_char_gt", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state("det_num_char_det", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state( + "det_gran_score_recall", + torch.tensor(0, dtype=torch.float32), + dist_reduce_fx="sum", + ) + self.add_state( + "det_num_char_tp_recall", + torch.tensor(0, dtype=torch.int32), + dist_reduce_fx="sum", + ) + self.add_state( + "det_gran_score_precision", + torch.tensor(0, dtype=torch.float32), + dist_reduce_fx="sum", + ) + self.add_state( + "det_num_char_tp_precision", + torch.tensor(0, dtype=torch.int32), + dist_reduce_fx="sum", + ) + + self.add_state("det_num_char_fp", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + + # E2E + self.add_state("e2e_num_char_gt", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state("e2e_num_char_det", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state( + "e2e_gran_score_recall", + torch.tensor(0, dtype=torch.float32), + dist_reduce_fx="sum", + ) + self.add_state( + "e2e_num_char_tp_recall", + torch.tensor(0, dtype=torch.int32), + dist_reduce_fx="sum", + ) + self.add_state( + "e2e_gran_score_precision", + torch.tensor(0, dtype=torch.float32), + dist_reduce_fx="sum", + ) + self.add_state( + "e2e_num_char_tp_precision", + torch.tensor(0, dtype=torch.int32), + dist_reduce_fx="sum", + ) + + self.add_state("e2e_num_char_fp", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + + # split-merge cases + self.add_state("num_splitted", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state("num_merged", torch.tensor(0, dtype=torch.int32), dist_reduce_fx="sum") + self.add_state( + "num_char_overlapped", + torch.tensor(0, dtype=torch.int32), + dist_reduce_fx="sum", + ) + + def to(self, *args, **kwargs): + super().to(*args, **kwargs) + for key, metric in self.scalewise_metric.items(): + self.scalewise_metric[key] = metric.to(*args, **kwargs) + return self + + def update( + self, + det_quads, + gt_quads, + det_letters=None, + gt_letters=None, + gt_is_dcs=None, + img_longer_length=None, + ): + """ + Args: + det_quads (NDArray[float32]): (N, 8) detected quads + gt_quads (NDArray[float32]): (N, 8) target quads + det_letters (List[str]): detected letters + gt_letters (List[str]): target letters + gt_is_dcs (List[bool]): is dc gt quad? + img_longer_length (int): longer length of images + """ + gt_inps = self.__make_eval_input(gt_quads, gt_letters, gt_is_dcs, img_longer_length) + det_inps = self.__make_eval_input(det_quads, det_letters) + self.options.E2E = False if gt_letters is None and det_letters is None else True + sample_res = evaluation(self.options, gt_inps, det_inps, scale_range=self.scale_range) + self.__accumulate(sample_res) + + for metric in self.scalewise_metric.values(): + if img_longer_length is None: + raise ValueError("[img_longer_length] argument should be " "given for scalewise evaluation.") + metric( + det_quads, + gt_quads, + det_letters, + gt_letters, + gt_is_dcs, + img_longer_length, + ) + + def __make_eval_input(self, quads, letters, is_dcs=None, img_longer_length=None): + eval_inps = [] + for i in range(len(quads)): + box_scale = None + if img_longer_length is not None: + box_scale = self.__check_box_scale(quads[i], img_longer_length) + + eval_inp = QUAD( + quads[i], + confidence=0.0, + transcription=None if letters is None else letters[i], + is_dc=None if is_dcs is None else is_dcs[i], + scale=box_scale, + ) + eval_inps.append(eval_inp) + return eval_inps + + @staticmethod + def __check_box_scale(quad, img_longer_length): + """The method calculates box scale + Box scale is defined using the equation: char-height / image-longer size + The size of a box is defined w.r.t image size, allowing us to judge how sensitive + the model is to the box scale. + """ + rect = cv2.minAreaRect(quad.reshape(4, 2)) + quad = cv2.boxPoints(rect) + quad = np.around(quad) + box_w = np.linalg.norm(quad[1] - quad[0]) + np.linalg.norm(quad[3] - quad[2]) + box_h = np.linalg.norm(quad[2] - quad[1]) + np.linalg.norm(quad[0] - quad[3]) + box_scale = min(box_w, box_h) / 2 / img_longer_length + return box_scale + + def __accumulate(self, sample_res: SampleResult): + self.num_splitted += sample_res.stats.num_splitted + self.num_merged += sample_res.stats.num_merged + self.num_char_overlapped += sample_res.stats.num_char_overlapped + + self.det_num_char_gt += sample_res.stats.det.num_char_gt + self.det_num_char_det += sample_res.stats.det.num_char_det + self.det_gran_score_recall += sample_res.stats.det.gran_score_recall + self.det_num_char_tp_recall += sample_res.stats.det.num_char_tp_recall + self.det_gran_score_precision += sample_res.stats.det.gran_score_precision + self.det_num_char_tp_precision += sample_res.stats.det.num_char_tp_precision + self.det_num_char_fp += sample_res.stats.det.num_char_fp + + self.e2e_num_char_gt += sample_res.stats.e2e.num_char_gt + self.e2e_num_char_det += sample_res.stats.e2e.num_char_det + self.e2e_gran_score_recall += sample_res.stats.e2e.gran_score_recall + self.e2e_num_char_tp_recall += sample_res.stats.e2e.num_char_tp_recall + self.e2e_gran_score_precision += sample_res.stats.e2e.gran_score_precision + self.e2e_num_char_tp_precision += sample_res.stats.e2e.num_char_tp_precision + self.e2e_num_char_fp += sample_res.stats.e2e.num_char_fp + + def compute(self): + det_r, det_p, det_h = self.__calculate_rph( + self.det_num_char_gt, + self.det_num_char_det, + self.det_gran_score_recall, + self.det_num_char_tp_recall, + self.det_gran_score_precision, + self.det_num_char_tp_precision, + ) + e2e_r, e2e_p, e2e_h = self.__calculate_rph( + self.e2e_num_char_gt, + self.e2e_num_char_det, + self.e2e_gran_score_recall, + self.e2e_num_char_tp_recall, + self.e2e_gran_score_precision, + self.e2e_num_char_tp_precision, + ) + return_dict = { + "det_r": det_r, + "det_p": det_p, + "det_h": det_h, + "e2e_r": e2e_r, + "e2e_p": e2e_p, + "e2e_h": e2e_h, + "num_splitted": self.num_splitted, + "num_merged": self.num_merged, + "num_char_overlapped": self.num_char_overlapped, + "scale_wise": {}, + } + + for scale_bin, metric in self.scalewise_metric.items(): + return_dict["scale_wise"][scale_bin] = metric.compute() + + return return_dict + + def reset(self): + super().reset() + for metric in self.scalewise_metric.values(): + metric.reset() + + def __calculate_rph( + self, + num_char_gt, + num_char_det, + gran_score_recall, + num_char_tp_recall, + gran_score_precision, + num_char_tp_precision, + ): + total_gt = num_char_gt + total_det = num_char_det + gran_gt = gran_score_recall + tp_gt = num_char_tp_recall + gran_det = gran_score_precision + tp_det = num_char_tp_precision + + # Sample Score : Character correct length - Granularity Penalty + recall = 0.0 if total_gt == 0 else max(0.0, tp_gt - gran_gt) / total_gt + precision = 0.0 if total_det == 0 else max(0.0, tp_det - gran_det) / total_det + hmean = self.harmonic_mean(recall, precision) + return recall, precision, hmean + + def harmonic_mean(self, score1, score2): + """get harmonic mean value""" + if score1 + score2 == 0: + return torch.tensor(0, dtype=torch.float32, device=self.device) + else: + return (2 * score1 * score2) / (score1 + score2) diff --git a/cleval/utils.py b/cleval/utils.py new file mode 100644 index 0000000..78876da --- /dev/null +++ b/cleval/utils.py @@ -0,0 +1,203 @@ +import codecs +import json +import re +import subprocess +import zipfile + +from numba import njit + + +def load_zip_file(file): + """ + Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. + all_entries validates that all entries in the ZIP file pass the fileNameRegExp + """ + archive = zipfile.ZipFile(file, mode="r", allowZip64=True) + + pairs = dict() + for name in archive.namelist(): + key_name = ( + name.replace("gt_", "").replace("res_", "").replace(".txt", "").replace(".json", "").replace(".jpg", "") + ) + pairs[key_name] = archive.read(name) + return pairs + + +def decode_utf8(raw): + """ + Returns a Unicode object + """ + raw = codecs.decode(raw, "utf-8", "replace") + + # extracts BOM if exists + raw = raw.encode("utf8") + if raw.startswith(codecs.BOM_UTF8): + raw = raw.replace(codecs.BOM_UTF8, b"", 1) + return raw.decode("utf-8") + + +def dump_json(json_file_path, json_data): + with open(json_file_path, "w", encoding="utf-8") as f: + json.dump(json_data, f) + + +def read_json(json_file_path): + with open(json_file_path, "r", encoding="utf-8") as f: + json_data = json.load(f) + return json_data + + +def convert_ltrb2quad(points): + """Convert point format from LTRB to QUAD""" + new_points = [ + points[0], + points[1], + points[2], + points[1], + points[2], + points[3], + points[0], + points[3], + ] + return new_points + + +def ltrb_regex_match(line, with_transcription, with_confidence): + if with_transcription and with_confidence: + m = re.match( + r"^\s*(-?[0-9]+)\s*" + r",\s*(-?[0-9]+)\s*" + r",\s*([0-9]+)\s*" + r",\s*([0-9]+)\s*" + r",\s*([0-1].?[0-9]*)\s*,(.*)$", + line, + ) + if m is None: + raise ValueError("Format incorrect. " "Should be: xmin,ymin,xmax,ymax,confidence,transcription") + elif with_confidence: + m = re.match( + r"^\s*(-?[0-9]+)\s*," r"\s*(-?[0-9]+)\s*," r"\s*([0-9]+)\s*," r"\s*([0-9]+)\s*," r"\s*([0-1].?[0-9]*)\s*$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence") + elif with_transcription: + m = re.match( + r"^\s*(-?[0-9]+)\s*," r"\s*(-?[0-9]+)\s*," r"\s*([0-9]+)\s*," r"\s*([0-9]+)\s*,(.*)$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: xmin,ymin,xmax,ymax,transcription") + else: + m = re.match( + r"^\s*(-?[0-9]+)\s*," r"\s*(-?[0-9]+)\s*," r"\s*([0-9]+)\s*," r"\s*([0-9]+)\s*,?\s*$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: xmin,ymin,xmax,ymax") + return m + + +def quad_regex_match(line, with_transcription, with_confidence): + if with_transcription and with_confidence: + m = re.match( + r"^\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*([0-1].?[0-9]*)\s*,(.*)$", + line, + ) + if m is None: + raise ValueError("Format incorrect. " "Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence,transcription") + elif with_confidence: + m = re.match( + r"^\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*([0-1].?[0-9]*)\s*$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence") + elif with_transcription: + m = re.match( + r"^\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*,(.*)$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,transcription") + else: + if line[-1] == ",": + line = line[:-1] + m = re.match( + r"^\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*," + r"\s*(-?[0-9]+)\s*$", + line, + ) + if m is None: + raise ValueError("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4") + return m + + +@njit +def lcs(s1, s2): + """Longeset Common Sequence between s1 & s2""" + # https://stackoverflow.com/questions/48651891/longest-common-subsequence-in-python + if len(s1) == 0 or len(s2) == 0: + return 0, "" + matrix = [["" for _ in range(len(s2))] for _ in range(len(s1))] + for i in range(len(s1)): + for j in range(len(s2)): + if s1[i] == s2[j]: + if i == 0 or j == 0: + matrix[i][j] = s1[i] + else: + matrix[i][j] = matrix[i - 1][j - 1] + s1[i] + else: + if len(matrix[i - 1][j]) > len(matrix[i][j - 1]): + matrix[i][j] = matrix[i - 1][j] + else: + matrix[i][j] = matrix[i][j - 1] + cs = matrix[-1][-1] + return len(cs), cs + + +@njit +def harmonic_mean(score1, score2): + """get harmonic mean value""" + if score1 + score2 == 0: + return 0 + else: + return (2 * score1 * score2) / (score1 + score2) + + +def cpu_count(): + """Get number of cpu + os.cpu_count() has a problem with docker container. + For example, we have 72 cpus. os.cpu_count() always return 72 + even if we allocate only 4 cpus for container. + """ + return int(subprocess.check_output("nproc").decode().strip()) diff --git a/cleval/validation.py b/cleval/validation.py new file mode 100644 index 0000000..e23c9bb --- /dev/null +++ b/cleval/validation.py @@ -0,0 +1,69 @@ +from cleval.utils import decode_utf8, load_zip_file + + +def validate_data(gt_file, submit_file, has_crlf): + gt = load_zip_file(gt_file) + subm = load_zip_file(submit_file) + + # Validate format of GroundTruth + for k in gt: + validate_lines_in_file(k, gt[k], has_crlf) + + # Validate format of results + for k in subm: + if k not in gt: + raise ValueError("The sample %s not present in GT" % k) + validate_lines_in_file(k, subm[k], has_crlf) + + +def validate_lines_in_file(file_name, file_contents, has_crlf=True): + """This function validates that all lines of the file. + Execute line validation function for each line. + """ + utf8file = decode_utf8(file_contents) + if utf8file is None: + raise ValueError("The file %s is not UTF-8" % file_name) + + lines = utf8file.split("\r\n" if has_crlf else "\n") + for line in lines: + _ = line.replace("\r", "").replace("\n", "") + + +def validate_point_inside_bounds(x, y, img_width, img_height): + if x < 0 or x > img_width: + raise ValueError("X value (%s) not valid. Image dimensions: (%s,%s)" % (x, img_width, img_height)) + if y < 0 or y > img_height: + raise ValueError("Y value (%s) not valid. Image dimensions: (%s,%s)" % (y, img_width, img_height)) + + +def validate_min_max_bounds(lower_val, upper_val): + if lower_val > upper_val: + raise ValueError(f"Value {lower_val} should be smaller than value {upper_val}.") + + +def validate_clockwise_points(points): + """ + Validates that the points are in clockwise order. + """ + + if len(points) != 8: + raise ValueError("Points list not valid." + str(len(points))) + + point = [ + [int(points[0]), int(points[1])], + [int(points[2]), int(points[3])], + [int(points[4]), int(points[5])], + [int(points[6]), int(points[7])], + ] + edge = [ + (point[1][0] - point[0][0]) * (point[1][1] + point[0][1]), + (point[2][0] - point[1][0]) * (point[2][1] + point[1][1]), + (point[3][0] - point[2][0]) * (point[3][1] + point[2][1]), + (point[0][0] - point[3][0]) * (point[0][1] + point[3][1]), + ] + + summatory = edge[0] + edge[1] + edge[2] + edge[3] + if summatory > 0: + raise ValueError( + "Points are not clockwise. " "The coordinates of bounding quads have to be given in clockwise order." + ) diff --git a/config/config.py b/config/config.py deleted file mode 100755 index e541c11..0000000 --- a/config/config.py +++ /dev/null @@ -1,75 +0,0 @@ -import json -# Name of the script used for the evalution -evaluation_script = 'script' - -# Upload instructions -instructions = """ - -

The submitted zip file is automatically checked at the time of submission, and a submission log is presented to the user along with a confirmation of the submission. The checks performed are the following:

- - - -

See here an example of the expected submission file

-""" -# Extension of the GT file. gt.[extension] -gt_ext = "zip" -# Acronym for the task. It's used to cache the Images -acronym = "IST-T1" -# Title of the Task -title = "CLEval Web Visualization" -# Custom JavaScript for the visualiztion. -customJS = 'visualization_TL_iou.js' -# Custom CSS for the visualiztion. -customCSS = 'visualization_TL_iou.css' -# Parameters used to show the results of a method and the method's ranking -method_params = json.loads( - """{ - "Detection":{ - "recall": {"long_name":"Recall","type":"double","order":"","grafic":"1","format":"perc"}, - "precision":{"long_name":"Precision","type":"double","order":"","grafic":"1","format":"perc"}, - "hmean":{"long_name":"Hmean","type":"double","order":"desc","grafic":"1","format":"perc"}}, - "EndtoEnd":{ - "recall": {"long_name":"Recall","type":"double","order":"","grafic":"1","format":"perc"}, - "precision":{"long_name":"Precision","type":"double","order":"","grafic":"1","format":"perc"}, - "hmean":{"long_name":"Hmean","type":"double","order":"desc","grafic":"1","format":"perc"}, - "recognition_score":{"long_name":"RS","type":"double","order":"","grafic":"1","format":""}}, - "Detection_Metadata":{ - "num_merge":{"long_name":"Num merge","type":"int","order":"","grafic":"1","format":""}, - "num_split":{"long_name":"Num split","type":"int","order":"","grafic":"1","format":""}, - "num_false_pos":{"long_name":"Num FP","type":"int","order":"","grafic":"1","format":""}, - "char_miss":{"long_name":"Char miss","type":"int","order":"","grafic":"1","format":""}, - "char_overlap":{"long_name":"Char overlap","type":"int","order":"","grafic":"1","format":""}, - "char_false_pos":{"long_name":"Char FP","type":"int","order":"","grafic":"1","format":""}}, - "EndtoEnd_Metadata":{ - "num_merge":{"long_name":"Num merge","type":"int","order":"","grafic":"1","format":""}, - "num_split":{"long_name":"Num split","type":"int","order":"","grafic":"1","format":""}, - "num_false_pos":{"long_name":"Num FP","type":"int","order":"","grafic":"1","format":""}, - "char_miss":{"long_name":"Char miss","type":"int","order":"","grafic":"1","format":""}, - "char_false_pos":{"long_name":"Char FP","type":"int","order":"","grafic":"1","format":""}} - }""") -# Parameters to show for each sample -sample_params = json.loads("""{ - "Detection":{ - "recall":{"long_name":"DET Recall","type":"double","order":"","grafic":"","format":"perc"}, - "precision":{"long_name":"DET Precision","type":"double","order":"","grafic":"","format":"perc"}, - "hmean":{"long_name":"DET Hmean","type":"double","order":"desc","grafic":"","format":"perc"}}, - "EndtoEnd":{ - "recall":{"long_name":"E2E Recall","type":"double","order":"","grafic":"","format":"perc"}, - "precision":{"long_name":"E2E Precision","type":"double","order":"","grafic":"","format":"perc"}, - "hmean":{"long_name":"E2E Hmean","type":"double","order":"desc","grafic":"","format":"perc"}} - }""") -# Parameters to ask for for each submition -submit_params = json.loads("""{}""") -# Regular expression to get the Sample ID from the image name. ID must be the first capturing group. -image_name_to_id_str = '*([0-9]+)*.(jpg|gif|png)' \ No newline at end of file diff --git a/file_utils.py b/file_utils.py deleted file mode 100644 index c6f52ee..0000000 --- a/file_utils.py +++ /dev/null @@ -1,67 +0,0 @@ -import re -import codecs -import zipfile - - -def load_zip_file_keys(file, file_name_reg_exp=''): - try: - archive = zipfile.ZipFile(file, mode='r', allowZip64=True) - except: - raise Exception('Error loading the ZIP archive.') - - pairs = [] - - for name in archive.namelist(): - addFile = True - keyName = name - if file_name_reg_exp != "": - m = re.match(file_name_reg_exp, name) - if m == None: - addFile = False - else: - if len(m.groups()) > 0: - keyName = m.group(1) - - if addFile: - pairs.append(keyName) - - return pairs - - -def load_zip_file(file, file_name_reg_exp='', allEntries=False): - """ - Returns an array with the contents (filtered by fileNameRegExp) of a ZIP file. - The key's are the names or the file or the capturing group definied in the fileNameRegExp - allEntries validates that all entries in the ZIP file pass the fileNameRegExp - """ - try: - archive = zipfile.ZipFile(file, mode='r', allowZip64=True) - except: - raise Exception('Error loading the ZIP archive') - - pairs = dict() - for name in archive.namelist(): - addFile = True - keyName = name.replace('gt_', '').replace('res_', '').replace('.txt', '') - - if addFile: - pairs[keyName] = archive.read(name) - else: - if allEntries: - raise Exception('ZIP entry not valid: %s' % name) - return pairs - - -def decode_utf8(raw): - """ - Returns a Unicode object on success, or None on failure - """ - try: - raw = codecs.decode(raw, 'utf-8', 'replace') - # extracts BOM if exists - raw = raw.encode('utf8') - if raw.startswith(codecs.BOM_UTF8): - raw = raw.replace(codecs.BOM_UTF8, '', 1) - return raw.decode('utf-8') - except Exception: - return None diff --git a/gt/gt.zip b/gt/gt.zip deleted file mode 100644 index 89c6284..0000000 Binary files a/gt/gt.zip and /dev/null differ diff --git a/images/ui-icons_444444_256x240.png b/images/ui-icons_444444_256x240.png deleted file mode 100755 index 19f664d..0000000 Binary files a/images/ui-icons_444444_256x240.png and /dev/null differ diff --git a/images/ui-icons_555555_256x240.png b/images/ui-icons_555555_256x240.png deleted file mode 100755 index e965f6d..0000000 Binary files a/images/ui-icons_555555_256x240.png and /dev/null differ diff --git a/images/ui-icons_777620_256x240.png b/images/ui-icons_777620_256x240.png deleted file mode 100755 index 9785948..0000000 Binary files a/images/ui-icons_777620_256x240.png and /dev/null differ diff --git a/images/ui-icons_777777_256x240.png b/images/ui-icons_777777_256x240.png deleted file mode 100755 index 323c456..0000000 Binary files a/images/ui-icons_777777_256x240.png and /dev/null differ diff --git a/images/ui-icons_cc0000_256x240.png b/images/ui-icons_cc0000_256x240.png deleted file mode 100755 index 45ac778..0000000 Binary files a/images/ui-icons_cc0000_256x240.png and /dev/null differ diff --git a/images/ui-icons_ffffff_256x240.png b/images/ui-icons_ffffff_256x240.png deleted file mode 100755 index fe41d2d..0000000 Binary files a/images/ui-icons_ffffff_256x240.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..356ff1e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[tool.isort] +profile = "black" + +[tool.black] +line-length = 120 +target-version = ['py38'] +include = '\.pyi?$' + +[tool.pytest.ini_options] +addopts = "-s" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index b2cfa53..0000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -bottle==0.12.17 -numpy==1.17.0 -Pillow==6.1.0 -Polygon3==3.0.8 -Shapely==1.6.4.post2 -six==1.12.0 -termcolor==1.1.0 -opencv-python==3.4.2.17 -tqdm==4.40.0 diff --git a/gt/gt_IC13.zip b/resources/gt/gt_IC13.zip similarity index 100% rename from gt/gt_IC13.zip rename to resources/gt/gt_IC13.zip diff --git a/gt/gt_IC15.zip b/resources/gt/gt_IC15.zip similarity index 100% rename from gt/gt_IC15.zip rename to resources/gt/gt_IC15.zip diff --git a/gt/gt_TotalText.zip b/resources/gt/gt_TotalText.zip similarity index 100% rename from gt/gt_TotalText.zip rename to resources/gt/gt_TotalText.zip diff --git a/screenshots/explanation.gif b/resources/screenshots/explanation.gif similarity index 100% rename from screenshots/explanation.gif rename to resources/screenshots/explanation.gif diff --git a/screenshots/pic1.jpg b/resources/screenshots/pic1.jpg similarity index 100% rename from screenshots/pic1.jpg rename to resources/screenshots/pic1.jpg diff --git a/screenshots/pic2.jpg b/resources/screenshots/pic2.jpg similarity index 100% rename from screenshots/pic2.jpg rename to resources/screenshots/pic2.jpg diff --git a/resources/test_data/gt/gt_eval_doc_v1_kr.zip b/resources/test_data/gt/gt_eval_doc_v1_kr.zip new file mode 100644 index 0000000..ae4193b Binary files /dev/null and b/resources/test_data/gt/gt_eval_doc_v1_kr.zip differ diff --git a/resources/test_data/gt/gt_eval_doc_v1_kr_single.zip b/resources/test_data/gt/gt_eval_doc_v1_kr_single.zip new file mode 100644 index 0000000..6f6e28c Binary files /dev/null and b/resources/test_data/gt/gt_eval_doc_v1_kr_single.zip differ diff --git a/resources/test_data/pred/res_eval_doc_v1_kr.zip b/resources/test_data/pred/res_eval_doc_v1_kr.zip new file mode 100644 index 0000000..37f5714 Binary files /dev/null and b/resources/test_data/pred/res_eval_doc_v1_kr.zip differ diff --git a/resources/test_data/pred/res_eval_doc_v1_kr_single.zip b/resources/test_data/pred/res_eval_doc_v1_kr_single.zip new file mode 100644 index 0000000..b39f080 Binary files /dev/null and b/resources/test_data/pred/res_eval_doc_v1_kr_single.zip differ diff --git a/rrc_evaluation_funcs.py b/rrc_evaluation_funcs.py deleted file mode 100644 index 9338f7b..0000000 --- a/rrc_evaluation_funcs.py +++ /dev/null @@ -1,300 +0,0 @@ -import json -import pprint -import sys;sys.path.append('./') -import zipfile -import re -import sys -import os -from io import StringIO - -from validation import * -from file_utils import decode_utf8 -from box_types import Box, QUAD, POLY - -from arg_parser import PARAMS - - -def print_help(): - sys.stdout.write('Usage: python %s.py -g= -s= [-o= -p=]' % sys.argv[0]) - sys.exit(2) - - -def convert_LTRB2QUAD(points): - """Convert point format from LTRB to QUAD""" - new_points = [points[0], points[1], points[2], points[1], points[2], points[3], points[0], points[3]] - return new_points - - -def parse_values_from_single_line(line, withTranscription=False, withConfidence=False, img_width=0, img_height=0) -> Box: - """ - Validate the format of line. If the line is not valid, an exception will be raised. - If max_width, and max_height are specified, all poi - :param line: - :param withTranscription: - :param withConfidence: - :param img_width: - :param img_height: - :return: - """ - """ - Validate the format of the line. If the line is not valid an exception will be raised. - If maxWidth and maxHeight are specified, all points must be inside the imgage bounds. - Posible values are: - LTRB=True: xmin,ymin,xmax,ymax[,confidence][,transcription] - LTRB=False: x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] - LTRB="POLY": x1,y1,x2,y2,x3,y3,x4,y4[,confidence][,transcription] - - box_type: - - LTRB: add description - - QUAD: add description - - POLY: add description - - Returns values from a textline. Points , [Confidences], [Transcriptions] - """ - confidence = 0.0 - transcription = "" - points = [] - box_type = None - - numPoints = 4 - - if PARAMS.BOX_TYPE == "LTRB": - box_type = QUAD - numPoints = 4 - - if withTranscription and withConfidence: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$',line) - if m == None : - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$',line) - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence,transcription") - elif withConfidence: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$',line) - if m == None : - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,confidence") - elif withTranscription: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,(.*)$', line) - if m == None : - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax,transcription") - else: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*,?\s*$', line) - if m == None : - raise Exception("Format incorrect. Should be: xmin,ymin,xmax,ymax") - - xmin = int(m.group(1)) - ymin = int(m.group(2)) - xmax = int(m.group(3)) - ymax = int(m.group(4)) - - validate_min_max_bounds(lower_val=xmin, upper_val=xmax) - validate_min_max_bounds(lower_val=ymin, upper_val=ymax) - - points = [float(m.group(i)) for i in range(1, (numPoints+1))] - points = convert_LTRB2QUAD(points) - - if img_width > 0 and img_height > 0: - validate_point_inside_bounds(xmin, ymin, img_width, img_height) - validate_point_inside_bounds(xmax, ymax, img_width, img_height) - - elif PARAMS.BOX_TYPE == "QUAD": - box_type = QUAD - - numPoints = 8 - - if withTranscription and withConfidence: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*,(.*)$',line) - if m == None : - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence,transcription") - elif withConfidence: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*([0-1].?[0-9]*)\s*$',line) - if m == None : - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,confidence") - elif withTranscription: - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,(.*)$',line) - if m == None : - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4,transcription") - else: - if line[-1] == ',' : line = line[:-1] - m = re.match(r'^\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*,\s*(-?[0-9]+)\s*$',line) - if m == None : - raise Exception("Format incorrect. Should be: x1,y1,x2,y2,x3,y3,x4,y4") - - points = [float(m.group(i)) for i in range(1, (numPoints+1))] - - validate_clockwise_points(points) - - if img_width > 0 and img_height > 0: - validate_point_inside_bounds(points[0], points[1], img_width, img_height) - validate_point_inside_bounds(points[2], points[3], img_width, img_height) - validate_point_inside_bounds(points[4], points[5], img_width, img_height) - validate_point_inside_bounds(points[6], points[7], img_width, img_height) - - elif PARAMS.BOX_TYPE == "POLY": - box_type = POLY - # TODO: TotalText GT보고 정하기 - # TODO: 이렇게 리턴하는 건 굉장히 위험 - splitted_line = line.split(',') - tmp_transcription = list() - - if withTranscription: - tmp_transcription.append(splitted_line.pop()) - while not len("".join(tmp_transcription)): - tmp_transcription.append(splitted_line.pop()) - - if withConfidence: - if len(splitted_line) % 2 != 0: - confidence = float(splitted_line.pop()) - points = [float(x) for x in splitted_line] - else: - backward_idx = len(splitted_line)-1 - while backward_idx > 0: - if splitted_line[backward_idx].isdigit() and len(splitted_line) % 2 != 0: - break - tmp_transcription.append(splitted_line.pop()) - backward_idx -= 1 - confidence = float(splitted_line.pop()) - points = [float(x) for x in splitted_line] - else: - if len(splitted_line) % 2 == 0: - points = [float(x) for x in splitted_line] - else: - backward_idx = len(splitted_line) - 1 - while backward_idx > 0: - if splitted_line[backward_idx].isdigit(): - break - tmp_transcription.append(splitted_line.pop()) - backward_idx -= 1 - points = [float(x) for x in splitted_line] - - transcription = ",".join(tmp_transcription) - return POLY(points, confidence=confidence, transcription=transcription) - - # QUAD or LTRB format - if withConfidence: - try: - confidence = float(m.group(numPoints+1)) - except ValueError: - raise Exception("Confidence value must be a float") - - if withTranscription: - posTranscription = numPoints + (2 if withConfidence else 1) - transcription = m.group(posTranscription) - m2 = re.match(r'^\s*\"(.*)\"\s*$', transcription) - if m2 != None : # Transcription with double quotes, we extract the value and replace escaped characters - transcription = m2.group(1).replace("\\\\", "\\").replace("\\\"", "\"") - - result_box = box_type(points, confidence=confidence, transcription=transcription) - return result_box - - -# TODO: why not using this logic? -def validate_clockwise_points(points): - """ - Validates that the points that the 4 points that dlimite a polygon are in clockwise order. - """ - - if len(points) != 8: - raise Exception("Points list not valid." + str(len(points))) - - point = [ - [int(points[0]) , int(points[1])], - [int(points[2]) , int(points[3])], - [int(points[4]) , int(points[5])], - [int(points[6]) , int(points[7])] - ] - edge = [ - ( point[1][0] - point[0][0])*( point[1][1] + point[0][1]), - ( point[2][0] - point[1][0])*( point[2][1] + point[1][1]), - ( point[3][0] - point[2][0])*( point[3][1] + point[2][1]), - ( point[0][0] - point[3][0])*( point[0][1] + point[3][1]) - ] - - summatory = edge[0] + edge[1] + edge[2] + edge[3] - if summatory > 0: - raise Exception("Points are not clockwise. The coordinates of bounding quadrilaterals have to be given in clockwise order. Regarding the correct interpretation of 'clockwise' remember that the image coordinate system used is the standard one, with the image origin at the upper left, the X axis extending to the right and Y axis extending downwards.") - - -def parse_single_file(content, CRLF=True, LTRB=True, withTranscription=False, withConfidence=False, img_width=0, img_height=0, sort_by_confidences=True): - """ - Returns all points, confindences and transcriptions of a file in lists. Valid line formats: - xmin,ymin,xmax,ymax,[confidence],[transcription] - x1,y1,x2,y2,x3,y3,x4,y4,[confidence],[transcription] - """ - result_boxes = [] - - lines = content.split("\r\n" if CRLF else "\n") - for line in lines: - line = line.replace("\r", "").replace("\n", "") - if line != "": - result_box = parse_values_from_single_line(line, withTranscription, withConfidence, img_width, img_height) - result_boxes.append(result_box) - - if withConfidence and len(result_boxes) and sort_by_confidences: - result_boxes.sort(key=lambda x: x.confidence, reverse=True) - - return result_boxes - - -def main_evaluation(validate_data_fn, evaluate_method_fn, show_result=True, per_sample=True): - """ - This process validates a method, evaluates it and if it succed generates a ZIP file with a JSON entry for each sample. - Params: - p: Dictionary of parmeters with the GT/submission locations. If None is passed, the parameters send by the system are used. - default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation - validate_data_fn: points to a method that validates the corrct format of the submission - evaluate_method_fn: points to a function that evaluated the submission and return a Dictionary with the results - """ - - resDict = {'calculated': True, 'Message': '', 'method': '{}', 'per_sample': '{}'} - - validate_data_fn(PARAMS.GT_PATH, PARAMS.SUBMIT_PATH) - evalData = evaluate_method_fn(PARAMS.GT_PATH, PARAMS.SUBMIT_PATH) - resDict.update(evalData) - - if PARAMS.OUTPUT_PATH: - if not os.path.exists(PARAMS.OUTPUT_PATH): - os.makedirs(PARAMS.OUTPUT_PATH) - - resultsOutputname = PARAMS.OUTPUT_PATH + '/results.zip' - outZip = zipfile.ZipFile(resultsOutputname, mode='w', allowZip64=True) - - del resDict['per_sample'] - if 'output_items' in resDict.keys(): - del resDict['output_items'] - - outZip.writestr('method.json', json.dumps(resDict)) - - if not resDict['calculated']: - if show_result: - sys.stderr.write('Error!\n' + resDict['Message']) - if PARAMS.OUTPUT_PATH: - outZip.close() - return resDict - - if PARAMS.OUTPUT_PATH: - if per_sample: - for k,v in evalData['per_sample'].items(): - outZip.writestr( k + '.json', json.dumps(v)) - - if 'output_items' in evalData.keys(): - for k, v in evalData['output_items'].items(): - outZip.writestr(k, v) - - outZip.close() - - if show_result: - pprint.pprint("Calculated!") - pprint.pprint(resDict['method']) - - return resDict - - -def main_validation(validate_data_fn): - """ - This process validates a method - Params: - default_evaluation_params_fn: points to a function that returns a dictionary with the default parameters used for the evaluation - validate_data_fn: points to a method that validates the corrct format of the submission - """ - validate_data_fn(PARAMS.GT_PATH, PARAMS.SUBMIT_PATH) - print('SUCCESS') - sys.exit(0) diff --git a/script.py b/script.py deleted file mode 100644 index a1c80e2..0000000 --- a/script.py +++ /dev/null @@ -1,718 +0,0 @@ -from itertools import permutations -import rrc_evaluation_funcs -import math - -import numpy as np -import Polygon as polygon3 -from shapely.geometry import Point - -from file_utils import load_zip_file, decode_utf8 -from validation import validate_data -from arg_parser import PARAMS - -import concurrent.futures -from tqdm import tqdm - - -def granularity_score(num_splitted): - """get granularity penalty given number of how many splitted""" - return max(num_splitted-1, 0) * PARAMS.GRANULARITY_PENALTY_WEIGHT - -def get_element_total_length(l): - return sum([len(x) for x in l]) - -def harmonic_mean(score1, score2): - """get harmonic mean value""" - if score1+score2 == 0: - return 0 - else: - return (2*score1*score2) / (score1+score2) - -def lcs(s1, s2): - """Longeset Common Sequence between s1 & s2""" - # source: https://stackoverflow.com/questions/48651891/longest-common-subsequence-in-python - if len(s1) == 0 or len(s2) == 0: - return 0, '' - matrix = [["" for x in range(len(s2))] for x in range(len(s1))] - for i in range(len(s1)): - for j in range(len(s2)): - if s1[i] == s2[j]: - if i == 0 or j == 0: - matrix[i][j] = s1[i] - else: - matrix[i][j] = matrix[i-1][j-1] + s1[i] - else: - matrix[i][j] = max(matrix[i-1][j], matrix[i][j-1], key=len) - cs = matrix[-1][-1] - return len(cs), cs - - -class GlobalResult(object): - """Object that holds each record of all samples.""" - - def __init__(self, with_e2e=False): - self.with_e2e = with_e2e # flags for calculate end-to-end or not - - # recording stats for detection evaluation - self.det_correct_num_recall = 0 # CorrectNum for Recall - self.det_correct_num_precision = 0 # CorrectNum for Precision - self.chars_gt = 0 # TotalNum for Recall (Both detection ,end-to-end) - self.chars_det = 0 # TotalNum for Precisiion - - # recording stats for end-to-end evaluation - self.e2e_correct_num_recall = 0 # CorrectNum for Recall - self.e2e_correct_num_precision = 0 # CorrectNum for Precision - self.chars_recog = 0 # TotalNum for Precision - - # metadata information - self.num_splitted = 0 - self.num_merged = 0 - self.num_false_positive = 0 - self.char_missed = 0 - self.char_overlapped = 0 - self.char_false_positive = 0 - - # # recording stats for end-to-end evaluation - self.e2e_correct_num_recall = 0 # CorrectNum for Recall - self.e2e_correct_num_precision = 0 # CorrectNum for Precision - self.chars_recog = 0 # TotalNum for Precision - - self.e2e_char_missed = 0 - self.e2e_char_false_positive = 0 - self.e2e_recog_score_chars = 0 - self.e2e_recog_score_correct_num = 0 - - - - - def accumulate_stats(self, sample_dict): - - """accumulate single sample statistics in this object.""" - self.det_correct_num_recall += sample_dict['det_correct_num_recall'] - self.det_correct_num_precision += sample_dict['det_correct_num_precision'] - self.chars_gt += sample_dict['chars_gt'] - self.chars_det += sample_dict['chars_det'] - - self.num_splitted += sample_dict['num_splitted'] - self.num_merged += sample_dict['num_merged'] - self.num_false_positive += sample_dict['num_false_positive'] - self.char_missed += sample_dict['char_missed'] - self.char_overlapped += sample_dict['char_overlapped'] - self.char_false_positive += sample_dict['char_false_positive'] - - if self.with_e2e: - self.e2e_correct_num_recall += sample_dict['e2e_correct_num_recall'] - self.e2e_correct_num_precision += sample_dict['e2e_correct_num_precision'] - self.chars_recog += sample_dict['chars_recog'] - - self.e2e_char_missed += sample_dict['e2e_char_missed'] - self.e2e_char_false_positive += sample_dict['e2e_char_false_positive'] - self.e2e_recog_score_chars += sample_dict['e2e_recog_score_chars'] - self.e2e_recog_score_correct_num += sample_dict['e2e_recog_score_correct_num'] - - - def to_dict(self): - """make stats to dictionary.""" - det_recall = 0 if self.chars_gt == 0 else self.det_correct_num_recall / self.chars_gt - det_precision = 0 if self.chars_det == 0 else self.det_correct_num_precision / self.chars_det - det_hmean = harmonic_mean(det_recall, det_precision) - - result_dict = { - 'Detection': { - 'recall': det_recall, - 'precision': det_precision, - 'hmean': det_hmean - }, - 'Detection_Metadata': { - 'num_merge': self.num_merged, - 'num_split': self.num_splitted, - 'num_false_pos': self.num_false_positive, - 'char_miss': self.char_missed, - 'char_overlap': self.char_overlapped, - 'char_false_pos': self.char_false_positive - } - - } - - e2e_recall = 0 if self.chars_gt == 0 else self.e2e_correct_num_recall / self.chars_gt - e2e_precision = 0 if self.chars_recog == 0 else self.e2e_correct_num_precision / self.chars_recog - e2e_hmean = harmonic_mean(e2e_recall, e2e_precision) - e2e_recog_score = 0 if self.e2e_recog_score_chars == 0 else self.e2e_recog_score_correct_num / self.e2e_recog_score_chars - result_dict.update({ - 'EndtoEnd': { - 'recall': e2e_recall, - 'precision': e2e_precision, - 'hmean': e2e_hmean, - 'recognition_score': e2e_recog_score, - }, - 'EndtoEnd_Metadata': { - 'num_merge': self.num_merged, - 'num_split': self.num_splitted, - 'num_false_pos': self.num_false_positive, - 'char_miss': self.e2e_char_missed, - 'char_false_pos': self.e2e_char_false_positive - } - }) - return result_dict - - -class SampleResult(object): - """Object that holds result of single sample.""" - def __init__(self, with_e2e=False, with_recog_score=False): - self.with_e2e = with_e2e - self.with_recog_score = with_recog_score - self.det_recall = 0 # Detection Recall score - self.det_precision = 0 # Detection Precision score - self.det_hmean = 0 # Detection H-Mean score - - self.chars_gt = 0 # TotalNum for Recall (Both detection ,end-to-end) - self.chars_det = 0 # TotalNum for Precisiion - - self.det_correct_num_recall = 0 # CorrectNum for recall - self.det_correct_num_precision = 0 # CorrectNum for precision - - # detection metadata information - self.num_splitted = 0 - self.num_merged = 0 - self.num_false_pos = 0 - self.char_missed = 0 - self.char_overlapped = 0 - self.char_false_pos = 0 - - if self.with_e2e: - # create variables for end-to-end evaluation - self.e2e_recall = 0 # End-to-End Recall score - self.e2e_precision = 0 # End-to-End Precision score - self.e2e_hmean = 0 # End-to-End H-Mean score - self.e2e_recog_score = 0 # Recognition Score - self.chars_recog = 0 - self.e2e_correct_num_precision = 0 - self.e2e_correct_num_recall = 0 - - self.e2e_result_matrix = np.zeros([1,1]) - self.e2e_char_missed = 0 - self.e2e_char_false_pos = 0 - self.e2e_recog_score_chars = 0 - self.e2e_recog_score_correct_num = 0 - - # Array of GT keys marked as don't Care - self.gt_dont_care_indices = [] - - # Array of Detected keys matched with a don't Care GT - self.det_dont_care_indices = [] - - # matrix for storing matching information (M_ij) - self.match_matrix = np.zeros([1,1]) - - # pseudo character centers (gt_pcc_points[i][k] == (c_i)^k ) - self.gt_pcc_points = [] - self.gt_char_counts = [] - - # for counting how may PCC points included - self.pcc_count_matrix = [] # ( (m_ij)^k ) - self.gt_pcc_checked = [] - self.gt_pcc_count = [] - - # for storing area precision / sample scoring - self.area_precision_matrix = np.zeros([1,1]) - self.det_result_matrix = np.zeros([1,1]) - - # for storing pairs to visualize on Web - # (just to give different color for case OO, OM, MO, not used for evaluation) - self.pairs = [] - - # Below variables are for web visualization - # evaluation logs - self.eval_log = "" - - # character count matrix - self.character_counts = np.zeros(([1,1])) - - def prepare_gt(self, gt_boxes): - """prepare ground-truth boxes in evaluation format.""" - self.gt_boxes = gt_boxes - for gt_idx, gt_box in enumerate(self.gt_boxes): - if not PARAMS.CASE_SENSITIVE: - gt_box.transcription = gt_box.transcription.upper() - - if gt_box.is_dc: - self.gt_dont_care_indices.append(gt_idx) - self.gt_pcc_points.append(gt_box.pseudo_character_center()) - self.eval_log += "GT polygons: " + str(len(self.gt_boxes)) + (" (" + str(len(self.gt_dont_care_indices)) + " don't care) \n") - - # subtract overlapping gt area from don't care boxes - # Area(Don't care) - Area(Ground Truth): - for dc in self.gt_dont_care_indices: - for idx in list(set(range(len(self.gt_boxes))) - set(self.gt_dont_care_indices)): - if self.gt_boxes[idx] & self.gt_boxes[dc] > 0: - # TODO: currently, PCC exclusion for area overlapped with don't care is not considered. - self.gt_boxes[dc].subtract(self.gt_boxes[idx]) - - - def prepare_det(self, det_boxes): - """prepare detection results in evaluation format.""" - self.det_boxes = det_boxes - for det_idx, det_box in enumerate(self.det_boxes): - if not PARAMS.CASE_SENSITIVE: - det_box.transcription = det_box.transcription.upper() - - self.eval_log += "DET polygons: {}\n".format(str(len(self.det_boxes))) - - - def total_character_counts(self): - """get TotalNum for detection evaluation.""" - total_num_recall = 0 - total_num_precision = 0 - for gt_idx in range(len(self.gt_boxes)): - if gt_idx not in self.gt_dont_care_indices: - total_num_recall += len(self.gt_boxes[gt_idx].transcription) - - for det_idx in range(len(self.det_boxes)): - if det_idx not in self.det_dont_care_indices: - total_num_precision += sum(self.pcc_count_matrix[gt_idx][det_idx]) - return total_num_recall, total_num_precision - - def get_false_positive_char_counts(self): - """get FalsePositive for detection evaluation.""" - fp_char_counts = 0 - for det_idx in range(len(self.det_boxes)): - # no match with any GTs && not matched with don't care - if self.match_matrix.sum(axis=0)[det_idx] == 0 and det_idx not in self.det_dont_care_indices: - fp_char_counts += min(round(0.5+(1 / (1e-5 + self.det_boxes[det_idx].aspect_ratio()))), 10) - return fp_char_counts - - def sort_detbox_order_by_pcc(self, gt_idx, det_indices): - """sort detected box order by pcc information.""" - char_len = len(self.gt_pcc_points[gt_idx]) - - not_ordered_yet = det_indices - ordered_indices = list() - - for c in range(char_len): - if len(not_ordered_yet) == 1: - break - - for det_id in not_ordered_yet: - if self.pcc_count_matrix[gt_idx][det_id][c] == 1: - ordered_indices.append(det_id) - not_ordered_yet.remove(det_id) - break - - ordered_indices.append(not_ordered_yet[0]) - return ordered_indices - - def lcs_elimination(self, gt_idx, sorted_det_indices): - """longest common sequence elimination by sorted detection boxes""" - standard_script = self.gtQuery[gt_idx] - lcs_length, lcs_string = lcs(standard_script, "".join(self.det_trans_not_found[idx] for idx in sorted_det_indices)) - for c in lcs_string: - self.gt_trans_not_found[gt_idx] = self.gt_trans_not_found[gt_idx].replace(c, '', 1) - - for det_idx in sorted_det_indices: - if not self.det_trans_not_found[det_idx].find(c) < 0: - self.det_trans_not_found[det_idx] = self.det_trans_not_found[det_idx].replace(c, '', 1) - break - return lcs_length - - def calc_area_precision(self): - """calculate area precision between each GTbox and DETbox""" - for gt_idx, gt_box in enumerate(self.gt_boxes): - det_char_counts = [] - self.gt_pcc_checked.append(np.zeros(len(self.gt_pcc_points[gt_idx]))) - for det_idx, det_box in enumerate(self.det_boxes): - intersected_area = gt_box & det_box - if det_box.area() > 0.0: - self.area_precision_matrix[gt_idx, det_idx] = intersected_area / det_box.area() - det_char_counts.append(np.zeros(len(self.gt_pcc_points[gt_idx]))) - self.gt_char_counts.append(det_char_counts) - self.pcc_count_matrix.append(det_char_counts) - - def calc_pcc_inclusion(self): - """fill PCC counting matrix by iterating each GTbox and DETbox""" - for gt_id, gt_box in enumerate(self.gt_boxes): - pcc_points = gt_box.pseudo_character_center() - for det_id, det_box in enumerate(self.det_boxes): - for pcc_id, pcc_point in enumerate(pcc_points): - if det_box.is_inside(pcc_point[0], pcc_point[1]): - self.pcc_count_matrix[gt_id][det_id][pcc_id] = 1 - - def filter_det_dont_care(self): - """Filter detection Don't care boxes""" - if len(self.gt_dont_care_indices) > 0: - for det_id in range(len(self.det_boxes)): - area_precision_sum = 0 - for gt_id in self.gt_dont_care_indices: - if sum(self.pcc_count_matrix[gt_id][det_id]) > 0: - area_precision_sum += self.area_precision_matrix[gt_id][det_id] - if area_precision_sum > PARAMS.AREA_PRECISION_CONSTRAINT: - self.det_dont_care_indices.append(det_id) - else: - for gt_id in self.gt_dont_care_indices: - if self.area_precision_matrix[gt_id, det_id] > PARAMS.AREA_PRECISION_CONSTRAINT: - self.det_dont_care_indices.append(det_id) - break - - self.eval_log += " (" + str(len(self.det_dont_care_indices)) + " don't care)\n" if len(self.det_dont_care_indices) > 0 else "\n" - - def one_to_one_match(self, row, col): - """One-to-One match condition""" - cont = 0 - for j in range(len(self.area_precision_matrix[0])): - if sum(self.pcc_count_matrix[row][j]) > 0 and self.area_precision_matrix[row, j] >= PARAMS.AREA_PRECISION_CONSTRAINT: - cont = cont + 1 - if cont != 1: - return False - cont = 0 - for i in range(len(self.area_precision_matrix)): - if sum(self.pcc_count_matrix[i][col]) > 0 and self.area_precision_matrix[i, col] >= PARAMS.AREA_PRECISION_CONSTRAINT: - cont = cont + 1 - if cont != 1: - return False - - if sum(self.pcc_count_matrix[row][col]) > 0 and self.area_precision_matrix[row, col] >= PARAMS.AREA_PRECISION_CONSTRAINT: - return True - return False - - - def one_to_many_match(self, gt_id): - """One-to-Many match condition""" - many_sum = 0 - detRects = [] - for det_idx in range(len(self.area_precision_matrix[0])): - if det_idx not in self.det_dont_care_indices: - if self.area_precision_matrix[gt_id, det_idx] >= PARAMS.AREA_PRECISION_CONSTRAINT and \ - sum(self.pcc_count_matrix[gt_id][det_idx]) > 0: - many_sum += sum(self.pcc_count_matrix[gt_id][det_idx]) - detRects.append(det_idx) - - if many_sum > 0 and len(detRects) >= 2: - return True, detRects - else: - return False, [] - - def many_to_one_match(self, det_id): - """Many-to-One match condition""" - many_sum = 0 - gtRects = [] - for gt_idx in range(len(self.area_precision_matrix)): - if gt_idx not in self.gt_dont_care_indices: - if sum(self.pcc_count_matrix[gt_idx][det_id]) > 0: - many_sum += self.area_precision_matrix[gt_idx][det_id] - gtRects.append(gt_idx) - if many_sum >= PARAMS.AREA_PRECISION_CONSTRAINT and len(gtRects) >= 2: - return True, gtRects - else: - return False, [] - - - def calc_match_matrix(self): - """Calculate match matrix with PCC counting matrix information.""" - self.eval_log += "Find one-to-one matches\n" - for gt_id in range(len(self.gt_boxes)): - for det_id in range(len(self.det_boxes)): - if gt_id not in self.gt_dont_care_indices and det_id not in self.det_dont_care_indices: - match = self.one_to_one_match(gt_id, det_id) - if match: - self.pairs.append({'gt': [gt_id], 'det': [det_id], 'type': 'OO'}) - self.eval_log += "Match GT #{} with Det #{}\n".format(gt_id, det_id) - - # one-to-many match - self.eval_log += "Find one-to-many matches\n" - for gt_id in range(len(self.gt_boxes)): - if gt_id not in self.gt_dont_care_indices: - match, matched_det = self.one_to_many_match(gt_id) - if match: - self.pairs.append({'gt': [gt_id], 'det': matched_det, 'type': 'OM'}) - self.eval_log += "Match GT #{} with Det #{}\n".format(gt_id, matched_det) - - # many-to-one match - self.eval_log += "Find many-to-one matches\n" - for det_id in range(len(self.det_boxes)): - if det_id not in self.det_dont_care_indices: - match, matched_gt = self.many_to_one_match(det_id) - if match: - self.pairs.append({'gt': matched_gt, 'det': [det_id], 'type': 'MO'}) - self.eval_log += "Match GT #{} with Det #{}\n".format(matched_gt, det_id) - - for pair in self.pairs: - self.match_matrix[pair['gt'], pair['det']] = 1 - - # clear pcc count flag for not matched pairs - for gt_idx in range(len(self.gt_boxes)): - for det_idx in range(len(self.det_boxes)): - if not self.match_matrix[gt_idx][det_idx]: - for pcc in range(len(self.gt_pcc_points[gt_idx])): - self.pcc_count_matrix[gt_idx][det_idx][pcc] = 0 - - def eval_det(self): - self.eval_log += "Detection | PRECISION\n" - for detNum in range(len(self.det_boxes)): - box_precision = 0 - if detNum in self.det_dont_care_indices: - continue - - if self.match_matrix.sum(axis=0)[detNum] > 0: - matched_gt_indices = np.where(self.match_matrix[:, detNum] == 1)[0] - if len(matched_gt_indices) > 1: - self.num_merged += 1 - - for gt_idx in matched_gt_indices: - intermediate_precision = 0 - found_char_pos = np.where(self.pcc_count_matrix[gt_idx][detNum] == 1)[0] - for x in found_char_pos: - if self.gt_pcc_checked[gt_idx][x] == 0: - self.gt_pcc_checked[gt_idx][x] = 1 - box_precision += 1 - intermediate_precision += 1 - elif self.gt_pcc_checked[gt_idx][x] >= 1: - self.char_overlapped += 1 - self.det_result_matrix[gt_idx][detNum] = intermediate_precision - - self.det_result_matrix[len(self.gt_boxes)][detNum] = box_precision - self.det_result_matrix[len(self.gt_boxes)+1][detNum] = granularity_score(len(matched_gt_indices)) - else: - self.num_false_pos += 1 - - # Recall score - self.eval_log += "Detection | RECALL\n" - for gtNum in range(len(self.gt_boxes)): - if gtNum in self.gt_dont_care_indices: - continue - found_gt_chars = 0 - num_gt_characters = len(self.gt_pcc_points[gtNum]) - box_char_recall_list = np.ones(num_gt_characters) - if self.match_matrix.sum(axis=1)[gtNum] > 0: - matched_det_indices = np.where(self.match_matrix[gtNum] > 0)[0] - if len(matched_det_indices) > 1: - self.num_splitted += 1 - - found_gt_chars = np.sum(self.gt_pcc_checked[gtNum]) - self.char_missed += int(np.sum(box_char_recall_list) - found_gt_chars) - self.det_result_matrix[gtNum][len(self.det_boxes)+1] = granularity_score(len(matched_det_indices)) - else: - self.char_missed += int(np.sum(box_char_recall_list)) - - self.det_result_matrix[gtNum][len(self.det_boxes)] = found_gt_chars - - - # Pseudo Character Center Visualization - self.character_counts = np.zeros((len(self.gt_boxes), len(self.det_boxes))) - for gtNum in range(len(self.gt_boxes)): - for detNum in range(len(self.det_boxes)): - self.character_counts[gtNum][detNum] = sum(self.gt_char_counts[gtNum][detNum]) - - # calculate precision / recall - self.chars_gt, self.chars_det = self.total_character_counts() - self.char_false_pos += self.get_false_positive_char_counts() - self.chars_det += self.char_false_pos - - self.eval_log += "# of false positive chars\n" - self.eval_log += "{}\n".format(self.char_false_pos) - - # Sample Score : Character correct length - Granularity Penalty - self.det_correct_num_recall = max(np.sum(self.det_result_matrix[:, len(self.det_boxes)]) - np.sum(self.det_result_matrix[:, len(self.det_boxes)+1]), 0) - self.det_correct_num_precision = max(np.sum(self.det_result_matrix[len(self.gt_boxes)]) - np.sum(self.det_result_matrix[len(self.gt_boxes)+1]), 0) - - self.det_recall = float(0) if self.chars_gt == 0 else float(self.det_correct_num_recall) / self.chars_gt - self.det_precision = float(0) if self.chars_det == 0 else float(self.det_correct_num_precision) / self.chars_det - self.det_hmean = harmonic_mean(self.det_recall, self.det_precision) - - - def eval_e2e(self): - self.gtQuery = [box.transcription for box in self.gt_boxes] - self.detQuery = [box.transcription for box in self.det_boxes] - self.gt_trans_not_found = [box.transcription for box in self.gt_boxes] - self.det_trans_not_found = [box.transcription for box in self.det_boxes] - - self.eval_log += "=================================\n" - self.eval_log += "End-to-End | Recall\n" - for gtNum in range(len(self.gt_boxes)): - if gtNum in self.gt_dont_care_indices: - continue - - if self.match_matrix.sum(axis=1)[gtNum] > 0: - matched_det_indices = np.where(self.match_matrix[gtNum] > 0)[0] - - sorted_det_indices = self.sort_detbox_order_by_pcc(gtNum, matched_det_indices.tolist()) - corrected_num_chars = self.lcs_elimination(gtNum, sorted_det_indices) - - self.e2e_result_matrix[gtNum][len(self.det_boxes)] = corrected_num_chars - self.e2e_result_matrix[gtNum][len(self.det_boxes)+1] = granularity_score(len(matched_det_indices)) - - self.eval_log += "End-to-End | Precision\n" - for detNum in range(len(self.det_boxes)): - if detNum in self.det_dont_care_indices: - continue - - if self.match_matrix.sum(axis=0)[detNum] > 0: - matched_gt_indices = np.where(self.match_matrix[:, detNum] == 1)[0] - self.e2e_result_matrix[len(self.gt_boxes)+1][detNum] = granularity_score(len(matched_gt_indices)) - self.e2e_result_matrix[len(self.gt_boxes)][detNum] = len(self.detQuery[detNum]) - len(self.det_trans_not_found[detNum]) - - - self.chars_recog = get_element_total_length([x for k, x in enumerate(self.detQuery) if k not in self.det_dont_care_indices]) - - # Sample Score : Character correct length - Granularity Penalty - self.e2e_correct_num_recall = max(np.sum(self.e2e_result_matrix[:, len(self.det_boxes)]) - np.sum(self.e2e_result_matrix[:, len(self.det_boxes)+1]), 0) - self.e2e_correct_num_precision = max(np.sum(self.e2e_result_matrix[len(self.gt_boxes)]) - np.sum(self.e2e_result_matrix[len(self.gt_boxes)+1]), 0) - - self.e2e_char_missed = self.chars_gt - self.e2e_correct_num_recall - self.e2e_char_false_pos = self.chars_recog - np.sum(self.e2e_result_matrix[len(self.gt_boxes)]) - - - self.e2e_recall = float(0) if self.chars_gt == 0 else float(self.e2e_correct_num_recall) / self.chars_gt - self.e2e_precision = float(0) if self.chars_recog == 0 else float(self.e2e_correct_num_precision) / self.chars_recog - self.e2e_hmean = harmonic_mean(self.e2e_recall, self.e2e_precision) - - # Additional recognition score calculation. Notated as RS in paper. - for det in np.where(np.sum(self.match_matrix, axis=0) > 0)[0]: - self.e2e_recog_score_chars += len(self.detQuery[det]) - self.e2e_recog_score_correct_num = np.sum(self.e2e_result_matrix[len(self.gt_boxes)]) - self.e2e_recog_score = float(0) if self.e2e_recog_score_chars == 0 else float(self.e2e_recog_score_correct_num) / self.e2e_recog_score_chars - - - def evaluation(self): - self.area_precision_matrix = np.zeros([len(self.gt_boxes), len(self.det_boxes)]) - self.det_result_matrix = np.zeros([len(self.gt_boxes)+2, len(self.det_boxes)+2]) - self.match_matrix = np.zeros([len(self.gt_boxes), len(self.det_boxes)]) - - self.calc_area_precision() - self.calc_pcc_inclusion() - self.filter_det_dont_care() - self.calc_match_matrix() - - # Matching Process - self.eval_det() - - # Evaluation Process - if self.with_e2e: - self.e2e_result_matrix = np.zeros([len(self.gt_boxes)+2, len(self.det_boxes)+2]) - self.eval_e2e() - - def to_dict(self): - # print(self.pcc_count_matrix[0], type(self.pcc_count_matrix[0])) - # print(self.pcc_count_matrix[0][0], type(self.pcc_count_matrix[0][0])) - - sample_metric_dict = { - 'Rawdata': { - 'det_correct_num_recall': self.det_correct_num_recall, - 'det_correct_num_precision': self.det_correct_num_precision, - 'chars_gt': self.chars_gt, - 'chars_det': self.chars_det, - 'num_splitted': self.num_splitted, - 'num_merged': self.num_merged, - 'num_false_positive': self.num_false_pos, - 'char_missed': self.char_missed, - 'char_overlapped': self.char_overlapped, - 'char_false_positive': self.char_false_pos - }, - 'Detection': { - 'precision': self.det_precision, - 'recall': self.det_recall, - 'hmean': self.det_hmean, - }, - 'pairs': self.pairs, - 'detectionMat': [] if len(self.gt_boxes) > 100 else self.det_result_matrix.tolist(), - 'precisionMat': [] if len(self.det_boxes) > 100 else self.area_precision_matrix.tolist(), - 'gtPolPoints': [box.points for box in self.gt_boxes], - 'detPolPoints': [box.points for box in self.det_boxes], - 'gtCharPoints': self.gt_pcc_points, - 'gtCharCounts': [np.sum(x, axis=0).tolist() for x in self.pcc_count_matrix], - 'gtDontCare': self.gt_dont_care_indices, - 'detDontCare': self.det_dont_care_indices, - 'evaluationParams': vars(PARAMS), - 'evaluationLog': self.eval_log - } - - if self.with_e2e: - sample_metric_dict['Rawdata'].update({ - 'e2e_correct_num_recall': self.e2e_correct_num_recall, - 'e2e_correct_num_precision': self.e2e_correct_num_precision, - 'chars_recog': self.chars_recog, - 'e2e_char_missed': self.e2e_char_missed, - 'e2e_char_false_positive': self.e2e_char_false_pos, - 'e2e_recog_score_chars': self.e2e_recog_score_chars, - 'e2e_recog_score_correct_num': self.e2e_recog_score_correct_num - }) - sample_metric_dict.update({ - 'EndtoEnd': { - 'precision': self.e2e_precision, - 'recall': self.e2e_recall, - 'hmean': self.e2e_hmean, - 'recognition_score': self.e2e_recog_score - }, - 'end2endMat': [] if len(self.gt_boxes) > 100 else self.e2e_result_matrix.tolist(), - 'gtTrans': [box.transcription for box in self.gt_boxes], - 'detTrans': [box.transcription for box in self.det_boxes], - 'gtQuery': self.gtQuery, - 'detQuery': self.detQuery - }) - - return sample_metric_dict - - -def eval_single_result(gt_file, det_file): - sample_result = SampleResult(PARAMS.E2E, PARAMS.RS) - # We suppose there always exist transcription information on end-to-end evaluation - if PARAMS.E2E: - PARAMS.TRANSCRIPTION = True - gt_boxes = rrc_evaluation_funcs.parse_single_file(gt_file, PARAMS.CRLF, PARAMS.BOX_TYPE, True, False) - sample_result.prepare_gt(gt_boxes) - det_boxes = rrc_evaluation_funcs.parse_single_file(det_file, PARAMS.CRLF, PARAMS.BOX_TYPE, - PARAMS.TRANSCRIPTION, PARAMS.CONFIDENCES) - sample_result.prepare_det(det_boxes) - sample_result.evaluation() - return sample_result.to_dict() - -def cleval_evaluation(gt_file, submit_file): - """ - evaluate and returns the results - Returns with the following values: - - method (required) Global method metrics. Ex: { 'Precision':0.8,'Recall':0.9 } - - per_sample (optional) Per sample metrics. Ex: {'sample1' : { 'Precision':0.8,'Recall':0.9 }, - 'sample2' : { 'Precision':0.8,'Recall':0.9 } - """ - - # storing overall result - overall_result = GlobalResult(PARAMS.E2E) - - # to store per sample evaluation results - per_sample_metrics = {} - - gt_files = load_zip_file(gt_file, PARAMS.GT_SAMPLE_NAME_2_ID) - submission_files = load_zip_file(submit_file, PARAMS.DET_SAMPLE_NAME_2_ID, True) - - # prepare ThreadPool for multi-process - executor = concurrent.futures.ProcessPoolExecutor(max_workers=PARAMS.NUM_WORKERS) - futures = {} - bar_len = len(gt_files) - - for file_idx in gt_files: - gt_file = rrc_evaluation_funcs.decode_utf8(gt_files[file_idx]) - if file_idx in submission_files: - det_file = decode_utf8(submission_files[file_idx]) - if det_file is None: - det_file = "" - else: - det_file = "" - - future = executor.submit(eval_single_result, gt_file, det_file) - futures[future] = file_idx - - with tqdm(total=bar_len) as pbar: - pbar.set_description("Integrating results...") - for future in concurrent.futures.as_completed(futures): - file_idx = futures[future] - result = future.result() - per_sample_metrics[file_idx] = result - overall_result.accumulate_stats(result['Rawdata']) - pbar.update(1) - - executor.shutdown() - - resDict = {'calculated': True, 'Message': '', 'method': overall_result.to_dict(), 'per_sample': per_sample_metrics} - return resDict - - -if __name__ == '__main__': - rrc_evaluation_funcs.main_evaluation(validate_data, cleval_evaluation) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..21e524c --- /dev/null +++ b/setup.py @@ -0,0 +1,40 @@ +import setuptools + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="cleval", + version="0.1.1", + author="dong.hyun", + author_email="dong.hyun@navercorp.com", + description="cleval", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://oss.navercorp.com/CLOVA-AI-OCR/cleval", + packages=setuptools.find_packages(), + install_requires=[ + "bottle", + "requests", + "Pillow", + "Polygon3", + "Shapely", + "tqdm", + "pprofile", + "numba>=0.58.0", + "six", + "torchmetrics>=1.2.0", + "numpy", + ], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + entry_points={ + "console_scripts": [ + "cleval = cleval.main:main", + ], + }, + python_requires=">=3.7", +) diff --git a/static/Clova-ico.png b/static/Clova-ico.png deleted file mode 100644 index d06e45b..0000000 Binary files a/static/Clova-ico.png and /dev/null differ diff --git a/static/Clova.png b/static/Clova.png deleted file mode 100644 index d21cfae..0000000 Binary files a/static/Clova.png and /dev/null differ diff --git a/static/funcs.js b/static/funcs.js deleted file mode 100755 index 9d92313..0000000 --- a/static/funcs.js +++ /dev/null @@ -1,15 +0,0 @@ - -var getUrlParameter = function getUrlParameter(sParam) { - var sPageURL = decodeURIComponent(window.location.search.substring(1)), - sURLVariables = sPageURL.split('&'), - sParameterName, - i; - - for (i = 0; i < sURLVariables.length; i++) { - sParameterName = sURLVariables[i].split('='); - - if (sParameterName[0] === sParam) { - return sParameterName[1] === undefined ? true : sParameterName[1]; - } - } -}; \ No newline at end of file diff --git a/static/jquery-1.8.2.min.js b/static/jquery-1.8.2.min.js deleted file mode 100755 index f65cf1d..0000000 --- a/static/jquery-1.8.2.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v1.8.2 jquery.com | jquery.org/license */ -(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.2",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return a!=null?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||p.guid++:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&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 p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c=0)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c=0)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,d+""),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0:p.find(m,this,null,[f]).length),h[m]&&j.push(l);j.length&&u.push({elem:f,matches:j})}o.length>q&&u.push({elem:this,matches:o.slice(q)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function bc(a,b,c,d){c=c||[],b=b||r;var e,f,i,j,k=b.nodeType;if(!a||typeof a!="string")return c;if(k!==1&&k!==9)return[];i=g(b);if(!i&&!d)if(e=P.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&h(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return w.apply(c,x.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&_&&b.getElementsByClassName)return w.apply(c,x.call(b.getElementsByClassName(j),0)),c}return bp(a.replace(L,"$1"),b,c,d,i)}function bd(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function be(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bf(a){return z(function(b){return b=+b,z(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 bg(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bh(a,b){var c,d,f,g,h,i,j,k=C[o][a];if(k)return b?0:k.slice(0);h=a,i=[],j=e.preFilter;while(h){if(!c||(d=M.exec(h)))d&&(h=h.slice(d[0].length)),i.push(f=[]);c=!1;if(d=N.exec(h))f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=d[0].replace(L," ");for(g in e.filter)(d=W[g].exec(h))&&(!j[g]||(d=j[g](d,r,!0)))&&(f.push(c=new q(d.shift())),h=h.slice(c.length),c.type=g,c.matches=d);if(!c)break}return b?h.length:h?bc.error(a):C(a,i).slice(0)}function bi(a,b,d){var e=b.dir,f=d&&b.dir==="parentNode",g=u++;return b.first?function(b,c,d){while(b=b[e])if(f||b.nodeType===1)return a(b,c,d)}:function(b,d,h){if(!h){var i,j=t+" "+g+" ",k=j+c;while(b=b[e])if(f||b.nodeType===1){if((i=b[o])===k)return b.sizset;if(typeof i=="string"&&i.indexOf(j)===0){if(b.sizset)return b}else{b[o]=k;if(a(b,d,h))return b.sizset=!0,b;b.sizset=!1}}}else while(b=b[e])if(f||b.nodeType===1)if(a(b,d,h))return b}}function bj(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 bk(a,b,c,d,e){var f,g=[],h=0,i=a.length,j=b!=null;for(;h-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==l)||((b=c).nodeType?j(a,c,d):k(a,c,d))}];for(;i1&&bj(m),i>1&&a.slice(0,i-1).join("").replace(L,"$1"),c,i0,f=a.length>0,g=function(h,i,j,k,m){var n,o,p,q=[],s=0,u="0",x=h&&[],y=m!=null,z=l,A=h||f&&e.find.TAG("*",m&&i.parentNode||i),B=t+=z==null?1:Math.E;y&&(l=i!==r&&i,c=g.el);for(;(n=A[u])!=null;u++){if(f&&n){for(o=0;p=a[o];o++)if(p(n,i,j)){k.push(n);break}y&&(t=B,c=++g.el)}d&&((n=!p&&n)&&s--,h&&x.push(n))}s+=u;if(d&&u!==s){for(o=0;p=b[o];o++)p(x,q,i,j);if(h){if(s>0)while(u--)!x[u]&&!q[u]&&(q[u]=v.call(k));q=bk(q)}w.apply(k,q),y&&!h&&q.length>0&&s+b.length>1&&bc.uniqueSort(k)}return y&&(t=B,l=z),x};return g.el=0,d?z(g):g}function bo(a,b,c,d){var e=0,f=b.length;for(;e2&&(j=h[0]).type==="ID"&&b.nodeType===9&&!f&&e.relative[h[1].type]){b=e.find.ID(j.matches[0].replace(V,""),b,f)[0];if(!b)return c;a=a.slice(h.shift().length)}for(g=W.POS.test(a)?-1:h.length-1;g>=0;g--){j=h[g];if(e.relative[k=j.type])break;if(l=e.find[k])if(d=l(j.matches[0].replace(V,""),R.test(h[0].type)&&b.parentNode||b,f)){h.splice(g,1),a=d.length&&h.join("");if(!a)return w.apply(c,x.call(d,0)),c;break}}}return i(a,m)(d,b,f,c,R.test(a)),c}function bq(){}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=String,r=a.document,s=r.documentElement,t=0,u=0,v=[].pop,w=[].push,x=[].slice,y=[].indexOf||function(a){var b=0,c=this.length;for(;be.cacheLength&&delete a[b.shift()],a[c]=d},a)},B=A(),C=A(),D=A(),E="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",G=F.replace("w","w#"),H="([*^$|!~]?=)",I="\\["+E+"*("+F+")"+E+"*(?:"+H+E+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+G+")|)|)"+E+"*\\]",J=":("+F+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+I+")|[^:]|\\\\.)*|.*))\\)|)",K=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+E+"*((?:-\\d)?\\d*)"+E+"*\\)|)(?=[^-]|$)",L=new RegExp("^"+E+"+|((?:^|[^\\\\])(?:\\\\.)*)"+E+"+$","g"),M=new RegExp("^"+E+"*,"+E+"*"),N=new RegExp("^"+E+"*([\\x20\\t\\r\\n\\f>+~])"+E+"*"),O=new RegExp(J),P=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,Q=/^:not/,R=/[\x20\t\r\n\f]*[+~]/,S=/:not\($/,T=/h\d/i,U=/input|select|textarea|button/i,V=/\\(?!\\)/g,W={ID:new RegExp("^#("+F+")"),CLASS:new RegExp("^\\.("+F+")"),NAME:new RegExp("^\\[name=['\"]?("+F+")['\"]?\\]"),TAG:new RegExp("^("+F.replace("w","w*")+")"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+J),POS:new RegExp(K,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+E+"*(even|odd|(([+-]|)(\\d*)n|)"+E+"*(?:([+-]|)"+E+"*(\\d+)|))"+E+"*\\)|)","i"),needsContext:new RegExp("^"+E+"*[>+~]|"+K,"i")},X=function(a){var b=r.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},Y=X(function(a){return a.appendChild(r.createComment("")),!a.getElementsByTagName("*").length}),Z=X(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),$=X(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),_=X(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),ba=X(function(a){a.id=o+0,a.innerHTML="
",s.insertBefore(a,s.firstChild);var b=r.getElementsByName&&r.getElementsByName(o).length===2+r.getElementsByName(o+0).length;return d=!r.getElementById(o),s.removeChild(a),b});try{x.call(s.childNodes,0)[0].nodeType}catch(bb){x=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}bc.matches=function(a,b){return bc(a,null,null,b)},bc.matchesSelector=function(a,b){return bc(b,null,null,[a]).length>0},f=bc.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=f(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=f(b);return c},g=bc.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},h=bc.contains=s.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:s.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},bc.attr=function(a,b){var c,d=g(a);return d||(b=b.toLowerCase()),(c=e.attrHandle[b])?c(a):d||$?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},e=bc.selectors={cacheLength:50,createPseudo:z,match:W,attrHandle:Z?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:d?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:Y?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:ba&&function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:_&&function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(V,""),a[3]=(a[4]||a[5]||"").replace(V,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||bc.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&bc.error(a[0]),a},PSEUDO:function(a){var b,c;if(W.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(b=a[4])O.test(b)&&(c=bh(b,!0))&&(c=b.indexOf(")",b.length-c)-b.length)&&(b=b.slice(0,c),a[0]=a[0].slice(0,c)),a[2]=b;return a.slice(0,3)}},filter:{ID:d?function(a){return a=a.replace(V,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(V,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(V,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=B[o][a];return b||(b=B(a,new RegExp("(^|"+E+")"+a+"("+E+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return function(d,e){var f=bc.attr(d,a);return f==null?b==="!=":b?(f+="",b==="="?f===c:b==="!="?f!==c:b==="^="?c&&f.indexOf(c)===0:b==="*="?c&&f.indexOf(c)>-1:b==="$="?c&&f.substr(f.length-c.length)===c:b==="~="?(" "+f+" ").indexOf(c)>-1:b==="|="?f===c||f.substr(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d){return a==="nth"?function(a){var b,e,f=a.parentNode;if(c===1&&d===0)return!0;if(f){e=0;for(b=f.firstChild;b;b=b.nextSibling)if(b.nodeType===1){e++;if(a===b)break}}return e-=d,e===c||e%c===0&&e/c>=0}:function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b){var c,d=e.pseudos[a]||e.setFilters[a.toLowerCase()]||bc.error("unsupported pseudo: "+a);return d[o]?d(b):d.length>1?(c=[a,a,"",b],e.setFilters.hasOwnProperty(a.toLowerCase())?z(function(a,c){var e,f=d(a,b),g=f.length;while(g--)e=y.call(a,f[g]),a[e]=!(c[e]=f[g])}):function(a){return d(a,0,c)}):d}},pseudos:{not:z(function(a){var b=[],c=[],d=i(a.replace(L,"$1"));return d[o]?z(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)if(f=g[h])a[h]=!(b[h]=f)}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:z(function(a){return function(b){return bc(a,b).length>0}}),contains:z(function(a){return function(b){return(b.textContent||b.innerText||f(b)).indexOf(a)>-1}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!e.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},header:function(a){return T.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:bd("radio"),checkbox:bd("checkbox"),file:bd("file"),password:bd("password"),image:bd("image"),submit:be("submit"),reset:be("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return U.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement},first:bf(function(a,b,c){return[0]}),last:bf(function(a,b,c){return[b-1]}),eq:bf(function(a,b,c){return[c<0?c+b:c]}),even:bf(function(a,b,c){for(var d=0;d=0;)a.push(d);return a}),gt:bf(function(a,b,c){for(var d=c<0?c+b:c;++d",a.querySelectorAll("[selected]").length||e.push("\\["+E+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),X(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+E+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=new RegExp(e.join("|")),bp=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a))){var i,j,k=!0,l=o,m=d,n=d.nodeType===9&&a;if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){i=bh(a),(k=d.getAttribute("id"))?l=k.replace(c,"\\$&"):d.setAttribute("id",l),l="[id='"+l+"'] ",j=i.length;while(j--)i[j]=l+i[j].join("");m=R.test(a)&&d.parentNode||d,n=i.join(",")}if(n)try{return w.apply(f,x.call(m.querySelectorAll(n),0)),f}catch(p){}finally{k||d.removeAttribute("id")}}return b(a,d,f,g,h)},h&&(X(function(b){a=h.call(b,"div");try{h.call(b,"[test!='']:sizzle"),f.push("!=",J)}catch(c){}}),f=new RegExp(f.join("|")),bc.matchesSelector=function(b,c){c=c.replace(d,"='$1']");if(!g(b)&&!f.test(c)&&(!e||!e.test(c)))try{var i=h.call(b,c);if(i||a||b.document&&b.document.nodeType!==11)return i}catch(j){}return bc(c,null,null,[b]).length>0})}(),e.pseudos.nth=e.pseudos.eq,e.filters=bq.prototype=e.pseudos,e.setFilters=new bq,bc.attr=p.attr,p.find=bc,p.expr=bc.selectors,p.expr[":"]=p.expr.pseudos,p.unique=bc.uniqueSort,p.text=bc.getText,p.isXMLDoc=bc.isXML,p.contains=bc.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{ck=f.href}catch(cy){ck=e.createElement("a"),ck.href="",ck=ck.href}cj=ct.exec(ck.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:ck,isLocal:cn.test(cj[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=(c||y)+"",k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,cj[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase())||!1,l.crossDomain=i&&i.join(":")+(i[3]?"":i[1]==="http:"?80:443)!==cj.join(":")+(cj[3]?"":cj[1]==="http:"?80:443)),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e=this.createTween(a,b),f=cQ.exec(b),g=e.cur(),h=+g||0,i=1,j=20;if(f){c=+f[2],d=f[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&h){h=p.css(e.elem,a,!0)||c||1;do i=i||".5",h=h/i,p.style(e.elem,a,h+d);while(i!==(i=e.cur()/g)&&i!==1&&--j)}e.unit=d,e.start=h,e.end=f[1]?h+(f[1]+1)*c:c}return e}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/static/jquery-mousewheel.js b/static/jquery-mousewheel.js deleted file mode 100755 index a579b50..0000000 --- a/static/jquery-mousewheel.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * credits for this plugin go to brandonaaron.net - * - * unfortunately his site is down - * - * @param {Object} up - * @param {Object} down - * @param {Object} preventDefault - */ -jQuery.fn.extend({ - mousewheel: function(up, down, preventDefault) { - return this.hover( - function() { - jQuery.event.mousewheel.giveFocus(this, up, down, preventDefault); - }, - function() { - jQuery.event.mousewheel.removeFocus(this); - } - ); - }, - mousewheeldown: function(fn, preventDefault) { - return this.mousewheel(function(){}, fn, preventDefault); - }, - mousewheelup: function(fn, preventDefault) { - return this.mousewheel(fn, function(){}, preventDefault); - }, - unmousewheel: function() { - return this.each(function() { - jQuery(this).unmouseover().unmouseout(); - jQuery.event.mousewheel.removeFocus(this); - }); - }, - unmousewheeldown: jQuery.fn.unmousewheel, - unmousewheelup: jQuery.fn.unmousewheel -}); - - -jQuery.event.mousewheel = { - giveFocus: function(el, up, down, preventDefault) { - if (el._handleMousewheel) jQuery(el).unmousewheel(); - - if (preventDefault == window.undefined && down && down.constructor != Function) { - preventDefault = down; - down = null; - } - - el._handleMousewheel = function(event) { - if (!event) event = window.event; - if (preventDefault) - if (event.preventDefault) event.preventDefault(); - else event.returnValue = false; - var delta = 0; - if (event.wheelDelta) { - delta = event.wheelDelta/120; - if (window.opera) delta = -delta; - } else if (event.detail) { - delta = -event.detail/3; - } - if (up && (delta > 0 || !down)) - up.apply(el, [event, delta]); - else if (down && delta < 0) - down.apply(el, [event, delta]); - }; - - if (window.addEventListener) - window.addEventListener('DOMMouseScroll', el._handleMousewheel, false); - window.onmousewheel = document.onmousewheel = el._handleMousewheel; - }, - - removeFocus: function(el) { - if (!el._handleMousewheel) return; - - if (window.removeEventListener) - window.removeEventListener('DOMMouseScroll', el._handleMousewheel, false); - window.onmousewheel = document.onmousewheel = null; - el._handleMousewheel = null; - } -}; \ No newline at end of file diff --git a/static/jquery-ui.min.css b/static/jquery-ui.min.css deleted file mode 100755 index 3b11633..0000000 --- a/static/jquery-ui.min.css +++ /dev/null @@ -1,7 +0,0 @@ -/*! jQuery UI - v1.12.1 - 2017-05-01 -* http://jqueryui.com -* Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, menu.css, slider.css, tabs.css, theme.css -* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666} \ No newline at end of file diff --git a/static/jquery-ui.min.js b/static/jquery-ui.min.js deleted file mode 100755 index 2389795..0000000 --- a/static/jquery-ui.min.js +++ /dev/null @@ -1,12 +0,0 @@ -/*! jQuery UI - v1.12.1 - 2017-05-01 -* http://jqueryui.com -* Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/droppable.js, widgets/resizable.js, widgets/selectable.js, widgets/sortable.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/menu.js, widgets/mouse.js, widgets/slider.js, widgets/tabs.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js -* Copyright jQuery Foundation and other contributors; Licensed MIT */ - -(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(t){for(var e=t.css("visibility");"inherit"===e;)t=t.parent(),e=t.css("visibility");return"hidden"!==e}function i(t){for(var e,i;t.length&&t[0]!==document;){if(e=t.css("position"),("absolute"===e||"relative"===e||"fixed"===e)&&(i=parseInt(t.css("zIndex"),10),!isNaN(i)&&0!==i))return i;t=t.parent()}return 0}function s(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.regional.en=t.extend(!0,{},this.regional[""]),this.regional["en-US"]=t.extend(!0,{},this.regional.en),this.dpDiv=n(t("
"))}function n(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.on("mouseout",i,function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",i,o)}function o(){t.datepicker._isDisabledDatepicker(d.inline?d.dpDiv.parent()[0]:d.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))}function a(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}t.ui=t.ui||{},t.ui.version="1.12.1";var r=0,l=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,n=l.call(arguments,1),o=0,a=n.length;a>o;o++)for(i in n[o])s=n[o][i],n[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(n){var o="string"==typeof n,a=l.call(arguments,1),r=this;return o?this.length||"instance"!==n?this.each(function(){var i,o=t.data(this,s);return"instance"===n?(r=o,!1):o?t.isFunction(o[n])&&"_"!==n.charAt(0)?(i=o[n].apply(o,a),i!==o&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+n+"'")}):r=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,s);e?(e.option(n||{}),e._init&&e._init()):t.data(this,s,new i(n,this))})),r}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,i){i=t(i||this.defaultElement||this)[0],this.element=t(i),this.uuid=r++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},i!==this&&(t.data(i,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===i&&this.destroy()}}),this.document=t(i.style?i.ownerDocument:i.document||i),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,l,h=i.nodeName.toLowerCase();return"area"===h?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(h)?(r=!i.disabled,r&&(l=t(i).closest("fieldset")[0],l&&(r=!l.disabled))):r="a"===h?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var h=!1;t(document).on("mouseup",function(){h=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!h){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,n="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!n&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),h=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,h=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.ui.safeBlur=function(e){e&&"body"!==e.nodeName.toLowerCase()&&t(e).trigger("blur")},t.widget("ui.draggable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"===this.options.helper&&this._setPositionRelative(),this.options.addClasses&&this._addClass("ui-draggable"),this._setHandleClassName(),this._mouseInit()},_setOption:function(t,e){this._super(t,e),"handle"===t&&(this._removeHandleClassName(),this._setHandleClassName())},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(this._blurActiveElement(e),this._blockFrames(i.iframeFix===!0?"iframe":i.iframeFix),!0):!1)},_blockFrames:function(e){this.iframeBlocks=this.document.find(e).map(function(){var e=t(this);return t("
").css("position","absolute").appendTo(e.parent()).outerWidth(e.outerWidth()).outerHeight(e.outerHeight()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_blurActiveElement:function(e){var i=t.ui.safeActiveElement(this.document[0]),s=t(e.target);s.closest(i).length||t.ui.safeBlur(i)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this._addClass(this.helper,"ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(!0),this.offsetParent=this.helper.offsetParent(),this.hasFixedAncestor=this.helper.parents().filter(function(){return"fixed"===t(this).css("position")}).length>0,this.positionAbs=this.element.offset(),this._refreshOffsets(e),this.originalPosition=this.position=this._generatePosition(e,!1),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_refreshOffsets:function(t){this.offset={top:this.positionAbs.top-this.margins.top,left:this.positionAbs.left-this.margins.left,scroll:!1,parent:this._getParentOffset(),relative:this._getRelativeOffset()},this.offset.click={left:t.pageX-this.offset.left,top:t.pageY-this.offset.top}},_mouseDrag:function(e,i){if(this.hasFixedAncestor&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e,!0),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp(new t.Event("mouseup",e)),!1;this.position=s.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1},_mouseUp:function(e){return this._unblockFrames(),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),this.handleElement.is(e.target)&&this.element.trigger("focus"),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp(new t.Event("mouseup",{target:this.element[0]})):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this.handleElement=this.options.handle?this.element.find(this.options.handle):this.element,this._addClass(this.handleElement,"ui-draggable-handle")},_removeHandleClassName:function(){this._removeClass(this.handleElement,"ui-draggable-handle")},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper),n=s?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return n.parents("body").length||n.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s&&n[0]===this.element[0]&&this._setPositionRelative(),n[0]===this.element[0]||/(fixed|absolute)/.test(n.css("position"))||n.css("position","absolute"),n},_setPositionRelative:function(){/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative")},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_isRootNode:function(t){return/(html|body)/i.test(t.tagName)||t===this.document[0]},_getParentOffset:function(){var e=this.offsetParent.offset(),i=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==i&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var t=this.element.position(),e=this._isRootNode(this.scrollParent[0]);return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+(e?0:this.scrollParent.scrollTop()),left:t.left-(parseInt(this.helper.css("left"),10)||0)+(e?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options,o=this.document[0];return this.relativeContainer=null,n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===n.containment?(this.containment=[0,0,t(o).width()-this.helperProportions.width-this.margins.left,(t(o).height()||o.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):n.containment.constructor===Array?(this.containment=n.containment,void 0):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e=/(scroll|auto)/.test(i.css("overflow")),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relativeContainer=i),void 0):(this.containment=null,void 0) -},_convertPositionTo:function(t,e){e||(e=this.position);var i="absolute"===t?1:-1,s=this._isRootNode(this.scrollParent[0]);return{top:e.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.offset.scroll.top:s?0:this.offset.scroll.top)*i,left:e.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.offset.scroll.left:s?0:this.offset.scroll.left)*i}},_generatePosition:function(t,e){var i,s,n,o,a=this.options,r=this._isRootNode(this.scrollParent[0]),l=t.pageX,h=t.pageY;return r&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),e&&(this.containment&&(this.relativeContainer?(s=this.relativeContainer.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,t.pageX-this.offset.click.lefti[2]&&(l=i[2]+this.offset.click.left),t.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),a.grid&&(n=a.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/a.grid[1])*a.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-a.grid[1]:n+a.grid[1]:n,o=a.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/a.grid[0])*a.grid[0]:this.originalPageX,l=i?o-this.offset.click.left>=i[0]||o-this.offset.click.left>i[2]?o:o-this.offset.click.left>=i[0]?o-a.grid[0]:o+a.grid[0]:o),"y"===a.axis&&(l=this.originalPageX),"x"===a.axis&&(h=this.originalPageY)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:r?0:this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:r?0:this.offset.scroll.left)}},_clear:function(){this._removeClass(this.helper,"ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s,this],!0),/^(drag|start|stop)/.test(e)&&(this.positionAbs=this._convertPositionTo("absolute"),s.offset=this.positionAbs),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i,s){var n=t.extend({},i,{item:s.element});s.sortables=[],t(s.options.connectToSortable).each(function(){var i=t(this).sortable("instance");i&&!i.options.disabled&&(s.sortables.push(i),i.refreshPositions(),i._trigger("activate",e,n))})},stop:function(e,i,s){var n=t.extend({},i,{item:s.element});s.cancelHelperRemoval=!1,t.each(s.sortables,function(){var t=this;t.isOver?(t.isOver=0,s.cancelHelperRemoval=!0,t.cancelHelperRemoval=!1,t._storedCSS={position:t.placeholder.css("position"),top:t.placeholder.css("top"),left:t.placeholder.css("left")},t._mouseStop(e),t.options.helper=t.options._helper):(t.cancelHelperRemoval=!0,t._trigger("deactivate",e,n))})},drag:function(e,i,s){t.each(s.sortables,function(){var n=!1,o=this;o.positionAbs=s.positionAbs,o.helperProportions=s.helperProportions,o.offset.click=s.offset.click,o._intersectsWith(o.containerCache)&&(n=!0,t.each(s.sortables,function(){return this.positionAbs=s.positionAbs,this.helperProportions=s.helperProportions,this.offset.click=s.offset.click,this!==o&&this._intersectsWith(this.containerCache)&&t.contains(o.element[0],this.element[0])&&(n=!1),n})),n?(o.isOver||(o.isOver=1,s._parent=i.helper.parent(),o.currentItem=i.helper.appendTo(o.element).data("ui-sortable-item",!0),o.options._helper=o.options.helper,o.options.helper=function(){return i.helper[0]},e.target=o.currentItem[0],o._mouseCapture(e,!0),o._mouseStart(e,!0,!0),o.offset.click.top=s.offset.click.top,o.offset.click.left=s.offset.click.left,o.offset.parent.left-=s.offset.parent.left-o.offset.parent.left,o.offset.parent.top-=s.offset.parent.top-o.offset.parent.top,s._trigger("toSortable",e),s.dropped=o.element,t.each(s.sortables,function(){this.refreshPositions()}),s.currentItem=s.element,o.fromOutside=s),o.currentItem&&(o._mouseDrag(e),i.position=o.position)):o.isOver&&(o.isOver=0,o.cancelHelperRemoval=!0,o.options._revert=o.options.revert,o.options.revert=!1,o._trigger("out",e,o._uiHash(o)),o._mouseStop(e,!0),o.options.revert=o.options._revert,o.options.helper=o.options._helper,o.placeholder&&o.placeholder.remove(),i.helper.appendTo(s._parent),s._refreshOffsets(e),i.position=s._generatePosition(e,!0),s._trigger("fromSortable",e),s.dropped=!1,t.each(s.sortables,function(){this.refreshPositions()}))})}}),t.ui.plugin.add("draggable","cursor",{start:function(e,i,s){var n=t("body"),o=s.options;n.css("cursor")&&(o._cursor=n.css("cursor")),n.css("cursor",o.cursor)},stop:function(e,i,s){var n=s.options;n._cursor&&t("body").css("cursor",n._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("opacity")&&(o._opacity=n.css("opacity")),n.css("opacity",o.opacity)},stop:function(e,i,s){var n=s.options;n._opacity&&t(i.helper).css("opacity",n._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(t,e,i){i.scrollParentNotHidden||(i.scrollParentNotHidden=i.helper.scrollParent(!1)),i.scrollParentNotHidden[0]!==i.document[0]&&"HTML"!==i.scrollParentNotHidden[0].tagName&&(i.overflowOffset=i.scrollParentNotHidden.offset())},drag:function(e,i,s){var n=s.options,o=!1,a=s.scrollParentNotHidden[0],r=s.document[0];a!==r&&"HTML"!==a.tagName?(n.axis&&"x"===n.axis||(s.overflowOffset.top+a.offsetHeight-e.pageY=0;d--)l=s.snapElements[d].left-s.margins.left,h=l+s.snapElements[d].width,c=s.snapElements[d].top-s.margins.top,u=c+s.snapElements[d].height,l-g>_||m>h+g||c-g>b||v>u+g||!t.contains(s.snapElements[d].item.ownerDocument,s.snapElements[d].item)?(s.snapElements[d].snapping&&s.options.snap.release&&s.options.snap.release.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=!1):("inner"!==f.snapMode&&(n=g>=Math.abs(c-b),o=g>=Math.abs(u-v),a=g>=Math.abs(l-_),r=g>=Math.abs(h-m),n&&(i.position.top=s._convertPositionTo("relative",{top:c-s.helperProportions.height,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l-s.helperProportions.width}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h}).left)),p=n||o||a||r,"outer"!==f.snapMode&&(n=g>=Math.abs(c-v),o=g>=Math.abs(u-b),a=g>=Math.abs(l-m),r=g>=Math.abs(h-_),n&&(i.position.top=s._convertPositionTo("relative",{top:c,left:0}).top),o&&(i.position.top=s._convertPositionTo("relative",{top:u-s.helperProportions.height,left:0}).top),a&&(i.position.left=s._convertPositionTo("relative",{top:0,left:l}).left),r&&(i.position.left=s._convertPositionTo("relative",{top:0,left:h-s.helperProportions.width}).left)),!s.snapElements[d].snapping&&(n||o||a||r||p)&&s.options.snap.snap&&s.options.snap.snap.call(s.element,e,t.extend(s._uiHash(),{snapItem:s.snapElements[d].item})),s.snapElements[d].snapping=n||o||a||r||p)}}),t.ui.plugin.add("draggable","stack",{start:function(e,i,s){var n,o=s.options,a=t.makeArray(t(o.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});a.length&&(n=parseInt(t(a[0]).css("zIndex"),10)||0,t(a).each(function(e){t(this).css("zIndex",n+e)}),this.css("zIndex",n+a.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i,s){var n=t(i.helper),o=s.options;n.css("zIndex")&&(o._zIndex=n.css("zIndex")),n.css("zIndex",o.zIndex)},stop:function(e,i,s){var n=s.options;n._zIndex&&t(i.helper).css("zIndex",n._zIndex)}}),t.ui.draggable,t.widget("ui.droppable",{version:"1.12.1",widgetEventPrefix:"drop",options:{accept:"*",addClasses:!0,greedy:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(s)?s:function(t){return t.is(s)},this.proportions=function(){return arguments.length?(e=arguments[0],void 0):e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(i.scope),i.addClasses&&this._addClass("ui-droppable")},_addToManager:function(e){t.ui.ddmanager.droppables[e]=t.ui.ddmanager.droppables[e]||[],t.ui.ddmanager.droppables[e].push(this)},_splice:function(t){for(var e=0;t.length>e;e++)t[e]===this&&t.splice(e,1)},_destroy:function(){var e=t.ui.ddmanager.droppables[this.options.scope];this._splice(e)},_setOption:function(e,i){if("accept"===e)this.accept=t.isFunction(i)?i:function(t){return t.is(i)};else if("scope"===e){var s=t.ui.ddmanager.droppables[this.options.scope];this._splice(s),this._addToManager(i)}this._super(e,i)},_activate:function(e){var i=t.ui.ddmanager.current;this._addActiveClass(),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this._removeActiveClass(),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._addHoverClass(),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this._removeHoverClass(),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var i=t(this).droppable("instance");return i.options.greedy&&!i.options.disabled&&i.options.scope===s.options.scope&&i.accept.call(i.element[0],s.currentItem||s.element)&&c(s,t.extend(i,{offset:i.element.offset()}),i.options.tolerance,e)?(n=!0,!1):void 0}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this._removeActiveClass(),this._removeHoverClass(),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}},_addHoverClass:function(){this._addClass("ui-droppable-hover")},_removeHoverClass:function(){this._removeClass("ui-droppable-hover")},_addActiveClass:function(){this._addClass("ui-droppable-active")},_removeActiveClass:function(){this._removeClass("ui-droppable-active")}});var c=t.ui.intersect=function(){function t(t,e,i){return t>=e&&e+i>t}return function(e,i,s,n){if(!i.offset)return!1;var o=(e.positionAbs||e.position.absolute).left+e.margins.left,a=(e.positionAbs||e.position.absolute).top+e.margins.top,r=o+e.helperProportions.width,l=a+e.helperProportions.height,h=i.offset.left,c=i.offset.top,u=h+i.proportions().width,d=c+i.proportions().height;switch(s){case"fit":return o>=h&&u>=r&&a>=c&&d>=l;case"intersect":return o+e.helperProportions.width/2>h&&u>r-e.helperProportions.width/2&&a+e.helperProportions.height/2>c&&d>l-e.helperProportions.height/2;case"pointer":return t(n.pageY,c,i.proportions().height)&&t(n.pageX,h,i.proportions().width);case"touch":return(a>=c&&d>=a||l>=c&&d>=l||c>a&&l>d)&&(o>=h&&u>=o||r>=h&&u>=r||h>o&&r>u);default:return!1}}}();t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,o=t.ui.ddmanager.droppables[e.options.scope]||[],a=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;o.length>s;s++)if(!(o[s].options.disabled||e&&!o[s].accept.call(o[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===o[s].element[0]){o[s].proportions().height=0;continue t}o[s].visible="none"!==o[s].element.css("display"),o[s].visible&&("mousedown"===a&&o[s]._activate.call(o[s],i),o[s].offset=o[s].element.offset(),o[s].proportions({width:o[s].element[0].offsetWidth,height:o[s].element[0].offsetHeight}))}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&c(e,this,this.options.tolerance,i)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").on("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,o,a=c(e,this,this.options.tolerance,i),r=!a&&this.isover?"isout":a&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,o=this.element.parents(":data(ui-droppable)").filter(function(){return t(this).droppable("instance").options.scope===n}),o.length&&(s=t(o[0]).droppable("instance"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").off("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}},t.uiBackCompat!==!1&&t.widget("ui.droppable",t.ui.droppable,{options:{hoverClass:!1,activeClass:!1},_addActiveClass:function(){this._super(),this.options.activeClass&&this.element.addClass(this.options.activeClass)},_removeActiveClass:function(){this._super(),this.options.activeClass&&this.element.removeClass(this.options.activeClass)},_addHoverClass:function(){this._super(),this.options.hoverClass&&this.element.addClass(this.options.hoverClass)},_removeHoverClass:function(){this._super(),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass)}}),t.ui.droppable,t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
"),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,l=this._change[o];return this._updatePrevProperties(),l?(i=l.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,l,h=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,l=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,h.animate||this.element.css(t.extend(a,{top:l,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!h.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,l=this.originalPosition.top+this.originalSize.height,h=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&h&&(t.left=r-e.minWidth),s&&h&&(t.left=r-e.maxWidth),a&&c&&(t.top=l-e.minHeight),n&&c&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
"),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,l={width:i.size.width-r,height:i.size.height-a},h=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(l,c&&h?{top:c,left:h}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,l=t(this).resizable("instance"),h=l.options,c=l.element,u=h.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(l.containerElement=t(d),/document/.test(u)||u===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=l._num(e.css("padding"+s))}),l.containerOffset=e.offset(),l.containerPosition=e.position(),l.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=l.containerOffset,n=l.containerSize.height,o=l.containerSize.width,a=l._hasScroll(d,"left")?d.scrollWidth:o,r=l._hasScroll(d)?d.scrollHeight:n,l.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,l=a.containerOffset,h=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=l),h.left<(a._helper?l.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-l.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?l.left:0),h.top<(a._helper?l.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-l.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?l.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-l.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-l.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),l=a.outerWidth()-e.sizeDiff.width,h=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:l,height:h})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0}; -t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,l="number"==typeof s.grid?[s.grid,s.grid]:s.grid,h=l[0]||1,c=l[1]||1,u=Math.round((n.width-o.width)/h)*h,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,g=s.maxWidth&&p>s.maxWidth,m=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=l,_&&(p+=h),v&&(f+=c),g&&(p-=h),m&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-h)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-h>0?(i.size.width=p,i.position.left=a.left-u):(p=h-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable,t.widget("ui.selectable",t.ui.mouse,{version:"1.12.1",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var e=this;this._addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){e.elementPos=t(e.element[0]).offset(),e.selectees=t(e.options.filter,e.element[0]),e._addClass(e.selectees,"ui-selectee"),e.selectees.each(function(){var i=t(this),s=i.offset(),n={left:s.left-e.elementPos.left,top:s.top-e.elementPos.top};t.data(this,"selectable-item",{element:this,$element:i,left:n.left,top:n.top,right:n.left+i.outerWidth(),bottom:n.top+i.outerHeight(),startselected:!1,selected:i.hasClass("ui-selected"),selecting:i.hasClass("ui-selecting"),unselecting:i.hasClass("ui-unselecting")})})},this.refresh(),this._mouseInit(),this.helper=t("
"),this._addClass(this.helper,"ui-selectable-helper")},_destroy:function(){this.selectees.removeData("selectable-item"),this._mouseDestroy()},_mouseStart:function(e){var i=this,s=this.options;this.opos=[e.pageX,e.pageY],this.elementPos=t(this.element[0]).offset(),this.options.disabled||(this.selectees=t(s.filter,this.element[0]),this._trigger("start",e),t(s.appendTo).append(this.helper),this.helper.css({left:e.pageX,top:e.pageY,width:0,height:0}),s.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var s=t.data(this,"selectable-item");s.startselected=!0,e.metaKey||e.ctrlKey||(i._removeClass(s.$element,"ui-selected"),s.selected=!1,i._addClass(s.$element,"ui-unselecting"),s.unselecting=!0,i._trigger("unselecting",e,{unselecting:s.element}))}),t(e.target).parents().addBack().each(function(){var s,n=t.data(this,"selectable-item");return n?(s=!e.metaKey&&!e.ctrlKey||!n.$element.hasClass("ui-selected"),i._removeClass(n.$element,s?"ui-unselecting":"ui-selected")._addClass(n.$element,s?"ui-selecting":"ui-unselecting"),n.unselecting=!s,n.selecting=s,n.selected=s,s?i._trigger("selecting",e,{selecting:n.element}):i._trigger("unselecting",e,{unselecting:n.element}),!1):void 0}))},_mouseDrag:function(e){if(this.dragged=!0,!this.options.disabled){var i,s=this,n=this.options,o=this.opos[0],a=this.opos[1],r=e.pageX,l=e.pageY;return o>r&&(i=r,r=o,o=i),a>l&&(i=l,l=a,a=i),this.helper.css({left:o,top:a,width:r-o,height:l-a}),this.selectees.each(function(){var i=t.data(this,"selectable-item"),h=!1,c={};i&&i.element!==s.element[0]&&(c.left=i.left+s.elementPos.left,c.right=i.right+s.elementPos.left,c.top=i.top+s.elementPos.top,c.bottom=i.bottom+s.elementPos.top,"touch"===n.tolerance?h=!(c.left>r||o>c.right||c.top>l||a>c.bottom):"fit"===n.tolerance&&(h=c.left>o&&r>c.right&&c.top>a&&l>c.bottom),h?(i.selected&&(s._removeClass(i.$element,"ui-selected"),i.selected=!1),i.unselecting&&(s._removeClass(i.$element,"ui-unselecting"),i.unselecting=!1),i.selecting||(s._addClass(i.$element,"ui-selecting"),i.selecting=!0,s._trigger("selecting",e,{selecting:i.element}))):(i.selecting&&((e.metaKey||e.ctrlKey)&&i.startselected?(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,s._addClass(i.$element,"ui-selected"),i.selected=!0):(s._removeClass(i.$element,"ui-selecting"),i.selecting=!1,i.startselected&&(s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0),s._trigger("unselecting",e,{unselecting:i.element}))),i.selected&&(e.metaKey||e.ctrlKey||i.startselected||(s._removeClass(i.$element,"ui-selected"),i.selected=!1,s._addClass(i.$element,"ui-unselecting"),i.unselecting=!0,s._trigger("unselecting",e,{unselecting:i.element})))))}),!1}},_mouseStop:function(e){var i=this;return this.dragged=!1,t(".ui-unselecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-unselecting"),s.unselecting=!1,s.startselected=!1,i._trigger("unselected",e,{unselected:s.element})}),t(".ui-selecting",this.element[0]).each(function(){var s=t.data(this,"selectable-item");i._removeClass(s.$element,"ui-selecting")._addClass(s.$element,"ui-selected"),s.selecting=!1,s.selected=!0,s.startselected=!0,i._trigger("selected",e,{selected:s.element})}),this._trigger("stop",e),this.helper.remove(),!1}}),t.widget("ui.sortable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(t,e,i){return t>=e&&e+i>t},_isFloating:function(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))},_create:function(){this.containerCache={},this._addClass("ui-sortable"),this.refresh(),this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(t,e){this._super(t,e),"handle"===t&&this._setHandleClassName()},_setHandleClassName:function(){var e=this;this._removeClass(this.element.find(".ui-sortable-handle"),"ui-sortable-handle"),t.each(this.items,function(){e._addClass(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item,"ui-sortable-handle")})},_destroy:function(){this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):void 0}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this._addClass(this.helper,"ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==this.document[0]&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===this.document[0].body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp(new t.Event("mouseup",{target:null})),"original"===this.options.helper?(this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,l=r+t.height,h=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+h>r&&l>s+h,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&l>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var e,i,s="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top,t.height),n="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left,t.width),o=s&&n;return o?(e=this._getDragVerticalDirection(),i=this._getDragHorizontalDirection(),this.floating?"right"===i||"down"===e?2:1:e&&("down"===e?2:1)):!1},_intersectsWithSides:function(t){var e=this._isOverAxis(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),i=this._isOverAxis(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),s=this._getDragVerticalDirection(),n=this._getDragHorizontalDirection();return this.floating&&n?"right"===n&&i||"left"===n&&!i:s&&("down"===s&&e||"up"===s&&!e)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],l=[],h=this._connectWith();if(h&&e)for(s=h.length-1;s>=0;s--)for(o=t(h[s],this.document[0]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&l.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(l.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=l.length-1;s>=0;s--)l[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,l,h,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i],this.document[0]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,h=r.length;h>s;s++)l=t(r[s]),l.data(this.widgetName+"-item",a),c.push({item:l,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.floating=this.items.length?"x"===this.options.axis||this._isFloating(this.items[0].item):!1,this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]);return e._addClass(n,"ui-sortable-placeholder",i||e.currentItem[0].className)._removeClass(n,"ui-sortable-helper"),"tbody"===s?e._createTrPlaceholder(e.currentItem.find("tr").eq(0),t("
",e.document[0]).appendTo(n)):"tr"===s?e._createTrPlaceholder(e.currentItem,n):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_createTrPlaceholder:function(e,i){var s=this;e.children().each(function(){t("",s.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(i)})},_contactContainers:function(e){var i,s,n,o,a,r,l,h,c,u,d=null,p=null;for(i=this.containers.length-1;i>=0;i--)if(!t.contains(this.currentItem[0],this.containers[i].element[0]))if(this._intersectsWith(this.containers[i].containerCache)){if(d&&t.contains(this.containers[i].element[0],d.element[0]))continue;d=this.containers[i],p=i}else this.containers[i].containerCache.over&&(this.containers[i]._trigger("out",e,this._uiHash(this)),this.containers[i].containerCache.over=0);if(d)if(1===this.containers.length)this.containers[p].containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1);else{for(n=1e4,o=null,c=d.floating||this._isFloating(this.currentItem),a=c?"left":"top",r=c?"width":"height",u=c?"pageX":"pageY",s=this.items.length-1;s>=0;s--)t.contains(this.containers[p].element[0],this.items[s].item[0])&&this.items[s].item[0]!==this.currentItem[0]&&(l=this.items[s].item.offset()[a],h=!1,e[u]-l>this.items[s][r]/2&&(h=!0),n>Math.abs(e[u]-l)&&(n=Math.abs(e[u]-l),o=this.items[s],this.direction=h?"up":"down"));if(!o&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[p])return this.currentContainer.containerCache.over||(this.containers[p]._trigger("over",e,this._uiHash()),this.currentContainer.containerCache.over=1),void 0;o?this._rearrange(e,o,null,!0):this._rearrange(e,null,this.containers[p].element,!0),this._trigger("change",e,this._uiHash()),this.containers[p]._trigger("change",e,this._uiHash(this)),this.currentContainer=this.containers[p],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[p]._trigger("over",e,this._uiHash(this)),this.containers[p].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===this.document[0].body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,"document"===n.containment?this.document.width():this.window.width()-this.helperProportions.width-this.margins.left,("document"===n.containment?this.document.height()||document.body.parentNode.scrollHeight:this.window.height()||this.document[0].body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==this.document[0]&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():l?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():l?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS),this._removeClass(this.currentItem,"ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.cancelHelperRemoval||(this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null),!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!this.cancelHelperRemoval},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}});var u=/ui-corner-([a-z]){2,6}/g;t.widget("ui.controlgroup",{version:"1.12.1",defaultElement:"
",options:{direction:"horizontal",disabled:null,onlyVisible:!0,items:{button:"input[type=button], input[type=submit], input[type=reset], button, a",controlgroupLabel:".ui-controlgroup-label",checkboxradio:"input[type='checkbox'], input[type='radio']",selectmenu:"select",spinner:".ui-spinner-input"}},_create:function(){this._enhance()},_enhance:function(){this.element.attr("role","toolbar"),this.refresh()},_destroy:function(){this._callChildMethod("destroy"),this.childWidgets.removeData("ui-controlgroup-data"),this.element.removeAttr("role"),this.options.items.controlgroupLabel&&this.element.find(this.options.items.controlgroupLabel).find(".ui-controlgroup-label-contents").contents().unwrap()},_initWidgets:function(){var e=this,i=[];t.each(this.options.items,function(s,n){var o,a={};return n?"controlgroupLabel"===s?(o=e.element.find(n),o.each(function(){var e=t(this);e.children(".ui-controlgroup-label-contents").length||e.contents().wrapAll("")}),e._addClass(o,null,"ui-widget ui-widget-content ui-state-default"),i=i.concat(o.get()),void 0):(t.fn[s]&&(a=e["_"+s+"Options"]?e["_"+s+"Options"]("middle"):{classes:{}},e.element.find(n).each(function(){var n=t(this),o=n[s]("instance"),r=t.widget.extend({},a); -if("button"!==s||!n.parent(".ui-spinner").length){o||(o=n[s]()[s]("instance")),o&&(r.classes=e._resolveClassesValues(r.classes,o)),n[s](r);var l=n[s]("widget");t.data(l[0],"ui-controlgroup-data",o?o:n[s]("instance")),i.push(l[0])}})),void 0):void 0}),this.childWidgets=t(t.unique(i)),this._addClass(this.childWidgets,"ui-controlgroup-item")},_callChildMethod:function(e){this.childWidgets.each(function(){var i=t(this),s=i.data("ui-controlgroup-data");s&&s[e]&&s[e]()})},_updateCornerClass:function(t,e){var i="ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all",s=this._buildSimpleOptions(e,"label").classes.label;this._removeClass(t,null,i),this._addClass(t,null,s)},_buildSimpleOptions:function(t,e){var i="vertical"===this.options.direction,s={classes:{}};return s.classes[e]={middle:"",first:"ui-corner-"+(i?"top":"left"),last:"ui-corner-"+(i?"bottom":"right"),only:"ui-corner-all"}[t],s},_spinnerOptions:function(t){var e=this._buildSimpleOptions(t,"ui-spinner");return e.classes["ui-spinner-up"]="",e.classes["ui-spinner-down"]="",e},_buttonOptions:function(t){return this._buildSimpleOptions(t,"ui-button")},_checkboxradioOptions:function(t){return this._buildSimpleOptions(t,"ui-checkboxradio-label")},_selectmenuOptions:function(t){var e="vertical"===this.options.direction;return{width:e?"auto":!1,classes:{middle:{"ui-selectmenu-button-open":"","ui-selectmenu-button-closed":""},first:{"ui-selectmenu-button-open":"ui-corner-"+(e?"top":"tl"),"ui-selectmenu-button-closed":"ui-corner-"+(e?"top":"left")},last:{"ui-selectmenu-button-open":e?"":"ui-corner-tr","ui-selectmenu-button-closed":"ui-corner-"+(e?"bottom":"right")},only:{"ui-selectmenu-button-open":"ui-corner-top","ui-selectmenu-button-closed":"ui-corner-all"}}[t]}},_resolveClassesValues:function(e,i){var s={};return t.each(e,function(n){var o=i.options.classes[n]||"";o=t.trim(o.replace(u,"")),s[n]=(o+" "+e[n]).replace(/\s+/g," ")}),s},_setOption:function(t,e){return"direction"===t&&this._removeClass("ui-controlgroup-"+this.options.direction),this._super(t,e),"disabled"===t?(this._callChildMethod(e?"disable":"enable"),void 0):(this.refresh(),void 0)},refresh:function(){var e,i=this;this._addClass("ui-controlgroup ui-controlgroup-"+this.options.direction),"horizontal"===this.options.direction&&this._addClass(null,"ui-helper-clearfix"),this._initWidgets(),e=this.childWidgets,this.options.onlyVisible&&(e=e.filter(":visible")),e.length&&(t.each(["first","last"],function(t,s){var n=e[s]().data("ui-controlgroup-data");if(n&&i["_"+n.widgetName+"Options"]){var o=i["_"+n.widgetName+"Options"](1===e.length?"only":s);o.classes=i._resolveClassesValues(o.classes,n),n.element[n.widgetName](o)}else i._updateCornerClass(e[s](),s)}),this._callChildMethod("refresh"))}}),t.widget("ui.checkboxradio",[t.ui.formResetMixin,{version:"1.12.1",options:{disabled:null,label:null,icon:!0,classes:{"ui-checkboxradio-label":"ui-corner-all","ui-checkboxradio-icon":"ui-corner-all"}},_getCreateOptions:function(){var e,i,s=this,n=this._super()||{};return this._readType(),i=this.element.labels(),this.label=t(i[i.length-1]),this.label.length||t.error("No label found for checkboxradio widget"),this.originalLabel="",this.label.contents().not(this.element[0]).each(function(){s.originalLabel+=3===this.nodeType?t(this).text():this.outerHTML}),this.originalLabel&&(n.label=this.originalLabel),e=this.element[0].disabled,null!=e&&(n.disabled=e),n},_create:function(){var t=this.element[0].checked;this._bindFormResetHandler(),null==this.options.disabled&&(this.options.disabled=this.element[0].disabled),this._setOption("disabled",this.options.disabled),this._addClass("ui-checkboxradio","ui-helper-hidden-accessible"),this._addClass(this.label,"ui-checkboxradio-label","ui-button ui-widget"),"radio"===this.type&&this._addClass(this.label,"ui-checkboxradio-radio-label"),this.options.label&&this.options.label!==this.originalLabel?this._updateLabel():this.originalLabel&&(this.options.label=this.originalLabel),this._enhance(),t&&(this._addClass(this.label,"ui-checkboxradio-checked","ui-state-active"),this.icon&&this._addClass(this.icon,null,"ui-state-hover")),this._on({change:"_toggleClasses",focus:function(){this._addClass(this.label,null,"ui-state-focus ui-visual-focus")},blur:function(){this._removeClass(this.label,null,"ui-state-focus ui-visual-focus")}})},_readType:function(){var e=this.element[0].nodeName.toLowerCase();this.type=this.element[0].type,"input"===e&&/radio|checkbox/.test(this.type)||t.error("Can't create checkboxradio on element.nodeName="+e+" and element.type="+this.type)},_enhance:function(){this._updateIcon(this.element[0].checked)},widget:function(){return this.label},_getRadioGroup:function(){var e,i=this.element[0].name,s="input[name='"+t.ui.escapeSelector(i)+"']";return i?(e=this.form.length?t(this.form[0].elements).filter(s):t(s).filter(function(){return 0===t(this).form().length}),e.not(this.element)):t([])},_toggleClasses:function(){var e=this.element[0].checked;this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",e),this.options.icon&&"checkbox"===this.type&&this._toggleClass(this.icon,null,"ui-icon-check ui-state-checked",e)._toggleClass(this.icon,null,"ui-icon-blank",!e),"radio"===this.type&&this._getRadioGroup().each(function(){var e=t(this).checkboxradio("instance");e&&e._removeClass(e.label,"ui-checkboxradio-checked","ui-state-active")})},_destroy:function(){this._unbindFormResetHandler(),this.icon&&(this.icon.remove(),this.iconSpace.remove())},_setOption:function(t,e){return"label"!==t||e?(this._super(t,e),"disabled"===t?(this._toggleClass(this.label,null,"ui-state-disabled",e),this.element[0].disabled=e,void 0):(this.refresh(),void 0)):void 0},_updateIcon:function(e){var i="ui-icon ui-icon-background ";this.options.icon?(this.icon||(this.icon=t(""),this.iconSpace=t(" "),this._addClass(this.iconSpace,"ui-checkboxradio-icon-space")),"checkbox"===this.type?(i+=e?"ui-icon-check ui-state-checked":"ui-icon-blank",this._removeClass(this.icon,null,e?"ui-icon-blank":"ui-icon-check")):i+="ui-icon-blank",this._addClass(this.icon,"ui-checkboxradio-icon",i),e||this._removeClass(this.icon,null,"ui-icon-check ui-state-checked"),this.icon.prependTo(this.label).after(this.iconSpace)):void 0!==this.icon&&(this.icon.remove(),this.iconSpace.remove(),delete this.icon)},_updateLabel:function(){var t=this.label.contents().not(this.element[0]);this.icon&&(t=t.not(this.icon[0])),this.iconSpace&&(t=t.not(this.iconSpace[0])),t.remove(),this.label.append(this.options.label)},refresh:function(){var t=this.element[0].checked,e=this.element[0].disabled;this._updateIcon(t),this._toggleClass(this.label,"ui-checkboxradio-checked","ui-state-active",t),null!==this.options.label&&this._updateLabel(),e!==this.options.disabled&&this._setOptions({disabled:e})}}]),t.ui.checkboxradio,t.widget("ui.button",{version:"1.12.1",defaultElement:"").addClass(this._triggerClass).html(o?t("").attr({src:o,alt:n,title:n}):n)),e[r?"before":"after"](i.trigger),i.trigger.on("click",function(){return t.datepicker._datepickerShowing&&t.datepicker._lastInput===e[0]?t.datepicker._hideDatepicker():t.datepicker._datepickerShowing&&t.datepicker._lastInput!==e[0]?(t.datepicker._hideDatepicker(),t.datepicker._showDatepicker(e[0])):t.datepicker._showDatepicker(e[0]),!1}))},_autoSize:function(t){if(this._get(t,"autoSize")&&!t.inline){var e,i,s,n,o=new Date(2009,11,20),a=this._get(t,"dateFormat");a.match(/[DM]/)&&(e=function(t){for(i=0,s=0,n=0;t.length>n;n++)t[n].length>i&&(i=t[n].length,s=n);return s},o.setMonth(e(this._get(t,a.match(/MM/)?"monthNames":"monthNamesShort"))),o.setDate(e(this._get(t,a.match(/DD/)?"dayNames":"dayNamesShort"))+20-o.getDay())),t.input.attr("size",this._formatDate(t,o).length)}},_inlineDatepicker:function(e,i){var s=t(e);s.hasClass(this.markerClassName)||(s.addClass(this.markerClassName).append(i.dpDiv),t.data(e,"datepicker",i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(e),i.dpDiv.css("display","block"))},_dialogDatepicker:function(e,i,s,n,o){var r,l,h,c,u,d=this._dialogInst;return d||(this.uuid+=1,r="dp"+this.uuid,this._dialogInput=t(""),this._dialogInput.on("keydown",this._doKeyDown),t("body").append(this._dialogInput),d=this._dialogInst=this._newInst(this._dialogInput,!1),d.settings={},t.data(this._dialogInput[0],"datepicker",d)),a(d.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(d,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(l=document.documentElement.clientWidth,h=document.documentElement.clientHeight,c=document.documentElement.scrollLeft||document.body.scrollLeft,u=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[l/2-100+c,h/2-150+u]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),d.settings.onSelect=s,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),t.blockUI&&t.blockUI(this.dpDiv),t.data(this._dialogInput[0],"datepicker",d),this},_destroyDatepicker:function(e){var i,s=t(e),n=t.data(e,"datepicker");s.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),t.removeData(e,"datepicker"),"input"===i?(n.append.remove(),n.trigger.remove(),s.removeClass(this.markerClassName).off("focus",this._showDatepicker).off("keydown",this._doKeyDown).off("keypress",this._doKeyPress).off("keyup",this._doKeyUp)):("div"===i||"span"===i)&&s.removeClass(this.markerClassName).empty(),d===n&&(d=null))},_enableDatepicker:function(e){var i,s,n=t(e),o=t.data(e,"datepicker");n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!1,o.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().removeClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}))},_disableDatepicker:function(e){var i,s,n=t(e),o=t.data(e,"datepicker");n.hasClass(this.markerClassName)&&(i=e.nodeName.toLowerCase(),"input"===i?(e.disabled=!0,o.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(s=n.children("."+this._inlineClass),s.children().addClass("ui-state-disabled"),s.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=t.map(this._disabledInputs,function(t){return t===e?null:t}),this._disabledInputs[this._disabledInputs.length]=e)},_isDisabledDatepicker:function(t){if(!t)return!1;for(var e=0;this._disabledInputs.length>e;e++)if(this._disabledInputs[e]===t)return!0;return!1},_getInst:function(e){try{return t.data(e,"datepicker")}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(e,i,s){var n,o,r,l,h=this._getInst(e);return 2===arguments.length&&"string"==typeof i?"defaults"===i?t.extend({},t.datepicker._defaults):h?"all"===i?t.extend({},h.settings):this._get(h,i):null:(n=i||{},"string"==typeof i&&(n={},n[i]=s),h&&(this._curInst===h&&this._hideDatepicker(),o=this._getDateDatepicker(e,!0),r=this._getMinMaxDate(h,"min"),l=this._getMinMaxDate(h,"max"),a(h.settings,n),null!==r&&void 0!==n.dateFormat&&void 0===n.minDate&&(h.settings.minDate=this._formatDate(h,r)),null!==l&&void 0!==n.dateFormat&&void 0===n.maxDate&&(h.settings.maxDate=this._formatDate(h,l)),"disabled"in n&&(n.disabled?this._disableDatepicker(e):this._enableDatepicker(e)),this._attachments(t(e),h),this._autoSize(h),this._setDate(h,o),this._updateAlternate(h),this._updateDatepicker(h)),void 0)},_changeDatepicker:function(t,e,i){this._optionDatepicker(t,e,i)},_refreshDatepicker:function(t){var e=this._getInst(t);e&&this._updateDatepicker(e)},_setDateDatepicker:function(t,e){var i=this._getInst(t);i&&(this._setDate(i,e),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(t,e){var i=this._getInst(t);return i&&!i.inline&&this._setDateFromField(i,e),i?this._getDate(i):null},_doKeyDown:function(e){var i,s,n,o=t.datepicker._getInst(e.target),a=!0,r=o.dpDiv.is(".ui-datepicker-rtl");if(o._keyEvent=!0,t.datepicker._datepickerShowing)switch(e.keyCode){case 9:t.datepicker._hideDatepicker(),a=!1;break;case 13:return n=t("td."+t.datepicker._dayOverClass+":not(."+t.datepicker._currentClass+")",o.dpDiv),n[0]&&t.datepicker._selectDay(e.target,o.selectedMonth,o.selectedYear,n[0]),i=t.datepicker._get(o,"onSelect"),i?(s=t.datepicker._formatDate(o),i.apply(o.input?o.input[0]:null,[s,o])):t.datepicker._hideDatepicker(),!1;case 27:t.datepicker._hideDatepicker();break;case 33:t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(o,"stepBigMonths"):-t.datepicker._get(o,"stepMonths"),"M");break;case 34:t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(o,"stepBigMonths"):+t.datepicker._get(o,"stepMonths"),"M");break;case 35:(e.ctrlKey||e.metaKey)&&t.datepicker._clearDate(e.target),a=e.ctrlKey||e.metaKey;break;case 36:(e.ctrlKey||e.metaKey)&&t.datepicker._gotoToday(e.target),a=e.ctrlKey||e.metaKey;break;case 37:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,r?1:-1,"D"),a=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?-t.datepicker._get(o,"stepBigMonths"):-t.datepicker._get(o,"stepMonths"),"M");break;case 38:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,-7,"D"),a=e.ctrlKey||e.metaKey;break;case 39:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,r?-1:1,"D"),a=e.ctrlKey||e.metaKey,e.originalEvent.altKey&&t.datepicker._adjustDate(e.target,e.ctrlKey?+t.datepicker._get(o,"stepBigMonths"):+t.datepicker._get(o,"stepMonths"),"M");break;case 40:(e.ctrlKey||e.metaKey)&&t.datepicker._adjustDate(e.target,7,"D"),a=e.ctrlKey||e.metaKey;break;default:a=!1}else 36===e.keyCode&&e.ctrlKey?t.datepicker._showDatepicker(this):a=!1;a&&(e.preventDefault(),e.stopPropagation())},_doKeyPress:function(e){var i,s,n=t.datepicker._getInst(e.target);return t.datepicker._get(n,"constrainInput")?(i=t.datepicker._possibleChars(t.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),e.ctrlKey||e.metaKey||" ">s||!i||i.indexOf(s)>-1):void 0},_doKeyUp:function(e){var i,s=t.datepicker._getInst(e.target);if(s.input.val()!==s.lastVal)try{i=t.datepicker.parseDate(t.datepicker._get(s,"dateFormat"),s.input?s.input.val():null,t.datepicker._getFormatConfig(s)),i&&(t.datepicker._setDateFromField(s),t.datepicker._updateAlternate(s),t.datepicker._updateDatepicker(s))}catch(n){}return!0},_showDatepicker:function(e){if(e=e.target||e,"input"!==e.nodeName.toLowerCase()&&(e=t("input",e.parentNode)[0]),!t.datepicker._isDisabledDatepicker(e)&&t.datepicker._lastInput!==e){var s,n,o,r,l,h,c;s=t.datepicker._getInst(e),t.datepicker._curInst&&t.datepicker._curInst!==s&&(t.datepicker._curInst.dpDiv.stop(!0,!0),s&&t.datepicker._datepickerShowing&&t.datepicker._hideDatepicker(t.datepicker._curInst.input[0])),n=t.datepicker._get(s,"beforeShow"),o=n?n.apply(e,[e,s]):{},o!==!1&&(a(s.settings,o),s.lastVal=null,t.datepicker._lastInput=e,t.datepicker._setDateFromField(s),t.datepicker._inDialog&&(e.value=""),t.datepicker._pos||(t.datepicker._pos=t.datepicker._findPos(e),t.datepicker._pos[1]+=e.offsetHeight),r=!1,t(e).parents().each(function(){return r|="fixed"===t(this).css("position"),!r}),l={left:t.datepicker._pos[0],top:t.datepicker._pos[1]},t.datepicker._pos=null,s.dpDiv.empty(),s.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),t.datepicker._updateDatepicker(s),l=t.datepicker._checkOffset(s,l,r),s.dpDiv.css({position:t.datepicker._inDialog&&t.blockUI?"static":r?"fixed":"absolute",display:"none",left:l.left+"px",top:l.top+"px"}),s.inline||(h=t.datepicker._get(s,"showAnim"),c=t.datepicker._get(s,"duration"),s.dpDiv.css("z-index",i(t(e))+1),t.datepicker._datepickerShowing=!0,t.effects&&t.effects.effect[h]?s.dpDiv.show(h,t.datepicker._get(s,"showOptions"),c):s.dpDiv[h||"show"](h?c:null),t.datepicker._shouldFocusInput(s)&&s.input.trigger("focus"),t.datepicker._curInst=s))}},_updateDatepicker:function(e){this.maxRows=4,d=e,e.dpDiv.empty().append(this._generateHTML(e)),this._attachHandlers(e);var i,s=this._getNumberOfMonths(e),n=s[1],a=17,r=e.dpDiv.find("."+this._dayOverClass+" a");r.length>0&&o.apply(r.get(0)),e.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),n>1&&e.dpDiv.addClass("ui-datepicker-multi-"+n).css("width",a*n+"em"),e.dpDiv[(1!==s[0]||1!==s[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),e.dpDiv[(this._get(e,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),e===t.datepicker._curInst&&t.datepicker._datepickerShowing&&t.datepicker._shouldFocusInput(e)&&e.input.trigger("focus"),e.yearshtml&&(i=e.yearshtml,setTimeout(function(){i===e.yearshtml&&e.yearshtml&&e.dpDiv.find("select.ui-datepicker-year:first").replaceWith(e.yearshtml),i=e.yearshtml=null},0))},_shouldFocusInput:function(t){return t.input&&t.input.is(":visible")&&!t.input.is(":disabled")&&!t.input.is(":focus")},_checkOffset:function(e,i,s){var n=e.dpDiv.outerWidth(),o=e.dpDiv.outerHeight(),a=e.input?e.input.outerWidth():0,r=e.input?e.input.outerHeight():0,l=document.documentElement.clientWidth+(s?0:t(document).scrollLeft()),h=document.documentElement.clientHeight+(s?0:t(document).scrollTop());return i.left-=this._get(e,"isRTL")?n-a:0,i.left-=s&&i.left===e.input.offset().left?t(document).scrollLeft():0,i.top-=s&&i.top===e.input.offset().top+r?t(document).scrollTop():0,i.left-=Math.min(i.left,i.left+n>l&&l>n?Math.abs(i.left+n-l):0),i.top-=Math.min(i.top,i.top+o>h&&h>o?Math.abs(o+r):0),i},_findPos:function(e){for(var i,s=this._getInst(e),n=this._get(s,"isRTL");e&&("hidden"===e.type||1!==e.nodeType||t.expr.filters.hidden(e));)e=e[n?"previousSibling":"nextSibling"];return i=t(e).offset(),[i.left,i.top]},_hideDatepicker:function(e){var i,s,n,o,a=this._curInst;!a||e&&a!==t.data(e,"datepicker")||this._datepickerShowing&&(i=this._get(a,"showAnim"),s=this._get(a,"duration"),n=function(){t.datepicker._tidyDialog(a)},t.effects&&(t.effects.effect[i]||t.effects[i])?a.dpDiv.hide(i,t.datepicker._get(a,"showOptions"),s,n):a.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?s:null,n),i||n(),this._datepickerShowing=!1,o=this._get(a,"onClose"),o&&o.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),t.blockUI&&(t.unblockUI(),t("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(t){t.dpDiv.removeClass(this._dialogClass).off(".ui-datepicker-calendar")},_checkExternalClick:function(e){if(t.datepicker._curInst){var i=t(e.target),s=t.datepicker._getInst(i[0]);(i[0].id!==t.datepicker._mainDivId&&0===i.parents("#"+t.datepicker._mainDivId).length&&!i.hasClass(t.datepicker.markerClassName)&&!i.closest("."+t.datepicker._triggerClass).length&&t.datepicker._datepickerShowing&&(!t.datepicker._inDialog||!t.blockUI)||i.hasClass(t.datepicker.markerClassName)&&t.datepicker._curInst!==s)&&t.datepicker._hideDatepicker()}},_adjustDate:function(e,i,s){var n=t(e),o=this._getInst(n[0]);this._isDisabledDatepicker(n[0])||(this._adjustInstDate(o,i+("M"===s?this._get(o,"showCurrentAtPos"):0),s),this._updateDatepicker(o))},_gotoToday:function(e){var i,s=t(e),n=this._getInst(s[0]);this._get(n,"gotoCurrent")&&n.currentDay?(n.selectedDay=n.currentDay,n.drawMonth=n.selectedMonth=n.currentMonth,n.drawYear=n.selectedYear=n.currentYear):(i=new Date,n.selectedDay=i.getDate(),n.drawMonth=n.selectedMonth=i.getMonth(),n.drawYear=n.selectedYear=i.getFullYear()),this._notifyChange(n),this._adjustDate(s)},_selectMonthYear:function(e,i,s){var n=t(e),o=this._getInst(n[0]);o["selected"+("M"===s?"Month":"Year")]=o["draw"+("M"===s?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(o),this._adjustDate(n)},_selectDay:function(e,i,s,n){var o,a=t(e);t(n).hasClass(this._unselectableClass)||this._isDisabledDatepicker(a[0])||(o=this._getInst(a[0]),o.selectedDay=o.currentDay=t("a",n).html(),o.selectedMonth=o.currentMonth=i,o.selectedYear=o.currentYear=s,this._selectDate(e,this._formatDate(o,o.currentDay,o.currentMonth,o.currentYear)))},_clearDate:function(e){var i=t(e);this._selectDate(i,"")},_selectDate:function(e,i){var s,n=t(e),o=this._getInst(n[0]);i=null!=i?i:this._formatDate(o),o.input&&o.input.val(i),this._updateAlternate(o),s=this._get(o,"onSelect"),s?s.apply(o.input?o.input[0]:null,[i,o]):o.input&&o.input.trigger("change"),o.inline?this._updateDatepicker(o):(this._hideDatepicker(),this._lastInput=o.input[0],"object"!=typeof o.input[0]&&o.input.trigger("focus"),this._lastInput=null)},_updateAlternate:function(e){var i,s,n,o=this._get(e,"altField");o&&(i=this._get(e,"altFormat")||this._get(e,"dateFormat"),s=this._getDate(e),n=this.formatDate(i,s,this._getFormatConfig(e)),t(o).val(n))},noWeekends:function(t){var e=t.getDay();return[e>0&&6>e,""]},iso8601Week:function(t){var e,i=new Date(t.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),e=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((e-i)/864e5)/7)+1},parseDate:function(e,i,s){if(null==e||null==i)throw"Invalid arguments";if(i="object"==typeof i?""+i:i+"",""===i)return null;var n,o,a,r,l=0,h=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,c="string"!=typeof h?h:(new Date).getFullYear()%100+parseInt(h,10),u=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,d=(s?s.dayNames:null)||this._defaults.dayNames,p=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,f=(s?s.monthNames:null)||this._defaults.monthNames,g=-1,m=-1,_=-1,v=-1,b=!1,y=function(t){var i=e.length>n+1&&e.charAt(n+1)===t;return i&&n++,i},w=function(t){var e=y(t),s="@"===t?14:"!"===t?20:"y"===t&&e?4:"o"===t?3:2,n="y"===t?s:1,o=RegExp("^\\d{"+n+","+s+"}"),a=i.substring(l).match(o);if(!a)throw"Missing number at position "+l;return l+=a[0].length,parseInt(a[0],10)},k=function(e,s,n){var o=-1,a=t.map(y(e)?n:s,function(t,e){return[[e,t]]}).sort(function(t,e){return-(t[1].length-e[1].length)});if(t.each(a,function(t,e){var s=e[1];return i.substr(l,s.length).toLowerCase()===s.toLowerCase()?(o=e[0],l+=s.length,!1):void 0}),-1!==o)return o+1;throw"Unknown name at position "+l},x=function(){if(i.charAt(l)!==e.charAt(n))throw"Unexpected literal at position "+l;l++};for(n=0;e.length>n;n++)if(b)"'"!==e.charAt(n)||y("'")?x():b=!1;else switch(e.charAt(n)){case"d":_=w("d");break;case"D":k("D",u,d);break;case"o":v=w("o");break;case"m":m=w("m");break;case"M":m=k("M",p,f);break;case"y":g=w("y");break;case"@":r=new Date(w("@")),g=r.getFullYear(),m=r.getMonth()+1,_=r.getDate();break;case"!":r=new Date((w("!")-this._ticksTo1970)/1e4),g=r.getFullYear(),m=r.getMonth()+1,_=r.getDate();break;case"'":y("'")?x():b=!0;break;default:x()}if(i.length>l&&(a=i.substr(l),!/^\s+/.test(a)))throw"Extra/unparsed characters found in date: "+a;if(-1===g?g=(new Date).getFullYear():100>g&&(g+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c>=g?0:-100)),v>-1)for(m=1,_=v;;){if(o=this._getDaysInMonth(g,m-1),o>=_)break;m++,_-=o}if(r=this._daylightSavingAdjust(new Date(g,m-1,_)),r.getFullYear()!==g||r.getMonth()+1!==m||r.getDate()!==_)throw"Invalid date";return r},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(t,e,i){if(!e)return"";var s,n=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,o=(i?i.dayNames:null)||this._defaults.dayNames,a=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,r=(i?i.monthNames:null)||this._defaults.monthNames,l=function(e){var i=t.length>s+1&&t.charAt(s+1)===e;return i&&s++,i},h=function(t,e,i){var s=""+e;if(l(t))for(;i>s.length;)s="0"+s;return s},c=function(t,e,i,s){return l(t)?s[e]:i[e]},u="",d=!1;if(e)for(s=0;t.length>s;s++)if(d)"'"!==t.charAt(s)||l("'")?u+=t.charAt(s):d=!1;else switch(t.charAt(s)){case"d":u+=h("d",e.getDate(),2);break;case"D":u+=c("D",e.getDay(),n,o);break;case"o":u+=h("o",Math.round((new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()-new Date(e.getFullYear(),0,0).getTime())/864e5),3);break;case"m":u+=h("m",e.getMonth()+1,2);break;case"M":u+=c("M",e.getMonth(),a,r);break;case"y":u+=l("y")?e.getFullYear():(10>e.getFullYear()%100?"0":"")+e.getFullYear()%100;break;case"@":u+=e.getTime();break;case"!":u+=1e4*e.getTime()+this._ticksTo1970;break;case"'":l("'")?u+="'":d=!0;break;default:u+=t.charAt(s)}return u},_possibleChars:function(t){var e,i="",s=!1,n=function(i){var s=t.length>e+1&&t.charAt(e+1)===i;return s&&e++,s -};for(e=0;t.length>e;e++)if(s)"'"!==t.charAt(e)||n("'")?i+=t.charAt(e):s=!1;else switch(t.charAt(e)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":n("'")?i+="'":s=!0;break;default:i+=t.charAt(e)}return i},_get:function(t,e){return void 0!==t.settings[e]?t.settings[e]:this._defaults[e]},_setDateFromField:function(t,e){if(t.input.val()!==t.lastVal){var i=this._get(t,"dateFormat"),s=t.lastVal=t.input?t.input.val():null,n=this._getDefaultDate(t),o=n,a=this._getFormatConfig(t);try{o=this.parseDate(i,s,a)||n}catch(r){s=e?"":s}t.selectedDay=o.getDate(),t.drawMonth=t.selectedMonth=o.getMonth(),t.drawYear=t.selectedYear=o.getFullYear(),t.currentDay=s?o.getDate():0,t.currentMonth=s?o.getMonth():0,t.currentYear=s?o.getFullYear():0,this._adjustInstDate(t)}},_getDefaultDate:function(t){return this._restrictMinMax(t,this._determineDate(t,this._get(t,"defaultDate"),new Date))},_determineDate:function(e,i,s){var n=function(t){var e=new Date;return e.setDate(e.getDate()+t),e},o=function(i){try{return t.datepicker.parseDate(t.datepicker._get(e,"dateFormat"),i,t.datepicker._getFormatConfig(e))}catch(s){}for(var n=(i.toLowerCase().match(/^c/)?t.datepicker._getDate(e):null)||new Date,o=n.getFullYear(),a=n.getMonth(),r=n.getDate(),l=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,h=l.exec(i);h;){switch(h[2]||"d"){case"d":case"D":r+=parseInt(h[1],10);break;case"w":case"W":r+=7*parseInt(h[1],10);break;case"m":case"M":a+=parseInt(h[1],10),r=Math.min(r,t.datepicker._getDaysInMonth(o,a));break;case"y":case"Y":o+=parseInt(h[1],10),r=Math.min(r,t.datepicker._getDaysInMonth(o,a))}h=l.exec(i)}return new Date(o,a,r)},a=null==i||""===i?s:"string"==typeof i?o(i):"number"==typeof i?isNaN(i)?s:n(i):new Date(i.getTime());return a=a&&"Invalid Date"==""+a?s:a,a&&(a.setHours(0),a.setMinutes(0),a.setSeconds(0),a.setMilliseconds(0)),this._daylightSavingAdjust(a)},_daylightSavingAdjust:function(t){return t?(t.setHours(t.getHours()>12?t.getHours()+2:0),t):null},_setDate:function(t,e,i){var s=!e,n=t.selectedMonth,o=t.selectedYear,a=this._restrictMinMax(t,this._determineDate(t,e,new Date));t.selectedDay=t.currentDay=a.getDate(),t.drawMonth=t.selectedMonth=t.currentMonth=a.getMonth(),t.drawYear=t.selectedYear=t.currentYear=a.getFullYear(),n===t.selectedMonth&&o===t.selectedYear||i||this._notifyChange(t),this._adjustInstDate(t),t.input&&t.input.val(s?"":this._formatDate(t))},_getDate:function(t){var e=!t.currentYear||t.input&&""===t.input.val()?null:this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return e},_attachHandlers:function(e){var i=this._get(e,"stepMonths"),s="#"+e.id.replace(/\\\\/g,"\\");e.dpDiv.find("[data-handler]").map(function(){var e={prev:function(){t.datepicker._adjustDate(s,-i,"M")},next:function(){t.datepicker._adjustDate(s,+i,"M")},hide:function(){t.datepicker._hideDatepicker()},today:function(){t.datepicker._gotoToday(s)},selectDay:function(){return t.datepicker._selectDay(s,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return t.datepicker._selectMonthYear(s,this,"M"),!1},selectYear:function(){return t.datepicker._selectMonthYear(s,this,"Y"),!1}};t(this).on(this.getAttribute("data-event"),e[this.getAttribute("data-handler")])})},_generateHTML:function(t){var e,i,s,n,o,a,r,l,h,c,u,d,p,f,g,m,_,v,b,y,w,k,x,C,D,T,I,M,P,S,N,H,z,A,O,E,W,F,L,R=new Date,Y=this._daylightSavingAdjust(new Date(R.getFullYear(),R.getMonth(),R.getDate())),B=this._get(t,"isRTL"),j=this._get(t,"showButtonPanel"),q=this._get(t,"hideIfNoPrevNext"),K=this._get(t,"navigationAsDateFormat"),U=this._getNumberOfMonths(t),V=this._get(t,"showCurrentAtPos"),X=this._get(t,"stepMonths"),$=1!==U[0]||1!==U[1],G=this._daylightSavingAdjust(t.currentDay?new Date(t.currentYear,t.currentMonth,t.currentDay):new Date(9999,9,9)),J=this._getMinMaxDate(t,"min"),Q=this._getMinMaxDate(t,"max"),Z=t.drawMonth-V,te=t.drawYear;if(0>Z&&(Z+=12,te--),Q)for(e=this._daylightSavingAdjust(new Date(Q.getFullYear(),Q.getMonth()-U[0]*U[1]+1,Q.getDate())),e=J&&J>e?J:e;this._daylightSavingAdjust(new Date(te,Z,1))>e;)Z--,0>Z&&(Z=11,te--);for(t.drawMonth=Z,t.drawYear=te,i=this._get(t,"prevText"),i=K?this.formatDate(i,this._daylightSavingAdjust(new Date(te,Z-X,1)),this._getFormatConfig(t)):i,s=this._canAdjustMonth(t,-1,te,Z)?""+i+"":q?"":""+i+"",n=this._get(t,"nextText"),n=K?this.formatDate(n,this._daylightSavingAdjust(new Date(te,Z+X,1)),this._getFormatConfig(t)):n,o=this._canAdjustMonth(t,1,te,Z)?""+n+"":q?"":""+n+"",a=this._get(t,"currentText"),r=this._get(t,"gotoCurrent")&&t.currentDay?G:Y,a=K?this.formatDate(a,r,this._getFormatConfig(t)):a,l=t.inline?"":"",h=j?"
"+(B?l:"")+(this._isInRange(t,r)?"":"")+(B?"":l)+"
":"",c=parseInt(this._get(t,"firstDay"),10),c=isNaN(c)?0:c,u=this._get(t,"showWeek"),d=this._get(t,"dayNames"),p=this._get(t,"dayNamesMin"),f=this._get(t,"monthNames"),g=this._get(t,"monthNamesShort"),m=this._get(t,"beforeShowDay"),_=this._get(t,"showOtherMonths"),v=this._get(t,"selectOtherMonths"),b=this._getDefaultDate(t),y="",k=0;U[0]>k;k++){for(x="",this.maxRows=4,C=0;U[1]>C;C++){if(D=this._daylightSavingAdjust(new Date(te,Z,t.selectedDay)),T=" ui-corner-all",I="",$){if(I+="
"}for(I+="
"+(/all|left/.test(T)&&0===k?B?o:s:"")+(/all|right/.test(T)&&0===k?B?s:o:"")+this._generateMonthYearHeader(t,Z,te,J,Q,k>0||C>0,f,g)+"
 
"+"",M=u?"":"",w=0;7>w;w++)P=(w+c)%7,M+="";for(I+=M+"",S=this._getDaysInMonth(te,Z),te===t.selectedYear&&Z===t.selectedMonth&&(t.selectedDay=Math.min(t.selectedDay,S)),N=(this._getFirstDayOfMonth(te,Z)-c+7)%7,H=Math.ceil((N+S)/7),z=$?this.maxRows>H?this.maxRows:H:H,this.maxRows=z,A=this._daylightSavingAdjust(new Date(te,Z,1-N)),O=0;z>O;O++){for(I+="",E=u?"":"",w=0;7>w;w++)W=m?m.apply(t.input?t.input[0]:null,[A]):[!0,""],F=A.getMonth()!==Z,L=F&&!v||!W[0]||J&&J>A||Q&&A>Q,E+="",A.setDate(A.getDate()+1),A=this._daylightSavingAdjust(A);I+=E+""}Z++,Z>11&&(Z=0,te++),I+="
"+this._get(t,"weekHeader")+"=5?" class='ui-datepicker-week-end'":"")+">"+""+p[P]+"
"+this._get(t,"calculateWeek")(A)+""+(F&&!_?" ":L?""+A.getDate()+"":""+A.getDate()+"")+"
"+($?""+(U[0]>0&&C===U[1]-1?"
":""):""),x+=I}y+=x}return y+=h,t._keyEvent=!1,y},_generateMonthYearHeader:function(t,e,i,s,n,o,a,r){var l,h,c,u,d,p,f,g,m=this._get(t,"changeMonth"),_=this._get(t,"changeYear"),v=this._get(t,"showMonthAfterYear"),b="
",y="";if(o||!m)y+=""+a[e]+"";else{for(l=s&&s.getFullYear()===i,h=n&&n.getFullYear()===i,y+=""}if(v||(b+=y+(!o&&m&&_?"":" ")),!t.yearshtml)if(t.yearshtml="",o||!_)b+=""+i+"";else{for(u=this._get(t,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(t){var e=t.match(/c[+\-].*/)?i+parseInt(t.substring(1),10):t.match(/[+\-].*/)?d+parseInt(t,10):parseInt(t,10);return isNaN(e)?d:e},f=p(u[0]),g=Math.max(f,p(u[1]||"")),f=s?Math.max(f,s.getFullYear()):f,g=n?Math.min(g,n.getFullYear()):g,t.yearshtml+="",b+=t.yearshtml,t.yearshtml=null}return b+=this._get(t,"yearSuffix"),v&&(b+=(!o&&m&&_?"":" ")+y),b+="
"},_adjustInstDate:function(t,e,i){var s=t.selectedYear+("Y"===i?e:0),n=t.selectedMonth+("M"===i?e:0),o=Math.min(t.selectedDay,this._getDaysInMonth(s,n))+("D"===i?e:0),a=this._restrictMinMax(t,this._daylightSavingAdjust(new Date(s,n,o)));t.selectedDay=a.getDate(),t.drawMonth=t.selectedMonth=a.getMonth(),t.drawYear=t.selectedYear=a.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(t)},_restrictMinMax:function(t,e){var i=this._getMinMaxDate(t,"min"),s=this._getMinMaxDate(t,"max"),n=i&&i>e?i:e;return s&&n>s?s:n},_notifyChange:function(t){var e=this._get(t,"onChangeMonthYear");e&&e.apply(t.input?t.input[0]:null,[t.selectedYear,t.selectedMonth+1,t])},_getNumberOfMonths:function(t){var e=this._get(t,"numberOfMonths");return null==e?[1,1]:"number"==typeof e?[1,e]:e},_getMinMaxDate:function(t,e){return this._determineDate(t,this._get(t,e+"Date"),null)},_getDaysInMonth:function(t,e){return 32-this._daylightSavingAdjust(new Date(t,e,32)).getDate()},_getFirstDayOfMonth:function(t,e){return new Date(t,e,1).getDay()},_canAdjustMonth:function(t,e,i,s){var n=this._getNumberOfMonths(t),o=this._daylightSavingAdjust(new Date(i,s+(0>e?e:n[0]*n[1]),1));return 0>e&&o.setDate(this._getDaysInMonth(o.getFullYear(),o.getMonth())),this._isInRange(t,o)},_isInRange:function(t,e){var i,s,n=this._getMinMaxDate(t,"min"),o=this._getMinMaxDate(t,"max"),a=null,r=null,l=this._get(t,"yearRange");return l&&(i=l.split(":"),s=(new Date).getFullYear(),a=parseInt(i[0],10),r=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(a+=s),i[1].match(/[+\-].*/)&&(r+=s)),(!n||e.getTime()>=n.getTime())&&(!o||e.getTime()<=o.getTime())&&(!a||e.getFullYear()>=a)&&(!r||r>=e.getFullYear())},_getFormatConfig:function(t){var e=this._get(t,"shortYearCutoff");return e="string"!=typeof e?e:(new Date).getFullYear()%100+parseInt(e,10),{shortYearCutoff:e,dayNamesShort:this._get(t,"dayNamesShort"),dayNames:this._get(t,"dayNames"),monthNamesShort:this._get(t,"monthNamesShort"),monthNames:this._get(t,"monthNames")}},_formatDate:function(t,e,i,s){e||(t.currentDay=t.selectedDay,t.currentMonth=t.selectedMonth,t.currentYear=t.selectedYear);var n=e?"object"==typeof e?e:this._daylightSavingAdjust(new Date(s,i,e)):this._daylightSavingAdjust(new Date(t.currentYear,t.currentMonth,t.currentDay));return this.formatDate(this._get(t,"dateFormat"),n,this._getFormatConfig(t))}}),t.fn.datepicker=function(e){if(!this.length)return this;t.datepicker.initialized||(t(document).on("mousedown",t.datepicker._checkExternalClick),t.datepicker.initialized=!0),0===t("#"+t.datepicker._mainDivId).length&&t("body").append(t.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof e||"isDisabled"!==e&&"getDate"!==e&&"widget"!==e?"option"===e&&2===arguments.length&&"string"==typeof arguments[1]?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof e?t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this].concat(i)):t.datepicker._attachDatepicker(this,e)}):t.datepicker["_"+e+"Datepicker"].apply(t.datepicker,[this[0]].concat(i))},t.datepicker=new s,t.datepicker.initialized=!1,t.datepicker.uuid=(new Date).getTime(),t.datepicker.version="1.12.1",t.datepicker,t.widget("ui.dialog",{version:"1.12.1",options:{appendTo:"body",autoOpen:!0,buttons:[],classes:{"ui-dialog":"ui-corner-all","ui-dialog-titlebar":"ui-corner-all"},closeOnEscape:!0,closeText:"Close",draggable:!0,hide:null,height:"auto",maxHeight:null,maxWidth:null,minHeight:150,minWidth:150,modal:!1,position:{my:"center",at:"center",of:window,collision:"fit",using:function(e){var i=t(this).css(e).offset().top;0>i&&t(this).css("top",e.top-i)}},resizable:!0,show:null,title:null,width:300,beforeClose:null,close:null,drag:null,dragStart:null,dragStop:null,focus:null,open:null,resize:null,resizeStart:null,resizeStop:null},sizeRelatedOptions:{buttons:!0,height:!0,maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0,width:!0},resizableRelatedOptions:{maxHeight:!0,maxWidth:!0,minHeight:!0,minWidth:!0},_create:function(){this.originalCss={display:this.element[0].style.display,width:this.element[0].style.width,minHeight:this.element[0].style.minHeight,maxHeight:this.element[0].style.maxHeight,height:this.element[0].style.height},this.originalPosition={parent:this.element.parent(),index:this.element.parent().children().index(this.element)},this.originalTitle=this.element.attr("title"),null==this.options.title&&null!=this.originalTitle&&(this.options.title=this.originalTitle),this.options.disabled&&(this.options.disabled=!1),this._createWrapper(),this.element.show().removeAttr("title").appendTo(this.uiDialog),this._addClass("ui-dialog-content","ui-widget-content"),this._createTitlebar(),this._createButtonPane(),this.options.draggable&&t.fn.draggable&&this._makeDraggable(),this.options.resizable&&t.fn.resizable&&this._makeResizable(),this._isOpen=!1,this._trackFocus()},_init:function(){this.options.autoOpen&&this.open()},_appendTo:function(){var e=this.options.appendTo;return e&&(e.jquery||e.nodeType)?t(e):this.document.find(e||"body").eq(0)},_destroy:function(){var t,e=this.originalPosition;this._untrackInstance(),this._destroyOverlay(),this.element.removeUniqueId().css(this.originalCss).detach(),this.uiDialog.remove(),this.originalTitle&&this.element.attr("title",this.originalTitle),t=e.parent.children().eq(e.index),t.length&&t[0]!==this.element[0]?t.before(this.element):e.parent.append(this.element)},widget:function(){return this.uiDialog},disable:t.noop,enable:t.noop,close:function(e){var i=this;this._isOpen&&this._trigger("beforeClose",e)!==!1&&(this._isOpen=!1,this._focusedElement=null,this._destroyOverlay(),this._untrackInstance(),this.opener.filter(":focusable").trigger("focus").length||t.ui.safeBlur(t.ui.safeActiveElement(this.document[0])),this._hide(this.uiDialog,this.options.hide,function(){i._trigger("close",e)}))},isOpen:function(){return this._isOpen},moveToTop:function(){this._moveToTop()},_moveToTop:function(e,i){var s=!1,n=this.uiDialog.siblings(".ui-front:visible").map(function(){return+t(this).css("z-index")}).get(),o=Math.max.apply(null,n);return o>=+this.uiDialog.css("z-index")&&(this.uiDialog.css("z-index",o+1),s=!0),s&&!i&&this._trigger("focus",e),s},open:function(){var e=this;return this._isOpen?(this._moveToTop()&&this._focusTabbable(),void 0):(this._isOpen=!0,this.opener=t(t.ui.safeActiveElement(this.document[0])),this._size(),this._position(),this._createOverlay(),this._moveToTop(null,!0),this.overlay&&this.overlay.css("z-index",this.uiDialog.css("z-index")-1),this._show(this.uiDialog,this.options.show,function(){e._focusTabbable(),e._trigger("focus")}),this._makeFocusTarget(),this._trigger("open"),void 0)},_focusTabbable:function(){var t=this._focusedElement;t||(t=this.element.find("[autofocus]")),t.length||(t=this.element.find(":tabbable")),t.length||(t=this.uiDialogButtonPane.find(":tabbable")),t.length||(t=this.uiDialogTitlebarClose.filter(":tabbable")),t.length||(t=this.uiDialog),t.eq(0).trigger("focus")},_keepFocus:function(e){function i(){var e=t.ui.safeActiveElement(this.document[0]),i=this.uiDialog[0]===e||t.contains(this.uiDialog[0],e);i||this._focusTabbable()}e.preventDefault(),i.call(this),this._delay(i)},_createWrapper:function(){this.uiDialog=t("
").hide().attr({tabIndex:-1,role:"dialog"}).appendTo(this._appendTo()),this._addClass(this.uiDialog,"ui-dialog","ui-widget ui-widget-content ui-front"),this._on(this.uiDialog,{keydown:function(e){if(this.options.closeOnEscape&&!e.isDefaultPrevented()&&e.keyCode&&e.keyCode===t.ui.keyCode.ESCAPE)return e.preventDefault(),this.close(e),void 0;if(e.keyCode===t.ui.keyCode.TAB&&!e.isDefaultPrevented()){var i=this.uiDialog.find(":tabbable"),s=i.filter(":first"),n=i.filter(":last");e.target!==n[0]&&e.target!==this.uiDialog[0]||e.shiftKey?e.target!==s[0]&&e.target!==this.uiDialog[0]||!e.shiftKey||(this._delay(function(){n.trigger("focus")}),e.preventDefault()):(this._delay(function(){s.trigger("focus")}),e.preventDefault())}},mousedown:function(t){this._moveToTop(t)&&this._focusTabbable()}}),this.element.find("[aria-describedby]").length||this.uiDialog.attr({"aria-describedby":this.element.uniqueId().attr("id")})},_createTitlebar:function(){var e;this.uiDialogTitlebar=t("
"),this._addClass(this.uiDialogTitlebar,"ui-dialog-titlebar","ui-widget-header ui-helper-clearfix"),this._on(this.uiDialogTitlebar,{mousedown:function(e){t(e.target).closest(".ui-dialog-titlebar-close")||this.uiDialog.trigger("focus")}}),this.uiDialogTitlebarClose=t("").button({label:t("").text(this.options.closeText).html(),icon:"ui-icon-closethick",showLabel:!1}).appendTo(this.uiDialogTitlebar),this._addClass(this.uiDialogTitlebarClose,"ui-dialog-titlebar-close"),this._on(this.uiDialogTitlebarClose,{click:function(t){t.preventDefault(),this.close(t)}}),e=t("").uniqueId().prependTo(this.uiDialogTitlebar),this._addClass(e,"ui-dialog-title"),this._title(e),this.uiDialogTitlebar.prependTo(this.uiDialog),this.uiDialog.attr({"aria-labelledby":e.attr("id")})},_title:function(t){this.options.title?t.text(this.options.title):t.html(" ")},_createButtonPane:function(){this.uiDialogButtonPane=t("
"),this._addClass(this.uiDialogButtonPane,"ui-dialog-buttonpane","ui-widget-content ui-helper-clearfix"),this.uiButtonSet=t("
").appendTo(this.uiDialogButtonPane),this._addClass(this.uiButtonSet,"ui-dialog-buttonset"),this._createButtons()},_createButtons:function(){var e=this,i=this.options.buttons;return this.uiDialogButtonPane.remove(),this.uiButtonSet.empty(),t.isEmptyObject(i)||t.isArray(i)&&!i.length?(this._removeClass(this.uiDialog,"ui-dialog-buttons"),void 0):(t.each(i,function(i,s){var n,o;s=t.isFunction(s)?{click:s,text:i}:s,s=t.extend({type:"button"},s),n=s.click,o={icon:s.icon,iconPosition:s.iconPosition,showLabel:s.showLabel,icons:s.icons,text:s.text},delete s.click,delete s.icon,delete s.iconPosition,delete s.showLabel,delete s.icons,"boolean"==typeof s.text&&delete s.text,t("",s).button(o).appendTo(e.uiButtonSet).on("click",function(){n.apply(e.element[0],arguments)})}),this._addClass(this.uiDialog,"ui-dialog-buttons"),this.uiDialogButtonPane.appendTo(this.uiDialog),void 0)},_makeDraggable:function(){function e(t){return{position:t.position,offset:t.offset}}var i=this,s=this.options;this.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",handle:".ui-dialog-titlebar",containment:"document",start:function(s,n){i._addClass(t(this),"ui-dialog-dragging"),i._blockFrames(),i._trigger("dragStart",s,e(n))},drag:function(t,s){i._trigger("drag",t,e(s))},stop:function(n,o){var a=o.offset.left-i.document.scrollLeft(),r=o.offset.top-i.document.scrollTop();s.position={my:"left top",at:"left"+(a>=0?"+":"")+a+" "+"top"+(r>=0?"+":"")+r,of:i.window},i._removeClass(t(this),"ui-dialog-dragging"),i._unblockFrames(),i._trigger("dragStop",n,e(o))}})},_makeResizable:function(){function e(t){return{originalPosition:t.originalPosition,originalSize:t.originalSize,position:t.position,size:t.size}}var i=this,s=this.options,n=s.resizable,o=this.uiDialog.css("position"),a="string"==typeof n?n:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:this.element,maxWidth:s.maxWidth,maxHeight:s.maxHeight,minWidth:s.minWidth,minHeight:this._minHeight(),handles:a,start:function(s,n){i._addClass(t(this),"ui-dialog-resizing"),i._blockFrames(),i._trigger("resizeStart",s,e(n))},resize:function(t,s){i._trigger("resize",t,e(s))},stop:function(n,o){var a=i.uiDialog.offset(),r=a.left-i.document.scrollLeft(),l=a.top-i.document.scrollTop();s.height=i.uiDialog.height(),s.width=i.uiDialog.width(),s.position={my:"left top",at:"left"+(r>=0?"+":"")+r+" "+"top"+(l>=0?"+":"")+l,of:i.window},i._removeClass(t(this),"ui-dialog-resizing"),i._unblockFrames(),i._trigger("resizeStop",n,e(o))}}).css("position",o)},_trackFocus:function(){this._on(this.widget(),{focusin:function(e){this._makeFocusTarget(),this._focusedElement=t(e.target)}})},_makeFocusTarget:function(){this._untrackInstance(),this._trackingInstances().unshift(this)},_untrackInstance:function(){var e=this._trackingInstances(),i=t.inArray(this,e);-1!==i&&e.splice(i,1)},_trackingInstances:function(){var t=this.document.data("ui-dialog-instances");return t||(t=[],this.document.data("ui-dialog-instances",t)),t},_minHeight:function(){var t=this.options;return"auto"===t.height?t.minHeight:Math.min(t.minHeight,t.height)},_position:function(){var t=this.uiDialog.is(":visible");t||this.uiDialog.show(),this.uiDialog.position(this.options.position),t||this.uiDialog.hide()},_setOptions:function(e){var i=this,s=!1,n={};t.each(e,function(t,e){i._setOption(t,e),t in i.sizeRelatedOptions&&(s=!0),t in i.resizableRelatedOptions&&(n[t]=e)}),s&&(this._size(),this._position()),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option",n)},_setOption:function(e,i){var s,n,o=this.uiDialog;"disabled"!==e&&(this._super(e,i),"appendTo"===e&&this.uiDialog.appendTo(this._appendTo()),"buttons"===e&&this._createButtons(),"closeText"===e&&this.uiDialogTitlebarClose.button({label:t("").text(""+this.options.closeText).html()}),"draggable"===e&&(s=o.is(":data(ui-draggable)"),s&&!i&&o.draggable("destroy"),!s&&i&&this._makeDraggable()),"position"===e&&this._position(),"resizable"===e&&(n=o.is(":data(ui-resizable)"),n&&!i&&o.resizable("destroy"),n&&"string"==typeof i&&o.resizable("option","handles",i),n||i===!1||this._makeResizable()),"title"===e&&this._title(this.uiDialogTitlebar.find(".ui-dialog-title")))},_size:function(){var t,e,i,s=this.options;this.element.show().css({width:"auto",minHeight:0,maxHeight:"none",height:0}),s.minWidth>s.width&&(s.width=s.minWidth),t=this.uiDialog.css({height:"auto",width:s.width}).outerHeight(),e=Math.max(0,s.minHeight-t),i="number"==typeof s.maxHeight?Math.max(0,s.maxHeight-t):"none","auto"===s.height?this.element.css({minHeight:e,maxHeight:i,height:"auto"}):this.element.height(Math.max(0,s.height-t)),this.uiDialog.is(":data(ui-resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())},_blockFrames:function(){this.iframeBlocks=this.document.find("iframe").map(function(){var e=t(this);return t("
").css({position:"absolute",width:e.outerWidth(),height:e.outerHeight()}).appendTo(e.parent()).offset(e.offset())[0]})},_unblockFrames:function(){this.iframeBlocks&&(this.iframeBlocks.remove(),delete this.iframeBlocks)},_allowInteraction:function(e){return t(e.target).closest(".ui-dialog").length?!0:!!t(e.target).closest(".ui-datepicker").length},_createOverlay:function(){if(this.options.modal){var e=!0;this._delay(function(){e=!1}),this.document.data("ui-dialog-overlays")||this._on(this.document,{focusin:function(t){e||this._allowInteraction(t)||(t.preventDefault(),this._trackingInstances()[0]._focusTabbable())}}),this.overlay=t("
").appendTo(this._appendTo()),this._addClass(this.overlay,null,"ui-widget-overlay ui-front"),this._on(this.overlay,{mousedown:"_keepFocus"}),this.document.data("ui-dialog-overlays",(this.document.data("ui-dialog-overlays")||0)+1)}},_destroyOverlay:function(){if(this.options.modal&&this.overlay){var t=this.document.data("ui-dialog-overlays")-1;t?this.document.data("ui-dialog-overlays",t):(this._off(this.document,"focusin"),this.document.removeData("ui-dialog-overlays")),this.overlay.remove(),this.overlay=null}}}),t.uiBackCompat!==!1&&t.widget("ui.dialog",t.ui.dialog,{options:{dialogClass:""},_createWrapper:function(){this._super(),this.uiDialog.addClass(this.options.dialogClass)},_setOption:function(t,e){"dialogClass"===t&&this.uiDialog.removeClass(this.options.dialogClass).addClass(e),this._superApply(arguments)}}),t.ui.dialog,t.widget("ui.menu",{version:"1.12.1",defaultElement:"