Commit d711fc55 authored by Randall Leeds's avatar Randall Leeds

Merge pull request #1326 from hypothesis/simplified-search

Simplified search
parents 54af6f72 b323b4b0
No preview for this file type
......@@ -3,81 +3,83 @@
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="h" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#x22;" d="M933.79 349.75c-53.726 93.054-21.416 212.304 72.152 266.488l-100.626 174.292c-28.75-16.854-62.176-26.518-97.846-26.518-107.536 0-194.708 87.746-194.708 195.99h-201.258c0.266-33.41-8.074-67.282-25.958-98.252-53.724-93.056-173.156-124.702-266.862-70.758l-100.624-174.292c28.97-16.472 54.050-40.588 71.886-71.478 53.638-92.908 21.512-211.92-71.708-266.224l100.626-174.292c28.65 16.696 61.916 26.254 97.4 26.254 107.196 0 194.144-87.192 194.7-194.958h201.254c-0.086 33.074 8.272 66.57 25.966 97.218 53.636 92.906 172.776 124.594 266.414 71.012l100.626 174.29c-28.78 16.466-53.692 40.498-71.434 71.228zM512 240.668c-114.508 0-207.336 92.824-207.336 207.334 0 114.508 92.826 207.334 207.336 207.334 114.508 0 207.332-92.826 207.332-207.334-0.002-114.51-92.824-207.334-207.332-207.334z" />
<glyph unicode="&#x23;" d="M75 759q0 25.5 8.5 51.25t26 47.75 44 38.75 63.5 24.25l16.5-35q-39-12.5-62-37t-18-63.5q11.5-9 32-18.75t35.5-14.75q4-16.5 0-32t-13.25-27.25-21.5-19.25-22.25-8.5q-36 3-62.5 26.5t-26.5 67.5zM261.5 759q0 25.5 8.5 51.25t26 47.75 44 38.75 63.5 24.25l16.5-35q-39-12.5-62-37t-18-63.5q11.5-9 32-18.75t35.5-14.75q4-16.5 0-32t-13.25-27.25-21.5-19.25-22.25-8.5q-36 3-62.5 26.5t-26.5 67.5z" horiz-adv-x="484" />
<glyph unicode="&#x24;" d="M58.5 27.5q40 12.5 66 42t21 68.5q-11.5 9-32 18.25t-35.5 15.25q-5 16.5-0.5 32t13.75 27.25 21 19.25 22.75 7.5q34-3 61.75-26t27.75-68q0-24.5-10.25-51.25t-29.75-50.25-47.25-42-61.25-27.5zM245 27.5q40 12.5 66 42t21 68.5q-11.5 9-32 18.25t-35.5 15.25q-5 16.5-0.5 32t13.75 27.25 21 19.25 22.75 7.5q34-3 61.75-26t27.75-68q0-24.5-10.25-51.25t-29.75-50.25-47.25-42-61.25-27.5z" horiz-adv-x="484" />
<glyph unicode="&#x25;" d="M1013.888 423.478c-5.632-5.632-14.684-5.686-20.26-0.054-5.522 5.632-31.836 31.89-58.276 58.386l-24.906 25.014c-26.55 26.55-52.864 52.754-58.442 58.386-5.632 5.522-14.684 5.522-20.26 0-5.632-5.632-31.89-31.89-58.386-58.386l-209.098-209.004c-26.55-26.55-57.344-57.398-68.48-68.48-11.136-11.136-29.33-11.136-40.466 0-11.136 11.082-42.002 41.892-68.534 68.48l-25.014 24.906c-26.55 26.55-57.398 57.398-68.48 68.534-11.136 11.136-11.136 29.33 0 40.466 11.082 11.136 41.892 42.002 68.48 68.534l209.116 209.006c26.55 26.55 52.754 52.864 58.386 58.276 5.632 5.632 5.632 14.684 0 20.26-5.522 5.632-31.836 31.89-58.386 58.442l-25.014 25.014c-26.55 26.55-52.754 52.754-58.386 58.276-5.632 5.632-5.522 14.684 0 20.26 5.632 5.632 17.426 10.112 26.276 10.112 8.906 0 46.866-0.164 84.316-0.274l309.706-1.206c37.578-0.164 78.646-0.274 91.374-0.274 12.672 0 23.094-10.368 23.094-23.094 0-12.618 0.164-53.796 0.274-91.374l1.316-305.078c-0.002-37.412 0.162-76.398 0.162-86.564 0-10.112-4.534-22.986-10.112-28.508zM819.2 4.26v341.34l-136.54-136.54v-102.4c0-18.78-15.342-34.14-34.14-34.14h-477.86c-18.78 0-34.14 15.342-34.14 34.14v477.86c0 18.78 15.342 34.14 34.14 34.14h102.4l136.54 136.54h-341.34c-37.522 0-68.26-30.702-68.26-68.26v-682.68c0-37.522 30.738-68.26 68.26-68.26h682.66c37.578 0 68.26 30.738 68.26 68.26z" />
<glyph unicode="&#x26;" d="M912.606 493.26l-101.478 101.48c-24.934 24.934-74.036 45.26-109.262 45.26h-256c-35.226 0-64-28.826-64-64v-576c0-35.174 28.826-64 64-64h448c35.226 0 64 28.826 64 64v384c0 35.226-20.378 84.326-45.26 109.26zM904.874 378.52c-3.020-0.308-85.452-7.322-138.65-7.322-44.032 0-57.856 4.812-65.024 12.032-14.54 14.49-16.076 101.836-4.66 203.98 0.922 8.14 7.732 14.336 15.872 14.54h0.256c8.14 0 15.002-5.94 16.128-14.028 7.22-50.842 20.224-139.776 28.006-149.146 10.138-7.68 90.726-20.48 148.326-27.648 8.192-1.076 14.336-8.090 14.284-16.384-0.154-8.244-6.452-15.104-14.644-15.924zM317.866 256h-256c-35.174 0-64 28.826-64 64v576c0 35.226 28.826 64 64 64h256c35.226 0 84.326-20.378 109.26-45.26l101.48-101.48c24.934-24.882 45.26-74.034 45.26-109.26h-42.496c2.458 2.816 4.044 6.452 4.096 10.444 0.052 8.346-6.092 15.308-14.284 16.384-57.6 7.168-138.24 19.968-148.326 27.7-7.782 9.318-20.788 98.304-28.006 149.146-1.228 8.090-7.988 14.028-16.128 14.028h-0.256c-8.14-0.204-15.002-6.4-15.872-14.54-11.316-102.144-9.78-189.492 4.66-203.98 5.478-5.478 15.206-9.524 39.22-11.212-22.582-13.262-38.608-36.302-38.608-63.644v-372.326z" horiz-adv-x="954" />
<glyph unicode="&#x27;" d="M198.766 557.068c-15.042-16.578-13.672-45.346 2.906-60.402 16.578-15.042 45.346-13.644 60.402 2.934l184.68 184.68h49.248l85.020 85.020h-152.452c-11.070 0-22.044-4.524-29.904-12.302l-197.010-197.010c-1.010-0.928-2.006-1.924-2.934-2.934zM243.24 384.774c-85.020-85.020-106.080-150.334-106.718-197.010-0.636-46.676 21.394-77.672 29.904-86.182 8.51-8.51 39.534-30.526 86.182-29.904 46.676 0.636 111.962 21.684 197.010 106.718l119.006 119.006c-45.928 15.374-87.22 46.176-123.71 82.668-36.49 36.49-67.156 77.7-82.668 123.71zM436.734 578.268c-7.874-14.226-6.808-28.064-0.582-47.492 8.704-27.26 32.92-62.242 65.094-94.388 32.172-32.172 67.348-56.528 94.388-65.094 20.286-6.42 34.318-7.084 49.248 2.338l232.78 232.78c1.508 30.942 0.194 57.372-2.934 79.152-26.084 12.274-48.682 32.256-70.366 53.94-21.49 21.49-41.514 44.502-53.94 70.366-21.74 3.1-48.266 4.414-79.152 2.934zM793.792 853.24c-0.11-0.554-0.526-1.204-0.582-1.758-0.498-4.040-0.442-9.396 2.934-16.992 6.752-15.236 24.742-36.462 45.734-57.454 21.006-21.006 41.79-38.47 56.888-45.14 7.556-3.322 13.588-4.040 17.602-3.514l68 68c40.85 37.778 19.872 77.45-13.478 110.8-33.364 33.364-72.454 53.732-110.232 12.896l-66.85-66.85zM23.966 11.872c-10.71-14.598-8.482-37.168 4.884-49.388 13.368-12.218 36.048-12.44 49.636-0.442l82.086 82.086c-13.368 7.444-23.414 15.208-29.904 21.684-6.504 6.504-14.71 16.938-22.292 30.498l-82.086-82.086c-0.83-0.748-1.592-1.536-2.338-2.338z" />
<glyph unicode="&#x28;" d="M508.118 837.954c-96.356 114.5-169.38 122.046-316.118 122.046v-512c256.534 128 285.272-16.67 447.012-2.046 159.456 14.44 295.050 137.174 384.988 258.046-254.712-58.906-382.92-24.046-515.882 133.954zM0 960h128v-1024h-128z" />
<glyph unicode="&#x29;" d="M1013.648 162.676l-285.138 285.194 285.138 285.194c13.46 13.406 13.46 35.244 0 48.63l-167.974 168.030c-6.534 6.48-15.136 10.054-24.352 10.054s-17.818-3.574-24.296-10.054l-285.174-285.232-285.138 285.12c-6.534 6.48-15.136 10.054-24.352 10.054s-17.818-3.574-24.296-10.054l-167.956-167.918c-6.536-6.536-10.11-15.192-10.11-24.298 0-9.216 3.574-17.874 10.11-24.352l285.062-285.23-285.062-285.12c-6.536-6.534-10.11-15.192-10.11-24.352 0-9.104 3.574-17.818 10.11-24.296l167.972-167.918c6.536-6.534 15.138-10.11 24.298-10.11 9.198 0 17.742 3.576 24.334 10.11l285.138 285.194 285.23-285.194c6.478-6.534 15.080-10.11 24.184-10.11 9.216 0 17.874 3.574 24.352 10.11l168.030 167.918c6.534 6.48 10.11 15.248 10.11 24.296 0 9.216-3.574 17.874-10.11 24.296z" />
<glyph unicode="&#x2a;" d="M788.142 143.144h-599.588v599.588h239.836l119.918 179.876h-539.63v-959.34h959.34v359.752l-59.958-59.958zM248.514 263.062c0 0 59.958 119.918 239.836 179.876 179.876 59.958 179.876 0 179.876 0v-239.836l359.752 359.752-359.752 359.752v-179.876c-89.22-12.89-179.278-59.358-239.836-119.916-119.918-119.918-179.876-359.752-179.876-359.752z" />
<glyph unicode="&#x2b;" d="M1023.948 660.428c-14.49 188.876-133.274 286.412-237.466 297.114-16.23 1.588-31.846 2.458-46.438 2.458-114.584 0-173.056-52.736-228.044-119.092-54.988 66.356-113.562 119.092-228.096 119.092-14.54 0-30.208-0.87-46.438-2.508-104.192-10.702-222.926-108.238-237.466-298.344l0.052-60.826c15.36-206.13 179.864-421.836 502.988-659.404 2.662-1.894 5.836-2.918 8.96-2.918 3.124 0 6.298 1.024 8.908 2.918 323.176 237.568 487.68 453.274 503.092 660.634l-0.052 60.876zM896 599.654c-11.060-144.486-140.236-316.262-384-506.93-243.764 190.77-373.044 363.928-384.308 515.072l0.308 51.2c9.164 116.020 78.284 166.656 122.522 171.212 11.468 1.126 22.836 1.792 33.434 1.792 55.654 0 81.1-14.284 129.484-72.756l98.56-119.090 98.612 119.090c48.384 58.472 73.728 72.756 129.432 72.756 10.65 0 21.964-0.716 33.74-1.844 44.032-4.506 113.152-56.012 122.572-179.508l-0.356-50.994z" />
<glyph unicode="&#x2c;" d="M1024 599.552c-15.412-207.36-179.916-423.066-503.092-660.634-2.61-1.894-5.784-2.918-8.908-2.918-3.124 0-6.298 1.024-8.96 2.918-323.124 237.568-487.628 453.274-502.988 659.354l-0.052 60.876c14.54 190.208 133.274 287.692 237.466 298.342 16.282 1.588 31.948 2.508 46.54 2.508 114.432 0.002 173.006-52.734 227.994-119.090 54.988 66.356 113.46 119.092 228.044 119.092 14.644 0 30.26-0.922 46.54-2.508 104.192-10.7 222.976-108.236 237.466-297.114l-0.050-60.826z" />
<glyph unicode="&#x2d;" d="M128 896h64v-64h-64v64zM256 896h64v-64h-64v64zM384 896h512v-64h-512v64zM960 768v-656c0-56-56-112-112-112h-672c-56 0-112 56-112 112v656h896zM0 832v-768c0-64 64-128 128-128h768c64 0 128 64 128 128v768c0 64-64 128-128 128h-768c-64 0-128-64-128-128zM128 704v-512c0-64 64-128 128-128h512c64 0 128 64 128 128v512h-768z" />
<glyph unicode="&#x2e;" d="M128 896h64v-64h-64v64zM256 896h64v-64h-64v64zM384 896h512v-64h-512v64zM960 768v-656c0-56-56-112-112-112h-672c-56 0-112 56-112 112v656h896zM0 832v-768c0-64 64-128 128-128h768c64 0 128 64 128 128v768c0 64-64 128-128 128h-768c-64 0-128-64-128-128z" />
<glyph unicode="&#x2f;" d="M745.954 652.696l-471.042 0.046 235.552-409.614z" />
<glyph unicode="&#x33;" d="M448 576h384v-64h-384zM448 448h384v-64h-384zM448 320h384v-64h-384zM448 192h256v-64h-256zM960 768h-192v128c0 35.2-28.8 64-64 64h-640c-35.2 0-64-28.8-64-64v-704c0-35.2 28.8-64 64-64h192v-128c0-35.2 28.8-64 64-64h640c35.2 0 64 28.8 64 64v704c0 35.2-28.8 64-64 64zM256 704v-480h-160v640h576v-96h-352c-35.2 0-64-28.8-64-64zM928 32h-576v640h576v-640z" />
<glyph unicode="&#x35;" d="M192 576l-192-192 384-384 640 640-192 192-448-448z" />
<glyph unicode="&#x36;" d="M960 179.264l-268.992 268.736 268.992 268.736-179.264 179.264-268.736-268.864-268.864 268.864-179.136-179.264 268.736-268.736-268.736-268.736 179.136-179.264 268.864 268.864 268.736-268.864z" />
<glyph unicode="&#x37;" d="M512 832c211.744 0 384-172.256 384-384s-172.256-384-384-384-384 172.256-384 384 172.256 384 384 384zM512 960c-282.752 0-512-229.248-512-512s229.248-512 512-512 512 229.248 512 512-229.248 512-512 512v0zM685.504 365.504l-109.504 109.632v228.864h-128.736v-255.744c0-19.264 8.864-35.872 22.24-47.744l125.504-125.504 90.496 90.496z" />
<glyph unicode="&#x38;" d="M626.784 447.968l195.072-195.072c12.672-12.672 12.672-33.248 0-45.92l-68.832-68.832c-12.672-12.672-33.216-12.672-45.92 0l-195.104 195.072-195.104-195.072c-12.672-12.672-33.216-12.672-45.888 0l-68.864 68.832c-12.672 12.672-12.672 33.216 0 45.92l195.104 195.072-195.104 195.104c-12.672 12.672-12.672 33.248 0 45.92l68.896 68.832c12.672 12.672 33.216 12.672 45.888 0l195.072-195.104 195.104 195.104c12.672 12.672 33.216 12.672 45.92 0l68.832-68.864c12.672-12.672 12.672-33.216 0-45.92l-195.072-195.072z" />
<glyph unicode="&#x39;" d="M1095.72 760.93l-617.552-819.22c-6.182-6.182-14.198-9.354-22.272-9.354-8.132 0-16.266 3.172-22.39 9.354l-424.192 429.332c-12.446 12.486-12.446 32.768-0.118 45.312l203.146 205.726c5.948 6.066 13.962 9.374 22.45 9.374h0.65c8.604-0.178 16.738-3.82 22.508-10.3l203.028-223.232 556.72 558.376c5.948 4.942 13.076 7.364 20.204 7.364 8.252 0 16.444-3.19 22.568-9.492l37.18-140.84c3.308-17.27 10.378-40.014-2.008-52.382z" horiz-adv-x="1102" />
<glyph unicode="&#x3a;" d="M927.936 687.008l-68.288 68.288c-12.608 12.576-32.96 12.576-45.536 0l-409.44-409.44-194.752 196.16c-12.576 12.576-32.928 12.576-45.536 0l-68.288-68.288c-12.576-12.608-12.576-32.96 0-45.536l285.568-287.488c12.576-12.576 32.96-12.576 45.536 0l500.736 500.768c12.576 12.544 12.576 32.96 0 45.536z" />
<glyph unicode="&#x3c;" d="M830.778 628.138l-640.692 0.064 320.39-557.14z" />
<glyph unicode="&#x3d;" d="M863.68 364.32c68.992-51.424 128.128-98.496 128.128-98.496l-84.192-147.648-152.16 67.168-140.992-82.464c-9.536-86.944-18.272-166.88-18.272-166.88h-168.384l-18.688 167.392-138.944 82.656-153.824-67.904-84.16 147.648c0 0 58.144 46.848 126.976 98.112v168.288l-126.976 97.92 84.16 147.68 150.144-66.816 149.056 80.864 12.256 168.16h168.384l12.192-167.456 147.008-82.496 152.256 67.776 84.192-147.68-128.128-98.272v-167.552zM512 576c-70.688 0-128-57.312-128-128s57.312-128 128-128 128 57.312 128 128-57.312 128-128 128z" />
<glyph unicode="&#x3e;" d="M819.2 960h-614.4c-112.622 0-204.8-92.142-204.8-204.8v-614.4c0-112.622 92.178-204.8 204.8-204.8h614.4c112.658 0 204.8 92.178 204.8 204.8v614.4c0 112.658-92.178 204.8-204.8 204.8zM887.46 188.526c0-75.026-40.96-115.986-115.986-115.986h-518.894c-75.080 0-116.040 40.96-116.040 115.986v518.876c0 75.1 40.96 116.060 116.060 116.060h518.876c75.024-0.002 115.984-40.962 115.984-116.042v-518.876z" />
<glyph unicode="&#x3f;" d="M464 567.716l-42.55 46.756c-25.29 27.794-52.206 55.37-55.314 56.028-3.054 0.768-5.632 1.316-5.742 1.316-0.054 0-7.862-1.426-10.368-3.054-2.614-1.7-26.222-25.014-52.644-51.658l-38.456-39.13c-26.442-26.698-54.346-61.714-52.096-67.658 2.34-6.016 25.728-32.768 52.096-59.466l184.868-186.95c26.368-26.77 52.206-51.402 57.454-54.84 0 0 0 0 5.412 0 5.358 0 5.358 0 5.358 0 5.248 3.438 27.904 30.738 50.578 60.782l326.528 433.244c22.602 29.988 42.934 59.374 45.22 65.262 2.23 5.962-9.29 41.070-15.726 68.15-6.492 26.972-16 51.876-21.248 55.37 0 0 0 0-5.468 0-4.644 0-4.644 0-4.644 0-4.81-2.67-30.372-26.642-56.906-53.194l-272.198-273.042c-26.496-26.55-68.81-25.618-94.154 2.176zM989.86 485.138l-102.4-102.454v-200.924c0-75.1-34.12-109.22-109.22-109.22h-525.66c-75.080 0-116.040 40.96-116.040 115.986v518.876c0 75.1 40.96 116.060 116.060 116.060h194.102l102.4 102.454c12.508 12.398 22.82 24.302 30.154 34.084h-374.456c-112.622 0-204.8-92.142-204.8-204.8v-614.4c0-112.622 92.178-204.8 204.8-204.8h614.4c112.658 0 204.8 92.178 204.8 204.8v374.492c-9.782-7.424-21.742-17.756-34.14-30.154z" />
<glyph unicode="&#xe000;" d="M753.464 954.214c-130.758 0-237.126-106.368-237.126-237.126v-169.376h-406.5c-37.262 0-67.75-30.488-67.75-67.75v-474.25c0-37.262 30.488-67.75 67.75-67.75h609.752c37.262 0 67.75 30.488 67.75 67.75v474.25c0 37.262-30.488 67.75-67.75 67.75h-67.75v169.376c0 56.030 45.596 101.626 101.626 101.626s101.626-45.596 101.626-101.626v-237.126h135.5v237.126c0 130.758-106.368 237.126-237.126 237.126z" />
<glyph unicode="&#xe001;" d="M851.454 546.712h-67.978v135.954c0 149.958-121.952 271.91-271.91 271.91s-271.91-121.952-271.91-271.91v-135.954h-67.978c-37.388 0-67.978-30.59-67.978-67.978v-475.842c0-37.388 30.59-67.978 67.978-67.978h679.774c37.388 0 67.978 30.59 67.978 67.978v475.842c0 37.388-30.59 67.978-67.978 67.978zM375.612 682.666c0 74.98 60.976 135.954 135.954 135.954 74.98 0 135.954-60.976 135.954-135.954v-135.954h-271.91v135.954z" />
<glyph unicode="&#xe002;" d="M633.066-14.824h-512.638c-62.826 0-113.92 51.092-113.92 113.92v512.638c0 62.826 51.092 113.92 113.92 113.92h512.638c62.826 0 113.92-51.092 113.92-113.92v-512.638c0-62.826-51.092-113.92-113.92-113.92zM120.428 611.732v-512.638h512.638v512.638h-512.638zM860.906 156.054h-56.96v113.92h56.96v569.598h-569.598v-56.96h-113.92v56.96c0 62.826 51.092 113.92 113.92 113.92h569.598c62.826 0 113.92-51.092 113.92-113.92v-569.598c0-62.826-51.092-113.92-113.92-113.92z" />
<glyph unicode="&#xe003;" d="M920.026 857.542h-819.184c-56.32 0-102.4-46.080-102.4-102.4v-614.408c0-56.32 46.080-102.4 102.4-102.4h819.184c56.32 0 102.4 46.080 102.4 102.4v614.408c0 56.32-46.080 102.4-102.4 102.4zM233.962 761.792c21.218 0 38.4-16.72 38.4-37.368s-17.182-37.368-38.4-37.368c-21.226 0-38.4 16.72-38.4 37.368s17.174 37.368 38.4 37.368zM93.162 724.422c0 20.648 17.174 37.368 38.4 37.368 21.218 0 38.4-16.72 38.4-37.368s-17.182-37.368-38.4-37.368c-21.226 0-38.4 16.722-38.4 37.368zM921.050 140.736h-821.232v471.046h821.232v-471.046zM921.050 693.702h-615.41v61.44h615.408l0.002-61.44z" />
<glyph unicode="&#xe004;" d="M5.068 628.602l406.022 333.046v-170.426c84.684 0 610.454-3.552 610.454-420.036 0-224.19-197.48-407.528-452.89-436.834 158.92 27.528 278.45 146.562 278.45 289.2 0 272.476-313.032 274.77-436.014 274.77v-200.25l-406.022 330.53z" />
<glyph unicode="&#xe005;" d="M215.578-67.682v6.736h-13.474v6.736h-6.736v6.736h-6.736v13.474h-6.736v20.21h-6.736v33.684h6.736v40.422h6.736v40.422h6.736v40.422h6.736v33.684h6.736v40.422h6.736v40.422h6.736v47.158h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v6.736h-6.736v13.474h-6.736v60.632h6.736v13.474h6.736v6.736h6.736v6.736h13.474v6.736h13.474v6.736h40.422v6.736h47.158v6.736h47.158v6.736h47.158v6.736h47.158v6.736h47.158v6.736h20.21v6.736h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v13.474h6.736v6.736h6.736v6.736h13.474v6.736h67.368v-6.736h13.474v-6.736h6.736v-6.736h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-13.474h6.736v-6.736h20.21v-6.736h47.158v-6.736h47.158v-6.736h47.158v-6.736h47.158v-6.736h47.158v-6.736h40.422v-6.736h13.474v-6.736h13.474v-6.736h6.736v-6.736h6.736v-13.474h6.736v-60.632h-6.736v-13.474h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-6.736h-6.736v-47.158h6.736v-40.422h6.736v-40.422h6.736v-33.684h6.736v-40.422h6.736v-40.422h6.736v-40.422h6.736v-33.684h-6.736v-20.21h-6.736v-13.474h-6.736v-6.736h-6.736v-6.736h-13.474v-6.736h-60.632v6.736h-20.21v6.736h-6.736v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-6.736v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-13.474v6.736h-26.948v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-6.736v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-13.474v-6.736h-6.736v-6.736h-20.21v-6.736z" horiz-adv-x="1076" />
<glyph unicode="&#xe006;" d="M1074.446 574.958c-8.030 24.738-29.104 42.416-54.81 46.242l-290.008 42.146-129.67 262.736c-11.534 23.498-34.87 37.996-60.954 37.996-26.138 0-49.422-14.498-60.902-37.942l-129.78-262.844-290.062-42.146c-25.762-3.772-46.78-21.504-54.864-46.35-8.138-24.792-1.51-51.47 17.138-69.578l209.866-204.584-49.53-288.822c-4.364-25.708 5.93-51.254 27.002-66.614 11.75-8.462 25.546-12.88 39.99-12.88 11.048 0 21.99 2.694 31.636 7.814l259.45 136.3 259.394-136.3c9.648-5.12 20.588-7.814 31.636-7.814 14.498 0 28.294 4.474 39.882 12.988 21.234 15.306 31.582 40.744 27.11 66.398l-49.584 288.876 209.866 204.53c18.648 18.27 25.276 44.948 17.246 69.794zM738.842 335.88l47.052-275.186-246.946 129.886-247.108-129.886 47.158 275.186-199.896 194.83 276.318 40.206 123.528 250.34 123.58-250.34 276.264-40.206-199.95-194.83z" horiz-adv-x="1076" />
<glyph unicode="&#xe007;" d="M1024 448c0 282.752-229.248 512-512 512-282.752 0-512-229.248-512-512 0-282.752 229.248-512 512-512 282.752 0 512 229.248 512 512zM256 600h512l-256-416.798z" />
<glyph unicode="&#xe008;" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512c282.77 0 512 229.23 512 512 0 282.77-229.23 512-512 512zM512 64c-212.078 0-384 171.922-384 384s171.922 384 384 384c212.078 0 384-171.922 384-384 0-212.078-171.922-384-384-384zM770.378 609.706l-516.756 0.048 258.414-418.288z" />
<glyph unicode="&#xe009;" d="M992.942 491.452c0 0 31.17-18.286 31.164-43.83 0-25.574-31.164-43.856-31.164-43.856l-876.6-453.036c-63.888-34.938-116.152-9.208-116.152 57.152v879.466c0 66.398 52.268 92.132 116.152 57.162l876.6-453.058z" />
<glyph unicode="&#xe00a;" d="M31.354 491.452l876.6 453.058c63.884 34.97 116.152 9.236 116.152-57.162v-879.466c0-66.36-52.264-92.090-116.154-57.152l-876.598 453.036c0 0-31.164 18.282-31.164 43.856-0.004 25.544 31.164 43.83 31.164 43.83z" />
<glyph unicode="&#xe00b;" d="M831.758 959.614h-639.518c-105.52 0-191.856-87.914-191.856-195.366v-390.734c0-107.452 86.334-195.366 191.856-195.366h63.952c0 0 0.032-218.49 0-231.058-0.032-12.568 13.73-15.532 22.49-6.53 8.762 9.004 233.318 237.588 233.318 237.588h319.758c105.52 0 191.856 87.914 191.856 195.366v390.734c0 107.452-86.334 195.366-191.856 195.366z" />
<glyph unicode="&#xe00c;" d="M513.266 950.068c-279.672 0-506.402-184.36-506.402-411.826 0-149.548 98.036-280.532 244.648-352.686 0-0.624 0.032-216.798 0-229.272-0.032-12.492 13.822-15.574 22.638-6.626 6.706 6.808 115.638 116.436 178.582 179.74 19.856-1.924 40.042-3.148 60.534-3.148 279.672 0 506.402 184.524 506.402 411.992 0 227.468-226.73 411.826-506.402 411.826z" />
<glyph unicode="&#xe00d;" d="M511.334 958c-282.416 0-511.334-192.080-511.334-428.998 0-236.922 228.918-429.002 511.334-429.002 4.41 0 8.778 0.074 13.168 0.166 99.040-56.070 248.794-140.79 258.496-146.332 28.596-16.324 73.604-10.628 73.504 12.166-0.064 13.414-0.022 131.498 0 246.502 102.138 78.432 166.164 191.178 166.164 316.496 0 236.922-228.916 429.002-511.332 429.002z" />
<glyph unicode="&#xe00e;" d="M511.334 958c282.414 0 511.332-192.082 511.332-429s-228.918-429-511.332-429c-4.414 0-8.782 0.074-13.168 0.166-99.040-56.068-248.792-140.79-258.5-146.332-28.594-16.328-73.604-10.626-73.5 12.166 0.062 13.412 0.020 131.496 0 246.5-102.142 78.432-166.166 191.182-166.166 316.5 0 236.918 228.92 429 511.334 429z" />
<glyph unicode="&#xe00f;" d="M859.824 447.936c0 53.634 33.016 95.96 82.736 125.064-8.952 29.936-20.824 58.608-35.424 85.6-55.744-14.584-100.864 7.264-138.8 45.186-37.928 37.936-49.538 83.064-34.946 138.816-26.992 14.6-55.664 26.464-85.614 35.416-29.096-49.728-81.656-82.736-135.298-82.736-53.634 0-106.192 33.008-135.306 82.736-29.936-8.938-58.608-20.816-85.6-35.416 14.584-55.744 2.984-100.872-34.954-138.816-37.928-37.928-83.064-59.766-138.81-45.186-14.6-26.992-26.472-55.664-35.416-85.6 49.72-29.104 82.728-71.432 82.728-125.064 0-53.622-33.008-106.208-82.728-135.306 8.944-29.944 20.816-58.608 35.416-85.6 55.752 14.584 100.88 2.984 138.81-34.96 37.918-37.918 49.538-83.064 34.954-138.8 26.992-14.6 55.664-26.48 85.608-35.416 29.112 49.712 81.664 82.736 135.298 82.728 53.642 0 106.2-33.016 135.298-82.728 29.952 8.944 58.624 20.816 85.624 35.424-14.584 55.728-2.992 100.872 34.934 138.79 37.936 37.944 83.064 59.786 138.8 45.2 14.6 26.992 26.472 55.656 35.424 85.6-49.718 29.102-82.734 71.444-82.734 125.068zM512.48 260.894c-103.298 0-187.032 83.734-187.032 187.040s83.734 187.040 187.032 187.040 187.024-83.734 187.024-187.040-83.728-187.040-187.024-187.040z" />
<glyph unicode="&#xe010;" d="M192.242 959.614h639.518c105.52 0 191.856-87.914 191.856-195.366v-390.734c0-107.452-86.334-195.366-191.856-195.366h-63.952c0 0-0.032-218.49 0-231.058 0.032-12.568-13.73-15.532-22.49-6.53-8.76 9.002-233.316 237.586-233.316 237.586h-319.758c-105.52 0-191.856 87.914-191.856 195.366v390.734c0 107.452 86.334 195.366 191.856 195.366z" />
<glyph unicode="&#xe011;" d="M928 960h-832c-52.8 0-96-43.2-96-96v-640c0-52.8 43.2-96 96-96h224l192-192 192 192h224c52.8 0 96 43.2 96 96v640c0 52.8-43.198 96-96 96z" />
<glyph unicode="&#xe012;" d="M992 576h-352v352c0 17.664-14.304 32-32 32h-192c-17.696 0-32-14.336-32-32v-352h-352c-17.696 0-32-14.336-32-32v-192c0-17.696 14.304-32 32-32h352v-352c0-17.696 14.304-32 32-32h192c17.696 0 32 14.304 32 32v352h352c17.696 0 32 14.304 32 32v192c0 17.664-14.304 32-32 32z" />
<glyph unicode="&#xe013;" d="M867.2 900.656h-710.4c-65.12 0-118.4-59.2-118.4-131.712v-460.64c0-72.144 53.28-131.76 118.4-131.76h236.8l236.8-197.216v197.216l236.8-0.016c65.152 0 118.4 59.568 118.4 131.76v460.64c0 72.528-53.28 131.728-118.4 131.728zM712.608 510.528l-161.872-0.592-0.592-171.68-76.288 0.592-0.592 170.528h-161.872l0.592 72.256 161.28 0.592 0.592 169.392h76.88v-169.392h161.872v-71.696z" />
<glyph unicode="&#xe014;" d="M540.314 35.776c35.676 30.978 71.622 61.646 107.352 92.564 70.3 0.472 145.966-0.902 216.116 0.714 28.556 4.502 40.626 35.59 36.84 61.62-0.412 230.256 0.784 424.858-0.632 654.998-2.514 29.37-33.572 44.826-60.464 40.356-209.332-0.41-429.096 0.818-638.31-0.614-29.402-2.716-44.262-34.094-39.944-60.868 0.316-230.272-0.672-424.826 0.46-655.022 1.548-28.542 30.948-45.942 57.456-41.16 63.296-0.008 131.714-0.016 195.010-0.024 38.924-32.974 77.38-67.894 116.58-99.68 3.658 0.058 6.402 5.34 9.536 7.116zM516.148-52.122c-40.386 34.954-80.956 69.692-121.274 104.722-69.612 1.206-165.206-0.78-234.58 2.408-47.592 7.558-79.946 55.316-75.18 102.072 0.164 241.364 0.326 482.726 0.488 724.088 6.3 39.19 37.36 72.104 77.032 78.046 227.758 1.076 506.972 0.154 734.828 0.462 38.6-5.68 70.846-36.072 77.736-74.602 2.53-89.334 0.472-179.282 1.24-268.852-0.248-160.076 0.93-320.296-0.212-480.282-4.11-48.194-50.212-86.848-98.362-82.26-61.758-0.36-149.118-0.718-210.876-1.078-45.222-39.066-90.278-78.336-135.906-116.922-5.298 3.662-10.026 8.054-14.934 12.2zM727.632 229.75c-34.042 7.418-46.316 56.226-19.714 78.77 24.542 26.894 75.762 8.706 77.736-27.778 5.124-32.196-26.592-60.294-58.022-50.992zM285.424 505.526c0 92.734 0 185.468 0 278.204 22.802-0.164 45.606-0.328 68.408-0.494 0.31-79.222 0.62-158.442 0.93-237.664 36.044 67.63 133.806 90.828 195.172 43.968 46.66-34.908 57.666-97.974 54.722-153.13 0.012-69.538 0.024-139.078 0.034-208.616-22.658-0.164-45.314-0.328-67.972-0.494-0.602 79.566 1.2 159.286-0.9 238.754-1.206 39.602-25.532 84.368-68.23 89.27-58.734 12.966-119.044-42.924-112.828-101.946-0.168-75.352-0.336-150.704-0.506-226.056-22.944 0-45.89 0-68.834 0 0 92.734-0.002 185.47 0 278.204z" />
<glyph unicode="&#xe015;" d="M292.576 448c0 117.824 98.24 213.344 219.424 213.344s219.424-95.52 219.424-213.344-98.24-213.344-219.424-213.344-219.424 95.52-219.424 213.344zM0 440.32c94.816-185.472 286.784-312.32 508.224-312.32 227.456 0 423.904 133.92 515.776 327.68-94.816 185.472-286.816 312.32-508.224 312.32-227.456 0-423.872-133.92-515.776-327.68zM402.272 448c0-58.912 49.12-106.656 109.728-106.656s109.728 47.744 109.728 106.656-49.12 106.656-109.728 106.656-109.728-47.744-109.728-106.656z" />
<glyph unicode="&#xe016;" d="M512 768c-282.784 0-512-320-512-320s229.216-320 512-320 512 320 512 320-229.216 320-512 320zM512 256c-106.016 0-192 85.984-192 192s85.984 192 192 192 192-85.984 192-192-85.984-192-192-192zM512 576c-70.688 0-128-57.312-128-128s57.312-128 128-128 128 57.312 128 128-57.312 128-128 128z" />
<glyph unicode="&#xe017;" d="M512 736c-256 0-511.998-223.998-512-288v0c0.002-64.002 256-288 512-288s511.994 223.998 512 288v0c-0.006 64.002-256 288-512 288zM512 256c-106.042 0-192 85.96-192 192s85.958 192 192 192c106.040 0 192-85.96 192-192 0-106.040-85.96-192-192-192zM448 448c0-35.346 28.654-64 64-64s64 28.654 64 64c0 35.346-28.654 64-64 64-35.346 0-64-28.654-64-64z" />
<glyph unicode="&#xe018;" d="M896 256v-128h-768v128h-128v-256h1024v256zM768 576h-192v320h-128v-320h-192l256-384z" />
<glyph unicode="&#xe019;" d="M1024 662.49l-131.93-211.316-446.732 324.582 154.598 184.244h-139.098l-123.956-147.546c-23.13-5.018-44.608-17.664-58.97-37.26l-73.472-101.196c-15.564-21.030-22.17-47.514-17.958-73.562 3.532-23.104 14.784-43.34 32.012-58.74 17.204-24.192 94.028-145.676 12.16-258.15-0.012-0.012-0.026-0.026-0.038-0.038l-180.606-248.756c-11.482-16.256-13.222-37.248-4.428-55.194 8.794-17.958 26.906-29.248 46.822-29.402l254.746-3.444c3.226 0.012 6.464 0.116 9.254 0.768 13.824 2.368 26.176 10.048 34.65 21.428l92.684 127.462c0.5 0.64 1.114 1.126 1.588 1.792 65.946 91.404 222.028 68.902 258.47 62.080 41.626-22.516 98.278-10.304 126.836 29.044 0.038 0.052 0.076 0.076 0.116 0.128 0.012 0.012 0.026 0.038 0.038 0.064 0.026 0.038 0.064 0.076 0.102 0.128l73.434 101.082c15.22 20.966 21.836 46.426 18.164 71.936l61.514 98.598v201.268zM321.114 53.696l-124.364 1.716 88.948 122.534 82.124-60.108zM435.136 200.73l-90.714 66.010c67.674 138.394 3.572 282.662-44.852 344.87-1.638 1.83-3.316 3.622-5.082 5.364l64.934 89.754 494.338-359.26-37.786-51.98-286.63 209.434c-23.54 17.78-57.114 12.134-74.368-11.38-17.37-23.54-12.186-57.088 11.354-74.38l198.516-145.036c-70.99 0.204-162.024-13.62-229.71-73.396z" />
<glyph unicode="&#xe01a;" d="M1024 662.49l-131.93-211.316-446.732 324.582 154.598 184.244h-139.098l-123.956-147.546c-23.13-5.018-44.608-17.664-58.97-37.26l-73.472-101.196c-15.564-21.030-22.17-47.514-17.958-73.562 3.532-23.104 14.784-43.34 32.012-58.74 17.204-24.192 94.028-145.676 12.16-258.15-0.012-0.012-0.026-0.026-0.038-0.038l-180.606-248.756c-11.482-16.256-13.222-37.248-4.428-55.194 8.794-17.958 26.906-29.248 46.822-29.402l254.746-3.444c3.226 0.012 6.464 0.116 9.254 0.768 13.824 2.368 26.176 10.048 34.65 21.428l92.684 127.462c0.5 0.64 1.114 1.126 1.588 1.792 65.946 91.404 222.028 68.902 258.47 62.080 41.626-22.516 98.278-10.304 126.836 29.044 0.038 0.052 0.076 0.076 0.116 0.128 0.012 0.012 0.026 0.038 0.038 0.064 0.026 0.038 0.064 0.076 0.102 0.128l73.434 101.082c15.22 20.966 21.836 46.426 18.164 71.936l61.514 98.598v201.268zM321.114 53.696l-124.364 1.716 88.948 122.534 82.124-60.108zM664.858 274.124c-71.014 0.204-162.048-13.62-229.722-73.396l-90.714 66.010c67.674 138.394 3.572 282.662-44.852 344.87-1.638 1.83-3.316 3.622-5.082 5.364l64.934 89.754 494.338-359.258-37.786-51.98z" />
<glyph unicode="&#xe01b;" d="M512 736c-256 0-511.998-223.998-512-288v0c0.002-64.002 256-288 512-288s511.994 223.998 512 288v0c-0.006 64.002-256 288-512 288zM512 256c-106.042 0-192 85.96-192 192s85.958 192 192 192c106.040 0 192-85.96 192-192 0-106.040-85.96-192-192-192z" />
<glyph unicode="&#xe01c;" d="M1024 662.49l-131.93-211.316-446.732 324.582 154.598 184.244h-139.098l-123.956-147.546c-23.13-5.018-44.608-17.664-58.97-37.26l-73.472-101.196c-15.564-21.030-22.17-47.514-17.958-73.562 3.532-23.104 14.784-43.34 32.012-58.74 17.204-24.192 94.028-145.676 12.16-258.15-0.012-0.012-0.026-0.026-0.038-0.038l-180.606-248.756c-11.482-16.256-13.222-37.248-4.428-55.194 8.794-17.958 26.906-29.248 46.822-29.402l254.746-3.444c3.226 0.012 6.464 0.116 9.254 0.768 13.824 2.368 26.176 10.048 34.65 21.428l92.684 127.462c0.5 0.64 1.114 1.126 1.588 1.792 65.946 91.404 222.028 68.902 258.47 62.080 41.626-22.516 98.278-10.304 126.836 29.044 0.038 0.052 0.076 0.076 0.116 0.128 0.012 0.012 0.026 0.038 0.038 0.064 0.026 0.038 0.064 0.076 0.102 0.128l73.434 101.082c15.22 20.966 21.836 46.426 18.164 71.936l61.514 98.598v201.268zM435.136 200.73l-90.714 66.010c67.674 138.394 3.572 282.662-44.852 344.87-1.638 1.83-3.316 3.622-5.082 5.364l64.934 89.754 494.338-359.26-37.786-51.98-286.63 209.434c-23.54 17.78-57.114 12.134-74.368-11.38-17.37-23.54-12.186-57.088 11.354-74.38l198.516-145.036c-70.99 0.204-162.024-13.62-229.71-73.396z" />
<glyph unicode="&#xe01d;" d="M1024 662.49l-131.93-211.316-446.732 324.582 154.598 184.244h-139.098l-123.956-147.546c-23.13-5.018-44.608-17.664-58.97-37.26l-73.472-101.196c-15.564-21.030-22.17-47.514-17.958-73.562 3.532-23.104 14.784-43.34 32.012-58.74 17.204-24.192 94.028-145.676 12.16-258.15-0.012-0.012-0.026-0.026-0.038-0.038l-180.606-248.756c-11.482-16.256-13.222-37.248-4.428-55.194 8.794-17.958 26.906-29.248 46.822-29.402l254.746-3.444c3.226 0.012 6.464 0.116 9.254 0.768 13.824 2.368 26.176 10.048 34.65 21.428l92.684 127.462c0.5 0.64 1.114 1.126 1.588 1.792 65.946 91.404 222.028 68.902 258.47 62.080 41.626-22.516 98.278-10.304 126.836 29.044 0.038 0.052 0.076 0.076 0.116 0.128 0.012 0.012 0.026 0.038 0.038 0.064 0.026 0.038 0.064 0.076 0.102 0.128l73.434 101.082c15.22 20.966 21.836 46.426 18.164 71.936l61.514 98.598v201.268zM664.858 274.124c-71.014 0.204-162.048-13.62-229.722-73.396l-90.714 66.010c67.674 138.394 3.572 282.662-44.852 344.87-1.638 1.83-3.316 3.622-5.082 5.364l64.934 89.754 494.338-359.258-37.786-51.98z" />
<glyph unicode="&#xe01e;" d="M156.8 900.656h710.4c65.12 0 118.4-59.2 118.4-131.712v-460.64c0-72.144-53.28-131.76-118.4-131.76h-236.8l-236.8-197.216v197.216l-236.8-0.016c-65.152 0-118.4 59.568-118.4 131.76v460.64c0 72.528 53.28 131.728 118.4 131.728zM311.392 510.528l161.872-0.592 0.592-171.68 76.288 0.592 0.592 170.528h161.872l-0.592 72.256-161.28 0.592-0.592 169.392h-76.88v-169.392h-161.872v-71.696z" />
<glyph unicode="&#xe020;" d="M877.91 685.91l-118.998-119.040-106.666 21.334-21.334 106.666 118.998 119.040c-17.494 5.29-35.754 8.96-54.998 8.96-105.984 0-192-85.974-192-192v-64l-293.418-238.080c-55.766-55.254-47.574-135.594 0-183.338 47.574-47.83 127.574-55.466 183.338 0l238.080 293.418h64c106.070 0 192 85.974 192 192 0 19.284-3.67 37.504-9.002 55.040z" />
<glyph unicode="&#xe600;" d="M903.529 960h-783.059c-66.5 0-120.471-58.85-120.471-131.433v-460.017c0-72.523 53.971-131.373 120.471-131.373h90.353v-263.529l263.529 263.529h429.176c66.5 0 120.471 58.85 120.471 131.433v459.957c0 72.584-53.971 131.433-120.471 131.433zM722.824 576h-180.706v-180.706h-60.235v180.706h-180.706v60.235h180.706v180.706h60.235v-180.706h180.706v-60.235z" />
<glyph unicode="&#xe601;" d="M747.947 894.707l215.846-215.816-431.674-431.735-215.846 215.816 431.674 431.735zM468.932 184.049l-260.578-44.755 44.695 260.578zM0 171.942h361.412v-120.471h-361.412v120.471z" />
<glyph unicode="&#xe602;" d="M0 484.804c560.911-662.588 1024 0 1024 0-560.911 662.588-1024 0-1024 0zM512 299.098c-102.581 0-185.705 83.125-185.705 185.705 0 102.581 83.125 185.705 185.705 185.705s185.705-83.125 185.705-185.705c0-102.581-83.125-185.705-185.705-185.705z" />
<glyph unicode="&#xe603;" d="M734.994 154.626c-18.952 2.988-19.384 54.654-19.384 54.654s55.688 54.656 67.824 128.152c32.652 0 52.814 78.138 20.164 105.628 1.362 28.94 41.968 227.176-163.598 227.176-205.564 0-164.958-198.236-163.598-227.176-32.654-27.49-12.488-105.628 20.162-105.628 12.134-73.496 67.826-128.152 67.826-128.152s-0.432-51.666-19.384-54.654c-61.048-9.632-289.006-109.316-289.006-218.626h768c0 109.31-227.958 208.994-289.006 218.626zM344.054 137.19c44.094 27.15 97.626 52.308 141.538 67.424-15.752 22.432-33.294 52.936-44.33 89.062-15.406 12.566-27.944 30.532-35.998 52.602-8.066 22.104-11.122 46.852-8.608 69.684 1.804 16.392 6.478 31.666 13.65 45.088-4.35 46.586-7.414 138.034 52.448 204.732 23.214 25.866 52.556 44.46 87.7 55.686-6.274 64.76-39.16 140.77-166.454 140.77-205.564 0-164.958-198.236-163.598-227.176-32.654-27.49-12.488-105.628 20.162-105.628 12.134-73.496 67.826-128.152 67.826-128.152s-0.432-51.666-19.384-54.654c-61.048-9.634-289.006-109.318-289.006-218.628h329.596c4.71 3.074 9.506 6.14 14.458 9.19z" />
<glyph unicode="&#xe604;" d="M622.826 257.264c-22.11 3.518-22.614 64.314-22.614 64.314s64.968 64.316 79.128 150.802c38.090 0 61.618 91.946 23.522 124.296 1.59 34.054 48.96 267.324-190.862 267.324-239.822 0-192.45-233.27-190.864-267.324-38.094-32.35-14.57-124.296 23.522-124.296 14.158-86.486 79.128-150.802 79.128-150.802s-0.504-60.796-22.614-64.314c-71.22-11.332-337.172-128.634-337.172-257.264h896c0 128.63-265.952 245.932-337.174 257.264z" />
<glyph unicode="&#xe605;" d="M1024 530.744c0 200.926-58.792 363.938-131.482 365.226 0.292 0.006 0.578 0.030 0.872 0.030h-82.942c0 0-194.8-146.336-475.23-203.754-8.56-45.292-14.030-99.274-14.030-161.502 0-62.228 5.466-116.208 14.030-161.5 280.428-57.418 475.23-203.756 475.23-203.756h82.942c-0.292 0-0.578 0.024-0.872 0.032 72.696 1.288 131.482 164.298 131.482 365.224zM864.824 220.748c-9.382 0-19.532 9.742-24.746 15.548-12.63 14.064-24.792 35.96-35.188 63.328-23.256 61.232-36.066 143.31-36.066 231.124 0 87.81 12.81 169.89 36.066 231.122 10.394 27.368 22.562 49.266 35.188 63.328 5.214 5.812 15.364 15.552 24.746 15.552 9.38 0 19.536-9.744 24.744-15.552 12.634-14.064 24.796-35.958 35.188-63.328 23.258-61.23 36.068-143.312 36.068-231.122 0-87.804-12.81-169.888-36.068-231.124-10.39-27.368-22.562-49.264-35.188-63.328-5.208-5.806-15.36-15.548-24.744-15.548zM251.812 530.744c0 51.95 3.81 102.43 11.052 149.094-47.372-6.554-88.942-10.324-140.34-10.324-67.058 0-67.058 0-67.058 0l-55.466-94.686v-88.17l55.46-94.686c0 0 0 0 67.060 0 51.398 0 92.968-3.774 140.34-10.324-7.236 46.664-11.048 97.146-11.048 149.096zM368.15 317.828l-127.998 24.51 81.842-321.544c4.236-16.634 20.744-25.038 36.686-18.654l118.556 47.452c15.944 6.376 22.328 23.964 14.196 39.084l-123.282 229.152zM864.824 411.27c-3.618 0-7.528 3.754-9.538 5.992-4.87 5.42-9.556 13.86-13.562 24.408-8.962 23.6-13.9 55.234-13.9 89.078 0 33.844 4.938 65.478 13.9 89.078 4.006 10.548 8.696 18.988 13.562 24.408 2.010 2.24 5.92 5.994 9.538 5.994 3.616 0 7.53-3.756 9.538-5.994 4.87-5.42 9.556-13.858 13.56-24.408 8.964-23.598 13.902-55.234 13.902-89.078 0-33.842-4.938-65.478-13.902-89.078-4.004-10.548-8.696-18.988-13.56-24.408-2.008-2.238-5.92-5.992-9.538-5.992z" />
<glyph unicode="&#xe606;" d="M1088 58.834c0-45.5 26.028-84.908 64-104.184v-15.938c-10.626-1.454-21.472-2.224-32.5-2.224-68.008 0-129.348 28.528-172.722 74.264-26.222-6.982-54.002-10.752-82.778-10.752-159.058 0-288 114.616-288 256 0 141.384 128.942 256 288 256 159.058 0 288-114.616 288-256 0-55.348-19.764-106.592-53.356-148.466-6.824-14.824-10.644-31.312-10.644-48.7zM230.678 738.814c-66.214-53.798-102.678-122.984-102.678-194.814 0-40.298 11.188-79.378 33.252-116.15 22.752-37.92 56.982-72.586 98.988-100.252 30.356-19.992 50.78-51.948 56.176-87.894 1.8-11.984 2.928-24.088 3.37-36.124 7.47 6.194 14.75 12.846 21.88 19.976 24.154 24.152 56.78 37.49 90.502 37.49 5.368 0 10.762-0.336 16.156-1.024 20.948-2.662 42.344-4.016 63.594-4.020v-128c-27.128 0.002-53.754 1.738-79.742 5.042-109.978-109.978-241.25-129.7-368.176-132.596v26.916c68.536 33.578 128 94.74 128 164.636 0 9.754-0.758 19.33-2.164 28.696-115.796 76.264-189.836 192.754-189.836 323.304 0 229.75 229.23 416 512 416 278.458 0 504.992-180.614 511.836-405.52-41.096 18.316-85.84 29.422-132.262 32.578-11.53 56.068-45.402 108.816-98.252 151.756-35.322 28.698-76.916 51.39-123.628 67.444-49.706 17.080-102.76 25.742-157.694 25.742-54.932 0-107.988-8.662-157.694-25.742-46.712-16.054-88.306-38.744-123.628-67.444z" horiz-adv-x="1152" />
<glyph unicode="&#xe607;" d="M480 960v0c265.096 0 480-173.914 480-388.448s-214.904-388.448-480-388.448c-25.458 0-50.446 1.62-74.834 4.71-103.106-102.694-222.172-121.108-341.166-123.814v25.134c64.252 31.354 116 88.466 116 153.734 0 9.106-0.712 18.048-2.030 26.794-108.558 71.214-177.97 179.988-177.97 301.89 0 214.534 214.904 388.448 480 388.448zM996 89.314c0-55.942 36.314-104.898 92-131.772v-21.542c-103.126 2.318-197.786 18.102-287.142 106.126-21.14-2.65-42.794-4.040-64.858-4.040-95.47 0-183.408 25.758-253.614 69.040 144.674 0.506 281.26 46.854 384.834 130.672 52.208 42.252 93.394 91.826 122.414 147.348 30.766 58.866 46.366 121.582 46.366 186.406 0 10.448-0.45 20.836-1.258 31.168 72.57-59.934 117.258-141.622 117.258-231.676 0-104.488-60.158-197.722-154.24-258.764-1.142-7.496-1.76-15.16-1.76-22.966z" horiz-adv-x="1152" />
<glyph unicode="&#xe608;" d="M1025.5 160c0 288-256 224-256 448 0 18.56-1.788 34.42-5.048 47.928-16.83 113.018-92.156 203.72-189.772 231.36 0.866 3.948 1.32 8.032 1.32 12.21 0 33.278-28.8 60.502-64 60.502-35.2 0-64-27.224-64-60.5 0-4.18 0.456-8.264 1.32-12.21-109.47-30.998-190.914-141.298-193.254-273.442-0.040-1.92-0.066-3.864-0.066-5.846 0-224.002-256-160.002-256-448.002 0-76.226 170.59-139.996 398.97-156.080 21.524-40.404 64.056-67.92 113.030-67.92s91.508 27.516 113.030 67.92c228.38 16.084 398.97 79.854 398.97 156.080 0 0.228-0.026 0.456-0.028 0.682l1.528-0.682zM826.246 105.904c-54.23-14.47-118.158-24.876-186.768-30.648-5.704 65.418-60.582 116.744-127.478 116.744s-121.774-51.326-127.478-116.744c-68.608 5.772-132.538 16.178-186.768 30.648-74.63 19.914-110.31 42.19-123.368 54.096 13.058 11.906 48.738 34.182 123.368 54.096 86.772 23.152 198.372 35.904 314.246 35.904s227.474-12.752 314.246-35.904c74.63-19.914 110.31-42.19 123.368-54.096-13.058-11.906-48.738-34.182-123.368-54.096z" />
<glyph unicode="&#xe609;" d="M874.040 810.038c96.702-96.704 149.96-225.28 149.96-362.040s-53.258-265.334-149.96-362.038c-96.706-96.702-225.28-149.96-362.040-149.96-136.76 0-265.334 53.258-362.040 149.96-96.702 96.704-149.96 225.278-149.96 362.038 0 136.76 53.254 265.336 149.96 362.040 96.706 96.704 225.28 149.962 362.040 149.962 136.76 0 265.334-53.258 362.040-149.962zM828.784 131.214c63.058 63.060 104.986 141.608 122.272 227.062-13.474-19.836-26.362-27.194-34.344 17.206-8.22 72.39-74.708 26.148-116.516 51.86-44.004-29.658-142.906 57.662-126.098-40.824 25.934-44.422 140.008 59.45 83.148-34.542-36.274-65.616-132.642-210.932-120.106-286.258 1.582-109.744-112.134-22.884-151.314 13.52-26.356 72.92-8.982 200.374-77.898 236.086-74.802 3.248-139.004 10.046-167.994 93.67-17.446 59.828 18.564 148.894 82.678 162.644 93.85 58.966 127.374-69.054 215.39-71.434 27.328 28.594 101.816 37.686 107.992 69.75-57.75 10.19 73.268 48.558-5.528 70.382-43.47-5.112-71.478-45.074-48.368-78.958-84.238-19.642-86.936 121.904-167.91 77.258-2.058-70.59-132.222-22.886-45.036-8.572 29.956 13.088-48.86 51.016-6.28 44.124 20.916 1.136 91.332 25.812 72.276 42.402 39.21 24.34 72.16-58.29 110.538 1.882 27.708 46.266-11.62 54.808-46.35 31.356-19.58 21.924 34.57 69.276 82.332 89.738 15.918 6.82 31.122 10.536 42.746 9.484 24.058-27.792 68.55-32.606 70.878 3.342-59.582 28.534-125.276 43.608-193.292 43.608-97.622 0-190.47-31.024-267.308-88.39 20.65-9.46 32.372-21.238 12.478-36.296-15.456-46.054-78.17-107.876-133.224-99.124-28.586-49.296-47.412-103.606-55.46-160.528 46.112-15.256 56.744-45.45 46.836-55.55-23.496-20.488-37.936-49.53-45.376-81.322 15.010-91.836 58.172-176.476 125.27-243.576 84.616-84.614 197.118-131.214 316.784-131.214 119.664 0 232.168 46.6 316.784 131.214z" />
<glyph unicode="&#xe60a;" d="M953.396 885.358l-4.028 4.042c-94.148 94.134-248.194 94.134-342.326 0l-218.106-218.136c-94.134-94.132-94.134-248.176 0-342.31l4.026-4.026c7.832-7.848 16.146-14.924 24.736-21.458l79.848 79.85c-9.302 5.494-18.126 12.072-26.116 20.060l-4.042 4.042c-51.114 51.098-51.114 134.272 0 185.39l218.128 218.112c51.116 51.118 134.274 51.118 185.386 0l4.042-4.024c51.1-51.116 51.1-134.292 0-185.39l-98.686-98.686c17.132-42.308 25.248-87.4 24.538-132.386l152.604 152.604c94.134 94.136 94.134 248.178-0.004 342.316zM631.042 571.066c-7.832 7.832-16.146 14.922-24.736 21.44l-79.848-79.832c9.304-5.496 18.126-12.074 26.116-20.062l4.042-4.040c51.116-51.116 51.116-134.272 0-185.388l-218.13-218.134c-51.118-51.102-134.276-51.102-185.388 0l-4.042 4.042c-51.098 51.12-51.098 134.276 0 185.388l98.688 98.686c-17.134 42.306-25.246 87.402-24.538 132.386l-152.602-152.598c-94.136-94.132-94.136-248.178 0-342.324l4.026-4.032c94.152-94.128 248.192-94.128 342.328 0l218.11 218.118c94.134 94.132 94.134 248.194 0 342.326l-4.026 4.024z" />
<glyph unicode="&#xe60b;" d="M843.294 960h-722.824c-66.259 0-120.471-60.235-120.471-132.518v-457.788c0-72.282 54.212-132.518 120.471-132.518h60.235v-301.176l289.129 301.176h373.459c66.259 0 120.471 60.235 120.471 132.518v457.788c0 72.282-54.212 132.518-120.471 132.518zM722.824 538.353h-180.706v-180.706h-120.471v180.706h-180.706v120.471h180.706v180.706h120.471v-180.706h180.706v-120.471z" />
<glyph unicode="&#xf06e;" d="M950.858 411.428q-86.858 134.858-217.714 201.714 34.858-59.428 34.858-128.572 0-105.714-75.142-180.858t-180.858-75.142-180.858 75.142-75.142 180.858q0 69.142 34.858 128.572-130.858-66.858-217.714-201.714 76-117.142 190.572-186.572t248.286-69.428 248.286 69.428 190.572 186.572zM539.428 630.858q0 11.428-8 19.428t-19.428 8q-71.428 0-122.572-51.142t-51.142-122.572q0-11.428 8-19.428t19.428-8 19.428 8 8 19.428q0 49.142 34.858 84t84 34.858q11.428 0 19.428 8t8 19.428zM1024 411.428q0-19.428-11.428-39.428-80-131.428-215.142-210.572t-285.428-79.142-285.428 79.428-215.142 210.286q-11.428 20-11.428 39.428t11.428 39.428q80 130.858 215.142 210.286t285.428 79.428 285.428-79.428 215.142-210.286q11.428-20 11.428-39.428z" />
<glyph unicode="&#xf070;" d="M317.142 197.142l44.572 80.572q-49.714 36-77.714 90.858t-28 116q0 69.142 34.858 128.572-130.858-66.858-217.714-201.714 95.428-147.428 244-214.286zM539.428 630.858q0 11.428-8 19.428t-19.428 8q-71.428 0-122.572-51.142t-51.142-122.572q0-11.428 8-19.428t19.428-8 19.428 8 8 19.428q0 49.142 34.858 84t84 34.858q11.428 0 19.428 8t8 19.428zM746.858 740q0-4-0.572-5.142-60-107.428-180-323.428t-180.572-324l-28-50.858q-5.714-9.142-16-9.142-6.858 0-76.572 40-9.142 5.714-9.142 16 0 6.858 25.142 49.714-81.714 37.142-150.572 98.858t-119.142 140q-11.428 17.714-11.428 39.428t11.428 39.428q87.428 134.286 217.142 212t283.428 77.714q50.858 0 102.858-9.714l30.858 55.428q5.714 9.142 16 9.142 2.858 0 10.286-3.428t17.714-8.858 18.858-10.572 18-10.572 11.142-6.572q9.142-5.714 9.142-15.428zM768 484.572q0-79.428-45.142-144.858t-119.428-94l160 286.858q4.572-25.714 4.572-48zM1024 411.428q0-20-11.428-39.428-22.286-36.572-62.286-82.858-85.714-98.286-198.572-152.572t-239.714-54.286l42.286 75.428q121.142 10.286 224.286 78.286t172.286 175.428q-65.714 102.286-161.142 168l36 64q54.286-36.572 104.286-87.428t82.572-105.142q11.428-19.428 11.428-39.428z" />
<glyph unicode="&#xf0d0;" d="M680 628l167.428 167.428-61.142 61.142-167.428-167.428zM935.428 795.428q0-15.428-10.286-25.714l-734.858-734.858q-10.286-10.286-25.714-10.286t-25.714 10.286l-113.142 113.142q-10.286 10.286-10.286 25.714t10.286 25.714l734.858 734.858q10.286 10.286 25.714 10.286t25.714-10.286l113.142-113.142q10.286-10.286 10.286-25.714zM163.428 904l56-17.142-56-17.142-17.142-56-17.142 56-56 17.142 56 17.142 17.142 56zM363.428 811.428l112-34.286-112-34.286-34.286-112-34.286 112-112 34.286 112 34.286 34.286 112zM894.858 538.286l56-17.142-56-17.142-17.142-56-17.142 56-56 17.142 56 17.142 17.142 56zM529.142 904l56-17.142-56-17.142-17.142-56-17.142 56-56 17.142 56 17.142 17.142 56z" horiz-adv-x="950" />
<glyph unicode="&#xf0fe;" d="M731.428 411.428v73.142q0 14.858-10.858 25.714t-25.714 10.858h-182.858v182.858q0 14.858-10.858 25.714t-25.714 10.858h-73.142q-14.858 0-25.714-10.858t-10.858-25.714v-182.858h-182.858q-14.858 0-25.714-10.858t-10.858-25.714v-73.142q0-14.858 10.858-25.714t25.714-10.858h182.858v-182.858q0-14.858 10.858-25.714t25.714-10.858h73.142q14.858 0 25.714 10.858t10.858 25.714v182.858h182.858q14.858 0 25.714 10.858t10.858 25.714zM877.714 722.286v-548.572q0-68-48.286-116.286t-116.286-48.286h-548.572q-68 0-116.286 48.286t-48.286 116.286v548.572q0 68 48.286 116.286t116.286 48.286h548.572q68 0 116.286-48.286t48.286-116.286z" horiz-adv-x="876" />
<font id="h" horiz-adv-x="512">
<font-face units-per-em="512" ascent="480" descent="-32" />
<missing-glyph horiz-adv-x="512" />
<glyph unicode="&#x20;" d="" horiz-adv-x="256" />
<glyph unicode="&#x22;" d="M466.895 174.875c-26.863 46.527-10.708 106.152 36.076 133.244l-50.313 87.146c-14.375-8.427-31.088-13.259-48.923-13.259-53.768 0-97.354 43.873-97.354 97.995h-100.629c0.133-16.705-4.037-33.641-12.979-49.126-26.862-46.528-86.578-62.351-133.431-35.379l-50.312-87.146c14.485-8.236 27.025-20.294 35.943-35.739 26.819-46.454 10.756-105.96-35.854-133.112l50.313-87.146c14.325 8.348 30.958 13.127 48.7 13.127 53.598 0 97.072-43.596 97.35-97.479h100.627c-0.043 16.537 4.136 33.285 12.983 48.609 26.818 46.453 86.388 62.297 133.207 35.506l50.313 87.145c-14.39 8.233-26.846 20.249-35.717 35.614zM256 120.334c-57.254 0-103.668 46.412-103.668 103.667 0 57.254 46.413 103.667 103.668 103.667s103.666-46.413 103.666-103.667c-0.001-57.255-46.412-103.667-103.666-103.667z" />
<glyph unicode="&#x23;" d="M37.5 379.5q0 12.75 4.25 25.625t13 23.875 22 19.375 31.75 12.125l8.25-17.5q-19.5-6.25-31-18.5t-9-31.75q5.75-4.5 16-9.375t17.75-7.375q2-8.25 0-16t-6.625-13.625-10.75-9.625-11.125-4.25q-18 1.5-31.25 13.25t-13.25 33.75zM130.75 379.5q0 12.75 4.25 25.625t13 23.875 22 19.375 31.75 12.125l8.25-17.5q-19.5-6.25-31-18.5t-9-31.75q5.75-4.5 16-9.375t17.75-7.375q2-8.25 0-16t-6.625-13.625-10.75-9.625-11.125-4.25q-18 1.5-31.25 13.25t-13.25 33.75z" horiz-adv-x="242" />
<glyph unicode="&#x24;" d="M29.25 13.75q20 6.25 33 21t10.5 34.25q-5.75 4.5-16 9.125t-17.75 7.625q-2.5 8.25-0.25 16t6.875 13.625 10.5 9.625 11.375 3.75q17-1.5 30.875-13t13.875-34q0-12.25-5.125-25.625t-14.875-25.125-23.625-21-30.625-13.75zM122.5 13.75q20 6.25 33 21t10.5 34.25q-5.75 4.5-16 9.125t-17.75 7.625q-2.5 8.25-0.25 16t6.875 13.625 10.5 9.625 11.375 3.75q17-1.5 30.875-13t13.875-34q0-12.25-5.125-25.625t-14.875-25.125-23.625-21-30.625-13.75z" horiz-adv-x="242" />
<glyph unicode="&#x25;" d="M506.944 211.739c-2.816-2.816-7.342-2.843-10.13-0.027-2.761 2.816-15.918 15.945-29.138 29.193l-12.453 12.507c-13.275 13.275-26.432 26.377-29.221 29.193-2.816 2.761-7.342 2.761-10.13 0-2.816-2.816-15.945-15.945-29.193-29.193l-104.549-104.502c-13.275-13.275-28.672-28.699-34.24-34.24-5.568-5.568-14.665-5.568-20.233 0-5.568 5.541-21.001 20.946-34.267 34.24l-12.507 12.453c-13.275 13.275-28.699 28.699-34.24 34.267-5.568 5.568-5.568 14.665 0 20.233 5.541 5.568 20.946 21.001 34.24 34.267l104.558 104.503c13.275 13.275 26.377 26.432 29.193 29.138 2.816 2.816 2.816 7.342 0 10.13-2.761 2.816-15.918 15.945-29.193 29.221l-12.507 12.507c-13.275 13.275-26.377 26.377-29.193 29.138-2.816 2.816-2.761 7.342 0 10.13 2.816 2.816 8.713 5.056 13.138 5.056 4.453 0 23.433-0.082 42.158-0.137l154.853-0.603c18.789-0.082 39.323-0.137 45.687-0.137 6.336 0 11.547-5.184 11.547-11.547 0-6.309 0.082-26.898 0.137-45.687l0.658-152.539c-0.001-18.706 0.081-38.199 0.081-43.282 0-5.056-2.267-11.493-5.056-14.254zM409.6 2.13v170.67l-68.27-68.27v-51.2c0-9.39-7.671-17.070-17.070-17.070h-238.93c-9.39 0-17.070 7.671-17.070 17.070v238.93c0 9.39 7.671 17.070 17.070 17.070h51.2l68.27 68.27h-170.67c-18.761 0-34.13-15.351-34.13-34.13v-341.34c0-18.761 15.369-34.13 34.13-34.13h341.33c18.789 0 34.13 15.369 34.13 34.13z" />
<glyph unicode="&#x26;" d="M456.303 246.63l-50.739 50.74c-12.467 12.467-37.018 22.63-54.631 22.63h-128c-17.613 0-32-14.413-32-32v-288c0-17.587 14.413-32 32-32h224c17.613 0 32 14.413 32 32v192c0 17.613-10.189 42.163-22.63 54.63zM452.437 189.26c-1.51-0.154-42.726-3.661-69.325-3.661-22.016 0-28.928 2.406-32.512 6.016-7.27 7.245-8.038 50.918-2.33 101.99 0.461 4.070 3.866 7.168 7.936 7.27h0.128c4.070 0 7.501-2.97 8.064-7.014 3.61-25.421 10.112-69.888 14.003-74.573 5.069-3.84 45.363-10.24 74.163-13.824 4.096-0.538 7.168-4.045 7.142-8.192-0.077-4.122-3.226-7.552-7.322-7.962zM158.933 128h-128c-17.587 0-32 14.413-32 32v288c0 17.613 14.413 32 32 32h128c17.613 0 42.163-10.189 54.63-22.63l50.74-50.74c12.467-12.441 22.63-37.017 22.63-54.63h-21.248c1.229 1.408 2.022 3.226 2.048 5.222 0.026 4.173-3.046 7.654-7.142 8.192-28.8 3.584-69.12 9.984-74.163 13.85-3.891 4.659-10.394 49.152-14.003 74.573-0.614 4.045-3.994 7.014-8.064 7.014h-0.128c-4.070-0.102-7.501-3.2-7.936-7.27-5.658-51.072-4.89-94.746 2.33-101.99 2.739-2.739 7.603-4.762 19.61-5.606-11.291-6.631-19.304-18.151-19.304-31.822v-186.163z" horiz-adv-x="477" />
<glyph unicode="&#x27;" d="M99.383 278.534c-7.521-8.289-6.836-22.673 1.453-30.201 8.289-7.521 22.673-6.822 30.201 1.467l92.34 92.34h24.624l42.51 42.51h-76.226c-5.535 0-11.022-2.262-14.952-6.151l-98.505-98.505c-0.505-0.464-1.003-0.962-1.467-1.467zM121.62 192.387c-42.51-42.51-53.040-75.167-53.359-98.505-0.318-23.338 10.697-38.836 14.952-43.091s19.767-15.263 43.091-14.952c23.338 0.318 55.981 10.842 98.505 53.359l59.503 59.503c-22.964 7.687-43.61 23.088-61.855 41.334s-33.578 38.85-41.334 61.855zM218.367 289.134c-3.937-7.113-3.404-14.032-0.291-23.746 4.352-13.63 16.46-31.121 32.547-47.194 16.086-16.086 33.674-28.264 47.194-32.547 10.143-3.21 17.159-3.542 24.624 1.169l116.39 116.39c0.754 15.471 0.097 28.686-1.467 39.576-13.042 6.137-24.341 16.128-35.183 26.97-10.745 10.745-20.757 22.251-26.97 35.183-10.87 1.55-24.133 2.207-39.576 1.467zM396.896 426.62c-0.055-0.277-0.263-0.602-0.291-0.879-0.249-2.020-0.221-4.698 1.467-8.496 3.376-7.618 12.371-18.231 22.867-28.727 10.503-10.503 20.895-19.235 28.444-22.57 3.778-1.661 6.794-2.020 8.801-1.757l34 34c20.425 18.889 9.936 38.725-6.739 55.4-16.682 16.682-36.227 26.866-55.116 6.448l-33.425-33.425zM11.983 5.936c-5.355-7.299-4.241-18.584 2.442-24.694 6.684-6.109 18.024-6.22 24.818-0.221l41.043 41.043c-6.684 3.722-11.707 7.604-14.952 10.842-3.252 3.252-7.355 8.469-11.146 15.249l-41.043-41.043c-0.415-0.374-0.796-0.768-1.169-1.169z" />
<glyph unicode="&#x28;" d="M254.059 418.977c-48.178 57.25-84.69 61.023-158.059 61.023v-256c128.267 64 142.636-8.335 223.506-1.023 79.728 7.22 147.525 68.587 192.494 129.023-127.356-29.453-191.46-12.023-257.941 66.977zM0 480h64v-512h-64z" />
<glyph unicode="&#x29;" d="M506.824 81.338l-142.569 142.597 142.569 142.597c6.73 6.703 6.73 17.622 0 24.315l-83.987 84.015c-3.267 3.24-7.568 5.027-12.176 5.027s-8.909-1.787-12.148-5.027l-142.587-142.616-142.569 142.56c-3.267 3.24-7.568 5.027-12.176 5.027s-8.909-1.787-12.148-5.027l-83.978-83.959c-3.268-3.268-5.055-7.596-5.055-12.149 0-4.608 1.787-8.937 5.055-12.176l142.531-142.615-142.531-142.56c-3.268-3.267-5.055-7.596-5.055-12.176 0-4.552 1.787-8.909 5.055-12.148l83.986-83.959c3.268-3.267 7.569-5.055 12.149-5.055 4.599 0 8.871 1.788 12.167 5.055l142.569 142.597 142.615-142.597c3.239-3.267 7.54-5.055 12.092-5.055 4.608 0 8.937 1.787 12.176 5.055l84.015 83.959c3.267 3.24 5.055 7.624 5.055 12.148 0 4.608-1.787 8.937-5.055 12.148z" />
<glyph unicode="&#x2a;" d="M394.071 71.572h-299.794v299.794h119.918l59.959 89.938h-269.815v-479.67h479.67v179.876l-29.979-29.979zM124.257 131.531c0 0 29.979 59.959 119.918 89.938 89.938 29.979 89.938 0 89.938 0v-119.918l179.876 179.876-179.876 179.876v-89.938c-44.61-6.445-89.639-29.679-119.918-59.958-59.959-59.959-89.938-179.876-89.938-179.876z" />
<glyph unicode="&#x2b;" d="M511.974 330.214c-7.245 94.438-66.637 143.206-118.733 148.557-8.115 0.794-15.923 1.229-23.219 1.229-57.292 0-86.528-26.368-114.022-59.546-27.494 33.178-56.781 59.546-114.048 59.546-7.27 0-15.104-0.435-23.219-1.254-52.096-5.351-111.463-54.119-118.733-149.172l0.026-30.413c7.68-103.065 89.932-210.918 251.494-329.702 1.331-0.947 2.918-1.459 4.48-1.459s3.149 0.512 4.454 1.459c161.588 118.784 243.84 226.637 251.546 330.317l-0.026 30.438zM448 299.827c-5.53-72.243-70.118-158.131-192-253.465-121.882 95.385-186.522 181.964-192.154 257.536l0.154 25.6c4.582 58.010 39.142 83.328 61.261 85.606 5.734 0.563 11.418 0.896 16.717 0.896 27.827 0 40.55-7.142 64.742-36.378l49.28-59.545 49.306 59.545c24.192 29.236 36.864 36.378 64.716 36.378 5.325 0 10.982-0.358 16.87-0.922 22.016-2.253 56.576-28.006 61.286-89.754l-0.178-25.497z" />
<glyph unicode="&#x2c;" d="M512 299.776c-7.706-103.68-89.958-211.533-251.546-330.317-1.305-0.947-2.892-1.459-4.454-1.459s-3.149 0.512-4.48 1.459c-161.562 118.784-243.814 226.637-251.494 329.677l-0.026 30.438c7.27 95.104 66.637 143.846 118.733 149.171 8.141 0.794 15.974 1.254 23.27 1.254 57.216 0.001 86.503-26.367 113.997-59.545 27.494 33.178 56.73 59.546 114.022 59.546 7.322 0 15.13-0.461 23.27-1.254 52.096-5.35 111.488-54.118 118.733-148.557l-0.025-30.413z" />
<glyph unicode="&#x2d;" d="M64 448h32v-32h-32v32zM128 448h32v-32h-32v32zM192 448h256v-32h-256v32zM480 384v-328c0-28-28-56-56-56h-336c-28 0-56 28-56 56v328h448zM0 416v-384c0-32 32-64 64-64h384c32 0 64 32 64 64v384c0 32-32 64-64 64h-384c-32 0-64-32-64-64zM64 352v-256c0-32 32-64 64-64h256c32 0 64 32 64 64v256h-384z" />
<glyph unicode="&#x2e;" d="M64 448h32v-32h-32v32zM128 448h32v-32h-32v32zM192 448h256v-32h-256v32zM480 384v-328c0-28-28-56-56-56h-336c-28 0-56 28-56 56v328h448zM0 416v-384c0-32 32-64 64-64h384c32 0 64 32 64 64v384c0 32-32 64-64 64h-384c-32 0-64-32-64-64z" />
<glyph unicode="&#x2f;" d="M372.977 326.348l-235.521 0.023 117.776-204.807z" />
<glyph unicode="&#x33;" d="M224 288h192v-32h-192zM224 224h192v-32h-192zM224 160h192v-32h-192zM224 96h128v-32h-128zM480 384h-96v64c0 17.6-14.4 32-32 32h-320c-17.6 0-32-14.4-32-32v-352c0-17.6 14.4-32 32-32h96v-64c0-17.6 14.4-32 32-32h320c17.6 0 32 14.4 32 32v352c0 17.6-14.4 32-32 32zM128 352v-240h-80v320h288v-48h-176c-17.6 0-32-14.4-32-32zM464 16h-288v320h288v-320z" />
<glyph unicode="&#x35;" d="M96 288l-96-96 192-192 320 320-96 96-224-224z" />
<glyph unicode="&#x36;" d="M480 89.632l-134.496 134.368 134.496 134.368-89.632 89.632-134.368-134.432-134.432 134.432-89.568-89.632 134.368-134.368-134.368-134.368 89.568-89.632 134.432 134.432 134.368-134.432z" />
<glyph unicode="&#x37;" d="M256 416c105.872 0 192-86.128 192-192s-86.128-192-192-192-192 86.128-192 192 86.128 192 192 192zM256 480c-141.376 0-256-114.624-256-256s114.624-256 256-256 256 114.624 256 256-114.624 256-256 256v0zM342.752 182.752l-54.752 54.816v114.432h-64.368v-127.872c0-9.632 4.432-17.936 11.12-23.872l62.752-62.752 45.248 45.248z" />
<glyph unicode="&#x38;" d="M313.392 223.984l97.536-97.536c6.336-6.336 6.336-16.624 0-22.96l-34.416-34.416c-6.336-6.336-16.608-6.336-22.96 0l-97.552 97.536-97.552-97.536c-6.336-6.336-16.608-6.336-22.944 0l-34.432 34.416c-6.336 6.336-6.336 16.608 0 22.96l97.552 97.536-97.552 97.552c-6.336 6.336-6.336 16.624 0 22.96l34.448 34.416c6.336 6.336 16.608 6.336 22.944 0l97.536-97.552 97.552 97.552c6.336 6.336 16.608 6.336 22.96 0l34.416-34.432c6.336-6.336 6.336-16.608 0-22.96l-97.536-97.536z" />
<glyph unicode="&#x39;" d="M547.86 380.465l-308.776-409.61c-3.091-3.091-7.099-4.677-11.136-4.677-4.066 0-8.133 1.586-11.195 4.677l-212.096 214.666c-6.223 6.243-6.223 16.384-0.059 22.656l101.573 102.863c2.974 3.033 6.981 4.687 11.225 4.687h0.325c4.302-0.089 8.369-1.91 11.254-5.15l101.514-111.616 278.36 279.188c2.974 2.471 6.538 3.682 10.102 3.682 4.126 0 8.222-1.595 11.284-4.746l18.59-70.42c1.654-8.635 5.189-20.007-1.004-26.191z" horiz-adv-x="551" />
<glyph unicode="&#x3a;" d="M463.968 343.504l-34.144 34.144c-6.304 6.288-16.48 6.288-22.768 0l-204.72-204.72-97.376 98.080c-6.288 6.288-16.464 6.288-22.768 0l-34.144-34.144c-6.288-6.304-6.288-16.48 0-22.768l142.784-143.744c6.288-6.288 16.48-6.288 22.768 0l250.368 250.384c6.288 6.272 6.288 16.48 0 22.768z" />
<glyph unicode="&#x3c;" d="M415.389 314.069l-320.346 0.032 160.195-278.57z" />
<glyph unicode="&#x3d;" d="M431.84 182.16c34.496-25.712 64.064-49.248 64.064-49.248l-42.096-73.824-76.080 33.584-70.496-41.232c-4.768-43.472-9.136-83.44-9.136-83.44h-84.192l-9.344 83.696-69.472 41.328-76.912-33.952-42.080 73.824c0 0 29.072 23.424 63.488 49.056v84.144l-63.488 48.96 42.080 73.84 75.072-33.408 74.528 40.432 6.128 84.080h84.192l6.096-83.728 73.504-41.248 76.128 33.888 42.096-73.84-64.064-49.136v-83.776zM256 288c-35.344 0-64-28.656-64-64s28.656-64 64-64 64 28.656 64 64-28.656 64-64 64z" />
<glyph unicode="&#x3e;" d="M409.6 480h-307.2c-56.311 0-102.4-46.071-102.4-102.4v-307.2c0-56.311 46.089-102.4 102.4-102.4h307.2c56.329 0 102.4 46.089 102.4 102.4v307.2c0 56.329-46.089 102.4-102.4 102.4zM443.73 94.263c0-37.513-20.48-57.993-57.993-57.993h-259.447c-37.54 0-58.020 20.48-58.020 57.993v259.438c0 37.55 20.48 58.030 58.030 58.030h259.438c37.512-0.001 57.992-20.481 57.992-58.021v-259.438z" />
<glyph unicode="&#x3f;" d="M232 283.858l-21.275 23.378c-12.645 13.897-26.103 27.685-27.657 28.014-1.527 0.384-2.816 0.658-2.871 0.658-0.027 0-3.931-0.713-5.184-1.527-1.307-0.85-13.111-12.507-26.322-25.829l-19.228-19.565c-13.221-13.349-27.173-30.857-26.048-33.829 1.17-3.008 12.864-16.384 26.048-29.733l92.434-93.475c13.184-13.385 26.103-25.701 28.727-27.42 0 0 0 0 2.706 0 2.679 0 2.679 0 2.679 0 2.624 1.719 13.952 15.369 25.289 30.391l163.264 216.622c11.301 14.994 21.467 29.687 22.61 32.631 1.115 2.981-4.645 20.535-7.863 34.075-3.246 13.486-8 25.938-10.624 27.685 0 0 0 0-2.734 0-2.322 0-2.322 0-2.322 0-2.405-1.335-15.186-13.321-28.453-26.597l-136.099-136.521c-13.248-13.275-34.405-12.809-47.077 1.088zM494.93 242.569l-51.2-51.227v-100.462c0-37.55-17.060-54.61-54.61-54.61h-262.83c-37.54 0-58.020 20.48-58.020 57.993v259.438c0 37.55 20.48 58.030 58.030 58.030h97.051l51.2 51.227c6.254 6.199 11.41 12.151 15.077 17.042h-187.228c-56.311 0-102.4-46.071-102.4-102.4v-307.2c0-56.311 46.089-102.4 102.4-102.4h307.2c56.329 0 102.4 46.089 102.4 102.4v187.246c-4.891-3.712-10.871-8.878-17.070-15.077z" />
<glyph unicode="&#xe000;" d="M376.732 477.107c-65.379 0-118.563-53.184-118.563-118.563v-84.688h-203.25c-18.631 0-33.875-15.244-33.875-33.875v-237.125c0-18.631 15.244-33.875 33.875-33.875h304.876c18.631 0 33.875 15.244 33.875 33.875v237.125c0 18.631-15.244 33.875-33.875 33.875h-33.875v84.688c0 28.015 22.798 50.813 50.813 50.813s50.813-22.798 50.813-50.813v-118.563h67.75v118.563c0 65.379-53.184 118.563-118.563 118.563z" />
<glyph unicode="&#xe001;" d="M425.727 273.356h-33.989v67.977c0 74.979-60.976 135.955-135.955 135.955s-135.955-60.976-135.955-135.955v-67.977h-33.989c-18.694 0-33.989-15.295-33.989-33.989v-237.921c0-18.694 15.295-33.989 33.989-33.989h339.887c18.694 0 33.989 15.295 33.989 33.989v237.921c0 18.694-15.295 33.989-33.989 33.989zM187.806 341.333c0 37.49 30.488 67.977 67.977 67.977s67.977-30.488 67.977-67.977v-67.977h-135.955v67.977z" />
<glyph unicode="&#xe002;" d="M316.533-7.412h-256.319c-31.413 0-56.96 25.546-56.96 56.96v256.319c0 31.413 25.546 56.96 56.96 56.96h256.319c31.413 0 56.96-25.546 56.96-56.96v-256.319c0-31.413-25.546-56.96-56.96-56.96zM60.214 305.866v-256.319h256.319v256.319h-256.319zM430.453 78.027h-28.48v56.96h28.48v284.799h-284.799v-28.48h-56.96v28.48c0 31.413 25.546 56.96 56.96 56.96h284.799c31.413 0 56.96-25.546 56.96-56.96v-284.799c0-31.413-25.546-56.96-56.96-56.96z" />
<glyph unicode="&#xe003;" d="M460.013 428.771h-409.592c-28.16 0-51.2-23.040-51.2-51.2v-307.204c0-28.16 23.040-51.2 51.2-51.2h409.592c28.16 0 51.2 23.040 51.2 51.2v307.204c0 28.16-23.040 51.2-51.2 51.2zM116.981 380.896c10.609 0 19.2-8.36 19.2-18.684s-8.591-18.684-19.2-18.684c-10.613 0-19.2 8.36-19.2 18.684s8.587 18.684 19.2 18.684zM46.581 362.211c0 10.324 8.587 18.684 19.2 18.684 10.609 0 19.2-8.36 19.2-18.684s-8.591-18.684-19.2-18.684c-10.613 0-19.2 8.361-19.2 18.684zM460.525 70.368h-410.616v235.523h410.616v-235.523zM460.525 346.851h-307.705v30.72h307.704l0.001-30.72z" />
<glyph unicode="&#xe004;" d="M2.534 314.301l203.011 166.523v-85.213c42.342 0 305.227-1.776 305.227-210.018 0-112.095-98.74-203.764-226.445-218.417 79.46 13.764 139.225 73.281 139.225 144.6 0 136.238-156.516 137.385-218.007 137.385v-100.125l-203.011 165.265z" />
<glyph unicode="&#xe005;" d="M107.789-33.841v3.368h-6.737v3.368h-3.368v3.368h-3.368v6.737h-3.368v10.105h-3.368v16.842h3.368v20.211h3.368v20.211h3.368v20.211h3.368v16.842h3.368v20.211h3.368v20.211h3.368v23.579h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v3.368h-3.368v6.737h-3.368v30.316h3.368v6.737h3.368v3.368h3.368v3.368h6.737v3.368h6.737v3.368h20.211v3.368h23.579v3.368h23.579v3.368h23.579v3.368h23.579v3.368h23.579v3.368h10.105v3.368h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v6.737h3.368v3.368h3.368v3.368h6.737v3.368h33.684v-3.368h6.737v-3.368h3.368v-3.368h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-6.737h3.368v-3.368h10.105v-3.368h23.579v-3.368h23.579v-3.368h23.579v-3.368h23.579v-3.368h23.579v-3.368h20.211v-3.368h6.737v-3.368h6.737v-3.368h3.368v-3.368h3.368v-6.737h3.368v-30.316h-3.368v-6.737h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-3.368h-3.368v-23.579h3.368v-20.211h3.368v-20.211h3.368v-16.842h3.368v-20.211h3.368v-20.211h3.368v-20.211h3.368v-16.842h-3.368v-10.105h-3.368v-6.737h-3.368v-3.368h-3.368v-3.368h-6.737v-3.368h-30.316v3.368h-10.105v3.368h-3.368v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-3.368v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-6.737v3.368h-13.474v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-3.368v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-6.737v-3.368h-3.368v-3.368h-10.105v-3.368z" horiz-adv-x="538" />
<glyph unicode="&#xe006;" d="M537.223 287.479c-4.015 12.369-14.552 21.208-27.405 23.121l-145.004 21.073-64.835 131.368c-5.767 11.749-17.435 18.998-30.477 18.998-13.069 0-24.711-7.249-30.451-18.971l-64.89-131.422-145.031-21.073c-12.881-1.886-23.39-10.752-27.432-23.175-4.069-12.396-0.755-25.735 8.569-34.789l104.933-102.292-24.765-144.411c-2.182-12.854 2.965-25.627 13.501-33.307 5.875-4.231 12.773-6.44 19.995-6.44 5.524 0 10.995 1.347 15.818 3.907l129.725 68.15 129.697-68.15c4.824-2.56 10.294-3.907 15.818-3.907 7.249 0 14.147 2.237 19.941 6.494 10.617 7.653 15.791 20.372 13.555 33.199l-24.792 144.438 104.933 102.265c9.324 9.135 12.638 22.474 8.623 34.897zM369.421 167.94l23.526-137.593-123.473 64.943-123.554-64.943 23.579 137.593-99.948 97.415 138.159 20.103 61.764 125.17 61.79-125.17 138.132-20.103-99.975-97.415z" horiz-adv-x="538" />
<glyph unicode="&#xe007;" d="M512 224c0 141.376-114.624 256-256 256s-256-114.624-256-256c0-141.376 114.624-256 256-256s256 114.624 256 256zM128 300h256l-128-208.399z" />
<glyph unicode="&#xe008;" d="M256 480c-141.385 0-256-114.615-256-256s114.615-256 256-256c141.385 0 256 114.615 256 256s-114.615 256-256 256zM256 32c-106.039 0-192 85.961-192 192s85.961 192 192 192c106.039 0 192-85.961 192-192s-85.961-192-192-192zM385.189 304.853l-258.378 0.024 129.207-209.144z" />
<glyph unicode="&#xe009;" d="M496.471 245.726c0 0 15.585-9.143 15.582-21.915 0-12.787-15.582-21.928-15.582-21.928l-438.3-226.518c-31.944-17.469-58.076-4.604-58.076 28.576v439.733c0 33.199 26.134 46.066 58.076 28.581l438.3-226.529z" />
<glyph unicode="&#xe00a;" d="M15.677 245.726l438.3 226.529c31.942 17.485 58.076 4.618 58.076-28.581v-439.733c0-33.18-26.132-46.045-58.077-28.576l-438.299 226.518c0 0-15.582 9.141-15.582 21.928-0.002 12.772 15.582 21.915 15.582 21.915z" />
<glyph unicode="&#xe00b;" d="M415.879 479.807h-319.759c-52.76 0-95.928-43.957-95.928-97.683v-195.367c0-53.726 43.167-97.683 95.928-97.683h31.976c0 0 0.016-109.245 0-115.529s6.865-7.766 11.245-3.265c4.381 4.502 116.659 118.794 116.659 118.794h159.879c52.76 0 95.928 43.957 95.928 97.683v195.367c0 53.726-43.167 97.683-95.928 97.683z" />
<glyph unicode="&#xe00c;" d="M256.633 475.034c-139.836 0-253.201-92.18-253.201-205.913 0-74.774 49.018-140.266 122.324-176.343 0-0.312 0.016-108.399 0-114.636-0.016-6.246 6.911-7.787 11.319-3.313 3.353 3.404 57.819 58.218 89.291 89.87 9.928-0.962 20.021-1.574 30.267-1.574 139.836 0 253.201 92.262 253.201 205.996s-113.365 205.913-253.201 205.913z" />
<glyph unicode="&#xe00d;" d="M255.667 479c-141.208 0-255.667-96.040-255.667-214.499 0-118.461 114.459-214.501 255.667-214.501 2.205 0 4.389 0.037 6.584 0.083 49.52-28.035 124.397-70.395 129.248-73.166 14.298-8.162 36.802-5.314 36.752 6.083-0.032 6.707-0.011 65.749 0 123.251 51.069 39.216 83.082 95.589 83.082 158.248 0 118.461-114.458 214.501-255.666 214.501z" />
<glyph unicode="&#xe00e;" d="M255.667 479c141.207 0 255.666-96.041 255.666-214.5s-114.459-214.5-255.666-214.5c-2.207 0-4.391 0.037-6.584 0.083-49.52-28.034-124.396-70.395-129.25-73.166-14.297-8.164-36.802-5.313-36.75 6.083 0.031 6.706 0.010 65.748 0 123.25-51.071 39.216-83.083 95.591-83.083 158.25 0 118.459 114.46 214.5 255.667 214.5z" />
<glyph unicode="&#xe00f;" d="M429.912 223.968c0 26.817 16.508 47.98 41.368 62.532-4.476 14.968-10.412 29.304-17.712 42.8-27.872-7.292-50.432 3.632-69.4 22.593-18.964 18.968-24.769 41.532-17.473 69.408-13.496 7.3-27.832 13.232-42.807 17.708-14.548-24.864-40.828-41.368-67.649-41.368-26.817 0-53.096 16.504-67.653 41.368-14.968-4.469-29.304-10.408-42.8-17.708 7.292-27.872 1.492-50.436-17.477-69.408-18.964-18.964-41.532-29.883-69.405-22.593-7.3-13.496-13.236-27.832-17.708-42.8 24.86-14.552 41.364-35.716 41.364-62.532 0-26.811-16.504-53.104-41.364-67.653 4.472-14.972 10.408-29.304 17.708-42.8 27.876 7.292 50.44 1.492 69.405-17.48 18.959-18.959 24.769-41.532 17.477-69.4 13.496-7.3 27.832-13.24 42.804-17.708 14.556 24.856 40.832 41.368 67.649 41.364 26.821 0 53.1-16.508 67.649-41.364 14.976 4.472 29.312 10.408 42.812 17.712-7.292 27.864-1.496 50.436 17.467 69.395 18.968 18.972 41.532 29.893 69.4 22.6 7.3 13.496 13.236 27.828 17.712 42.8-24.859 14.551-41.367 35.722-41.367 62.534zM256.24 130.447c-51.649 0-93.516 41.867-93.516 93.52s41.867 93.52 93.516 93.52 93.512-41.867 93.512-93.52-41.864-93.52-93.512-93.52z" />
<glyph unicode="&#xe010;" d="M96.121 479.807h319.759c52.76 0 95.928-43.957 95.928-97.683v-195.367c0-53.726-43.167-97.683-95.928-97.683h-31.976c0 0-0.016-109.245 0-115.529s-6.865-7.766-11.245-3.265c-4.38 4.501-116.658 118.793-116.658 118.793h-159.879c-52.76 0-95.928 43.957-95.928 97.683v195.367c0 53.726 43.167 97.683 95.928 97.683z" />
<glyph unicode="&#xe011;" d="M464 480h-416c-26.4 0-48-21.6-48-48v-320c0-26.4 21.6-48 48-48h112l96-96 96 96h112c26.4 0 48 21.6 48 48v320c0 26.4-21.599 48-48 48z" />
<glyph unicode="&#xe012;" d="M496 288h-176v176c0 8.832-7.152 16-16 16h-96c-8.848 0-16-7.168-16-16v-176h-176c-8.848 0-16-7.168-16-16v-96c0-8.848 7.152-16 16-16h176v-176c0-8.848 7.152-16 16-16h96c8.848 0 16 7.152 16 16v176h176c8.848 0 16 7.152 16 16v96c0 8.832-7.152 16-16 16z" />
<glyph unicode="&#xe013;" d="M433.6 450.328h-355.2c-32.56 0-59.2-29.6-59.2-65.856v-230.32c0-36.072 26.64-65.88 59.2-65.88h118.4l118.4-98.608v98.608l118.4-0.008c32.576 0 59.2 29.784 59.2 65.88v230.32c0 36.264-26.64 65.864-59.2 65.864zM356.304 255.264l-80.936-0.296-0.296-85.84-38.144 0.296-0.296 85.264h-80.936l0.296 36.128 80.64 0.296 0.296 84.696h38.44v-84.696h80.936v-35.848z" />
<glyph unicode="&#xe014;" d="M270.157 17.888c17.838 15.489 35.811 30.823 53.676 46.282 35.15 0.236 72.983-0.451 108.058 0.357 14.278 2.251 20.313 17.795 18.42 30.81-0.206 115.128 0.392 212.429-0.316 327.499-1.257 14.685-16.786 22.413-30.232 20.178-104.666-0.205-214.548 0.409-319.155-0.307-14.701-1.358-22.131-17.047-19.972-30.434 0.158-115.136-0.336-212.413 0.23-327.511 0.774-14.271 15.474-22.971 28.728-20.58 31.648-0.004 65.857-0.008 97.505-0.012 19.462-16.487 38.69-33.947 58.29-49.84 1.829 0.029 3.201 2.67 4.768 3.558zM258.074-26.061c-20.193 17.477-40.478 34.846-60.637 52.361-34.806 0.603-82.603-0.39-117.29 1.204-23.796 3.779-39.973 27.658-37.59 51.036 0.082 120.682 0.163 241.363 0.244 362.044 3.15 19.595 18.68 36.052 38.516 39.023 113.879 0.538 253.486 0.077 367.414 0.231 19.3-2.84 35.423-18.036 38.868-37.301 1.265-44.667 0.236-89.641 0.62-134.426-0.124-80.038 0.465-160.148-0.106-240.141-2.055-24.097-25.106-43.424-49.181-41.13-30.879-0.18-74.559-0.359-105.438-0.539-22.611-19.533-45.139-39.168-67.953-58.461-2.649 1.831-5.013 4.027-7.467 6.1zM363.816 114.875c-17.021 3.709-23.158 28.113-9.857 39.385 12.271 13.447 37.881 4.353 38.868-13.889 2.562-16.098-13.296-30.147-29.011-25.496zM142.712 252.763c0 46.367 0 92.734 0 139.102 11.401-0.082 22.803-0.164 34.204-0.247 0.155-39.611 0.31-79.221 0.465-118.832 18.022 33.815 66.903 45.414 97.586 21.984 23.33-17.454 28.833-48.987 27.361-76.565 0.006-34.769 0.012-69.539 0.017-104.308-11.329-0.082-22.657-0.164-33.986-0.247-0.301 39.783 0.6 79.643-0.45 119.377-0.603 19.801-12.766 42.184-34.115 44.635-29.367 6.483-59.522-21.462-56.414-50.973-0.084-37.676-0.168-75.352-0.253-113.028-11.472 0-22.945 0-34.417 0 0 46.367-0.001 92.735 0 139.102z" />
<glyph unicode="&#xe015;" d="M146.288 224c0 58.912 49.12 106.672 109.712 106.672s109.712-47.76 109.712-106.672-49.12-106.672-109.712-106.672-109.712 47.76-109.712 106.672zM0 220.16c47.408-92.736 143.392-156.16 254.112-156.16 113.728 0 211.952 66.96 257.888 163.84-47.408 92.736-143.408 156.16-254.112 156.16-113.728 0-211.936-66.96-257.888-163.84zM201.136 224c0-29.456 24.56-53.328 54.864-53.328s54.864 23.872 54.864 53.328-24.56 53.328-54.864 53.328-54.864-23.872-54.864-53.328z" />
<glyph unicode="&#xe016;" d="M256 384c-141.392 0-256-160-256-160s114.608-160 256-160 256 160 256 160-114.608 160-256 160zM256 128c-53.008 0-96 42.992-96 96s42.992 96 96 96 96-42.992 96-96-42.992-96-96-96zM256 288c-35.344 0-64-28.656-64-64s28.656-64 64-64 64 28.656 64 64-28.656 64-64 64z" />
<glyph unicode="&#xe017;" d="M256 368c-128 0-255.999-111.999-256-144v0c0.001-32.001 128-144 256-144s255.997 111.999 256 144v0c-0.003 32.001-128 144-256 144zM256 128c-53.021 0-96 42.98-96 96s42.979 96 96 96c53.020 0 96-42.98 96-96s-42.98-96-96-96zM224 224c0-17.673 14.327-32 32-32s32 14.327 32 32c0 17.673-14.327 32-32 32s-32-14.327-32-32z" />
<glyph unicode="&#xe018;" d="M448 128v-64h-384v64h-64v-128h512v128zM384 288h-96v160h-64v-160h-96l128-192z" />
<glyph unicode="&#xe019;" d="M512 331.245l-65.965-105.658-223.366 162.291 77.299 92.122h-69.549l-61.978-73.773c-11.565-2.509-22.304-8.832-29.485-18.63l-36.736-50.598c-7.782-10.515-11.085-23.757-8.979-36.781 1.766-11.552 7.392-21.67 16.006-29.37 8.602-12.096 47.014-72.838 6.080-129.075-0.006-0.006-0.013-0.013-0.019-0.019l-90.303-124.378c-5.741-8.128-6.611-18.624-2.214-27.597 4.397-8.979 13.453-14.624 23.411-14.701l127.373-1.722c1.613 0.006 3.232 0.058 4.627 0.384 6.912 1.184 13.088 5.024 17.325 10.714l46.342 63.731c0.25 0.32 0.557 0.563 0.794 0.896 32.973 45.702 111.014 34.451 129.235 31.040 20.813-11.258 49.139-5.152 63.418 14.522 0.019 0.026 0.038 0.038 0.058 0.064 0.006 0.006 0.013 0.019 0.019 0.032 0.013 0.019 0.032 0.038 0.051 0.064l36.717 50.541c7.61 10.483 10.918 23.213 9.082 35.968l30.757 49.299v100.634zM160.557 26.848l-62.182 0.858 44.474 61.267 41.062-30.054zM217.568 100.365l-45.357 33.005c33.837 69.197 1.786 141.331-22.426 172.435-0.819 0.915-1.658 1.811-2.541 2.682l32.467 44.877 247.169-179.63-18.893-25.99-143.315 104.717c-11.77 8.89-28.557 6.067-37.184-5.69-8.685-11.77-6.093-28.544 5.677-37.19l99.258-72.518c-35.495 0.102-81.012-6.81-114.855-36.698z" />
<glyph unicode="&#xe01a;" d="M512 331.245l-65.965-105.658-223.366 162.291 77.299 92.122h-69.549l-61.978-73.773c-11.565-2.509-22.304-8.832-29.485-18.63l-36.736-50.598c-7.782-10.515-11.085-23.757-8.979-36.781 1.766-11.552 7.392-21.67 16.006-29.37 8.602-12.096 47.014-72.838 6.080-129.075-0.006-0.006-0.013-0.013-0.019-0.019l-90.303-124.378c-5.741-8.128-6.611-18.624-2.214-27.597 4.397-8.979 13.453-14.624 23.411-14.701l127.373-1.722c1.613 0.006 3.232 0.058 4.627 0.384 6.912 1.184 13.088 5.024 17.325 10.714l46.342 63.731c0.25 0.32 0.557 0.563 0.794 0.896 32.973 45.702 111.014 34.451 129.235 31.040 20.813-11.258 49.139-5.152 63.418 14.522 0.019 0.026 0.038 0.038 0.058 0.064 0.006 0.006 0.013 0.019 0.019 0.032 0.013 0.019 0.032 0.038 0.051 0.064l36.717 50.541c7.61 10.483 10.918 23.213 9.082 35.968l30.757 49.299v100.634zM160.557 26.848l-62.182 0.858 44.474 61.267 41.062-30.054zM332.429 137.062c-35.507 0.102-81.024-6.81-114.861-36.698l-45.357 33.005c33.837 69.197 1.786 141.331-22.426 172.435-0.819 0.915-1.658 1.811-2.541 2.682l32.467 44.877 247.169-179.629-18.893-25.99z" />
<glyph unicode="&#xe01b;" d="M256 368c-128 0-255.999-111.999-256-144v0c0.001-32.001 128-144 256-144s255.997 111.999 256 144v0c-0.003 32.001-128 144-256 144zM256 128c-53.021 0-96 42.98-96 96s42.979 96 96 96c53.020 0 96-42.98 96-96s-42.98-96-96-96z" />
<glyph unicode="&#xe01c;" d="M512 331.245l-65.965-105.658-223.366 162.291 77.299 92.122h-69.549l-61.978-73.773c-11.565-2.509-22.304-8.832-29.485-18.63l-36.736-50.598c-7.782-10.515-11.085-23.757-8.979-36.781 1.766-11.552 7.392-21.67 16.006-29.37 8.602-12.096 47.014-72.838 6.080-129.075-0.006-0.006-0.013-0.013-0.019-0.019l-90.303-124.378c-5.741-8.128-6.611-18.624-2.214-27.597 4.397-8.979 13.453-14.624 23.411-14.701l127.373-1.722c1.613 0.006 3.232 0.058 4.627 0.384 6.912 1.184 13.088 5.024 17.325 10.714l46.342 63.731c0.25 0.32 0.557 0.563 0.794 0.896 32.973 45.702 111.014 34.451 129.235 31.040 20.813-11.258 49.139-5.152 63.418 14.522 0.019 0.026 0.038 0.038 0.058 0.064 0.006 0.006 0.013 0.019 0.019 0.032 0.013 0.019 0.032 0.038 0.051 0.064l36.717 50.541c7.61 10.483 10.918 23.213 9.082 35.968l30.757 49.299v100.634zM217.568 100.365l-45.357 33.005c33.837 69.197 1.786 141.331-22.426 172.435-0.819 0.915-1.658 1.811-2.541 2.682l32.467 44.877 247.169-179.63-18.893-25.99-143.315 104.717c-11.77 8.89-28.557 6.067-37.184-5.69-8.685-11.77-6.093-28.544 5.677-37.19l99.258-72.518c-35.495 0.102-81.012-6.81-114.855-36.698z" />
<glyph unicode="&#xe01d;" d="M512 331.245l-65.965-105.658-223.366 162.291 77.299 92.122h-69.549l-61.978-73.773c-11.565-2.509-22.304-8.832-29.485-18.63l-36.736-50.598c-7.782-10.515-11.085-23.757-8.979-36.781 1.766-11.552 7.392-21.67 16.006-29.37 8.602-12.096 47.014-72.838 6.080-129.075-0.006-0.006-0.013-0.013-0.019-0.019l-90.303-124.378c-5.741-8.128-6.611-18.624-2.214-27.597 4.397-8.979 13.453-14.624 23.411-14.701l127.373-1.722c1.613 0.006 3.232 0.058 4.627 0.384 6.912 1.184 13.088 5.024 17.325 10.714l46.342 63.731c0.25 0.32 0.557 0.563 0.794 0.896 32.973 45.702 111.014 34.451 129.235 31.040 20.813-11.258 49.139-5.152 63.418 14.522 0.019 0.026 0.038 0.038 0.058 0.064 0.006 0.006 0.013 0.019 0.019 0.032 0.013 0.019 0.032 0.038 0.051 0.064l36.717 50.541c7.61 10.483 10.918 23.213 9.082 35.968l30.757 49.299v100.634zM332.429 137.062c-35.507 0.102-81.024-6.81-114.861-36.698l-45.357 33.005c33.837 69.197 1.786 141.331-22.426 172.435-0.819 0.915-1.658 1.811-2.541 2.682l32.467 44.877 247.169-179.629-18.893-25.99z" />
<glyph unicode="&#xe01e;" d="M78.4 450.328h355.2c32.56 0 59.2-29.6 59.2-65.856v-230.32c0-36.072-26.64-65.88-59.2-65.88h-118.4l-118.4-98.608v98.608l-118.4-0.008c-32.576 0-59.2 29.784-59.2 65.88v230.32c0 36.264 26.64 65.864 59.2 65.864zM155.696 255.264l80.936-0.296 0.296-85.84 38.144 0.296 0.296 85.264h80.936l-0.296 36.128-80.64 0.296-0.296 84.696h-38.44v-84.696h-80.936v-35.848z" />
<glyph unicode="&#xe020;" d="M438.955 342.955l-59.499-59.52-53.333 10.667-10.667 53.333 59.499 59.52c-8.747 2.645-17.877 4.48-27.499 4.48-52.992 0-96-42.987-96-96v-32l-146.709-119.040c-27.883-27.627-23.787-67.797 0-91.669 23.787-23.915 63.787-27.733 91.669 0l119.040 146.709h32c53.035 0 96 42.987 96 96 0 9.642-1.835 18.752-4.501 27.52z" />
<glyph unicode="&#xe600;" d="M451.764 480h-391.529c-33.25 0-60.236-29.425-60.236-65.716v-230.009c0-36.262 26.985-65.687 60.236-65.687h45.177v-131.764l131.764 131.764h214.588c33.25 0 60.236 29.425 60.236 65.716v229.978c0 36.292-26.986 65.716-60.236 65.716zM361.412 288h-90.353v-90.353h-30.118v90.353h-90.353v30.118h90.353v90.353h30.118v-90.353h90.353v-30.118z" />
<glyph unicode="&#xe601;" d="M373.974 447.353l107.923-107.908-215.837-215.868-107.923 107.908 215.837 215.868zM234.466 92.024l-130.289-22.377 22.347 130.289zM0 85.971h180.706v-60.236h-180.706v60.236z" />
<glyph unicode="&#xe602;" d="M0 242.402c280.455-331.294 512 0 512 0-280.455 331.294-512 0-512 0zM256 149.549c-51.291 0-92.853 41.563-92.853 92.853s41.563 92.853 92.853 92.853 92.853-41.563 92.853-92.853c0-51.291-41.563-92.853-92.853-92.853z" />
<glyph unicode="&#xe603;" d="M367.497 77.313c-9.476 1.494-9.692 27.327-9.692 27.327s27.844 27.328 33.912 64.076c16.326 0 26.407 39.069 10.082 52.814 0.681 14.47 20.984 113.588-81.799 113.588s-82.479-99.118-81.799-113.588c-16.327-13.745-6.244-52.814 10.081-52.814 6.067-36.748 33.913-64.076 33.913-64.076s-0.216-25.833-9.692-27.327c-30.524-4.816-144.503-54.658-144.503-109.313h384c0 54.655-113.979 104.497-144.503 109.313zM172.027 68.595c22.047 13.575 48.813 26.154 70.769 33.712-7.876 11.216-16.647 26.468-22.165 44.531-7.703 6.283-13.972 15.266-17.999 26.301-4.033 11.052-5.561 23.426-4.304 34.842 0.902 8.196 3.239 15.833 6.825 22.544-2.175 23.293-3.707 69.017 26.224 102.366 11.607 12.933 26.278 22.23 43.85 27.843-3.137 32.38-19.58 70.385-83.227 70.385-102.782 0-82.479-99.118-81.799-113.588-16.327-13.745-6.244-52.814 10.081-52.814 6.067-36.748 33.913-64.076 33.913-64.076s-0.216-25.833-9.692-27.327c-30.524-4.817-144.503-54.659-144.503-109.314h164.798c2.355 1.537 4.753 3.070 7.229 4.595z" />
<glyph unicode="&#xe604;" d="M311.413 128.632c-11.055 1.759-11.307 32.157-11.307 32.157s32.484 32.158 39.564 75.401c19.045 0 30.809 45.973 11.761 62.148 0.795 17.027 24.48 133.662-95.431 133.662s-96.225-116.635-95.432-133.662c-19.047-16.175-7.285-62.148 11.761-62.148 7.079-43.243 39.564-75.401 39.564-75.401s-0.252-30.398-11.307-32.157c-35.61-5.666-168.586-64.317-168.586-128.632h448c0 64.315-132.976 122.966-168.587 128.632z" />
<glyph unicode="&#xe605;" d="M512 265.372c0 100.463-29.396 181.969-65.741 182.613 0.146 0.003 0.289 0.015 0.436 0.015h-41.471c0 0-97.4-73.168-237.615-101.877-4.28-22.646-7.015-49.637-7.015-80.751s2.733-58.104 7.015-80.75c140.214-28.709 237.615-101.878 237.615-101.878h41.471c-0.146 0-0.289 0.012-0.436 0.016 36.348 0.644 65.741 82.149 65.741 182.612zM432.412 110.374c-4.691 0-9.766 4.871-12.373 7.774-6.315 7.032-12.396 17.98-17.594 31.664-11.628 30.616-18.033 71.655-18.033 115.562 0 43.905 6.405 84.945 18.033 115.561 5.197 13.684 11.281 24.633 17.594 31.664 2.607 2.906 7.682 7.776 12.373 7.776s9.768-4.872 12.372-7.776c6.317-7.032 12.398-17.979 17.594-31.664 11.629-30.615 18.034-71.656 18.034-115.561 0-43.902-6.405-84.944-18.034-115.562-5.195-13.684-11.281-24.632-17.594-31.664-2.604-2.903-7.68-7.774-12.372-7.774zM125.906 265.372c0 25.975 1.905 51.215 5.526 74.547-23.686-3.277-44.471-5.162-70.17-5.162-33.529 0-33.529 0-33.529 0l-27.733-47.343v-44.085l27.73-47.343c0 0 0 0 33.53 0 25.699 0 46.484-1.887 70.17-5.162-3.618 23.332-5.524 48.573-5.524 74.548zM184.075 158.914l-63.999 12.255 40.921-160.772c2.118-8.317 10.372-12.519 18.343-9.327l59.278 23.726c7.972 3.188 11.164 11.982 7.098 19.542l-61.641 114.576zM432.412 205.635c-1.809 0-3.764 1.877-4.769 2.996-2.435 2.71-4.778 6.93-6.781 12.204-4.481 11.8-6.95 27.617-6.95 44.539s2.469 32.739 6.95 44.539c2.003 5.274 4.348 9.494 6.781 12.204 1.005 1.12 2.96 2.997 4.769 2.997 1.808 0 3.765-1.878 4.769-2.997 2.435-2.71 4.778-6.929 6.78-12.204 4.482-11.799 6.951-27.617 6.951-44.539s-2.469-32.739-6.951-44.539c-2.002-5.274-4.348-9.494-6.78-12.204-1.004-1.119-2.96-2.996-4.769-2.996z" />
<glyph unicode="&#xe606;" d="M544 29.417c0-22.75 13.014-42.454 32-52.092v-7.969c-5.313-0.727-10.736-1.112-16.25-1.112-34.004 0-64.674 14.264-86.361 37.132-13.111-3.491-27.001-5.376-41.389-5.376-79.529 0-144 57.308-144 128s64.471 128 144 128c79.529 0 144-57.308 144-128 0-27.674-9.882-53.296-26.678-74.233-3.412-7.412-5.322-15.656-5.322-24.35zM115.339 369.407c-33.107-26.899-51.339-61.492-51.339-97.407 0-20.149 5.594-39.689 16.626-58.075 11.376-18.96 28.491-36.293 49.494-50.126 15.178-9.996 25.39-25.974 28.088-43.947 0.9-5.992 1.464-12.044 1.685-18.062 3.735 3.097 7.375 6.423 10.94 9.988 12.077 12.076 28.39 18.745 45.251 18.745 2.684 0 5.381-0.168 8.078-0.512 10.474-1.331 21.172-2.008 31.797-2.010v-64c-13.564 0.001-26.877 0.869-39.871 2.521-54.989-54.989-120.625-64.85-184.088-66.298v13.458c34.268 16.789 64 47.37 64 82.318 0 4.877-0.379 9.665-1.082 14.348-57.898 38.132-94.918 96.377-94.918 161.652 0 114.875 114.615 208 256 208 139.229 0 252.496-90.307 255.918-202.76-20.548 9.158-42.92 14.711-66.131 16.289-5.765 28.034-22.701 54.408-49.126 75.878-17.661 14.349-38.458 25.695-61.814 33.722-24.853 8.54-51.38 12.871-78.847 12.871s-53.994-4.331-78.847-12.871c-23.356-8.027-44.153-19.372-61.814-33.722z" horiz-adv-x="576" />
<glyph unicode="&#xe607;" d="M240 480v0c132.548 0 240-86.957 240-194.224s-107.452-194.224-240-194.224c-12.729 0-25.223 0.81-37.417 2.355-51.553-51.347-111.086-60.554-170.583-61.907v12.567c32.126 15.677 58 44.233 58 76.867 0 4.553-0.356 9.024-1.015 13.397-54.279 35.607-88.985 89.994-88.985 150.945 0 107.267 107.452 194.224 240 194.224zM498 44.657c0-27.971 18.157-52.449 46-65.886v-10.771c-51.563 1.159-98.893 9.051-143.571 53.063-10.57-1.325-21.397-2.020-32.429-2.020-47.735 0-91.704 12.879-126.807 34.52 72.337 0.253 140.63 23.427 192.417 65.336 26.104 21.126 46.697 45.913 61.207 73.674 15.383 29.433 23.183 60.791 23.183 93.203 0 5.224-0.225 10.418-0.629 15.584 36.285-29.967 58.629-70.811 58.629-115.838 0-52.244-30.079-98.861-77.12-129.382-0.571-3.748-0.88-7.58-0.88-11.483z" horiz-adv-x="576" />
<glyph unicode="&#xe608;" d="M512.75 80c0 144-128 112-128 224 0 9.28-0.894 17.21-2.524 23.964-8.415 56.509-46.078 101.86-94.886 115.68 0.433 1.974 0.66 4.016 0.66 6.105 0 16.639-14.4 30.251-32 30.251s-32-13.612-32-30.25c0-2.090 0.228-4.132 0.66-6.105-54.735-15.499-95.457-70.649-96.627-136.721-0.020-0.96-0.033-1.932-0.033-2.923 0-112.001-128-80.001-128-224.001 0-38.113 85.295-69.998 199.485-78.040 10.762-20.202 32.028-33.96 56.515-33.96s45.754 13.758 56.515 33.96c114.19 8.042 199.485 39.927 199.485 78.040 0 0.114-0.013 0.228-0.014 0.341l0.764-0.341zM413.123 52.952c-27.115-7.235-59.079-12.438-93.384-15.324-2.852 32.709-30.291 58.372-63.739 58.372s-60.887-25.663-63.739-58.372c-34.304 2.886-66.269 8.089-93.384 15.324-37.315 9.957-55.155 21.095-61.684 27.048 6.529 5.953 24.369 17.091 61.684 27.048 43.386 11.576 99.186 17.952 157.123 17.952s113.737-6.376 157.123-17.952c37.315-9.957 55.155-21.095 61.684-27.048-6.529-5.953-24.369-17.091-61.684-27.048z" />
<glyph unicode="&#xe609;" d="M437.020 405.019c48.351-48.352 74.98-112.64 74.98-181.020s-26.629-132.667-74.98-181.019c-48.353-48.351-112.64-74.98-181.020-74.98s-132.667 26.629-181.020 74.98c-48.351 48.352-74.98 112.639-74.98 181.019s26.627 132.668 74.98 181.020c48.353 48.352 112.64 74.981 181.020 74.981s132.667-26.629 181.020-74.981zM414.392 65.607c31.529 31.53 52.493 70.804 61.136 113.531-6.737-9.918-13.181-13.597-17.172 8.603-4.11 36.195-37.354 13.074-58.258 25.93-22.002-14.829-71.453 28.831-63.049-20.412 12.967-22.211 70.004 29.725 41.574-17.271-18.137-32.808-66.321-105.466-60.053-143.129 0.791-54.872-56.067-11.442-75.657 6.76-13.178 36.46-4.491 100.187-38.949 118.043-37.401 1.624-69.502 5.023-83.997 46.835-8.723 29.914 9.282 74.447 41.339 81.322 46.925 29.483 63.687-34.527 107.695-35.717 13.664 14.297 50.908 18.843 53.996 34.875-28.875 5.095 36.634 24.279-2.764 35.191-21.735-2.556-35.739-22.537-24.184-39.479-42.119-9.821-43.468 60.952-83.955 38.629-1.029-35.295-66.111-11.443-22.518-4.286 14.978 6.544-24.43 25.508-3.14 22.062 10.458 0.568 45.666 12.906 36.138 21.201 19.605 12.17 36.080-29.145 55.269 0.941 13.854 23.133-5.81 27.404-23.175 15.678-9.79 10.962 17.285 34.638 41.166 44.869 7.959 3.41 15.561 5.268 21.373 4.742 12.029-13.896 34.275-16.303 35.439 1.671-29.791 14.267-62.638 21.804-96.646 21.804-48.811 0-95.235-15.512-133.654-44.195 10.325-4.73 16.186-10.619 6.239-18.148-7.728-23.027-39.085-53.938-66.612-49.562-14.293-24.648-23.706-51.803-27.73-80.264 23.056-7.628 28.372-22.725 23.418-27.775-11.748-10.244-18.968-24.765-22.688-40.661 7.505-45.918 29.086-88.238 62.635-121.788 42.308-42.307 98.559-65.607 158.392-65.607s116.084 23.3 158.392 65.607z" />
<glyph unicode="&#xe60a;" d="M476.698 442.679l-2.014 2.021c-47.074 47.067-124.097 47.067-171.163 0l-109.053-109.068c-47.067-47.066-47.067-124.088 0-171.155l2.013-2.013c3.916-3.924 8.073-7.462 12.368-10.729l39.924 39.925c-4.651 2.747-9.063 6.036-13.058 10.030l-2.021 2.021c-25.557 25.549-25.557 67.136 0 92.695l109.064 109.056c25.558 25.559 67.137 25.559 92.693 0l2.021-2.012c25.55-25.558 25.55-67.146 0-92.695l-49.343-49.343c8.566-21.154 12.624-43.7 12.269-66.193l76.302 76.302c47.067 47.068 47.067 124.089-0.002 171.158zM315.521 285.533c-3.916 3.916-8.073 7.461-12.368 10.72l-39.924-39.916c4.652-2.748 9.063-6.037 13.058-10.031l2.021-2.020c25.558-25.558 25.558-67.136 0-92.694l-109.065-109.067c-25.559-25.551-67.138-25.551-92.694 0l-2.021 2.021c-25.549 25.56-25.549 67.138 0 92.694l49.344 49.343c-8.567 21.153-12.623 43.701-12.269 66.193l-76.301-76.299c-47.068-47.066-47.068-124.089 0-171.162l2.013-2.016c47.076-47.064 124.096-47.064 171.164 0l109.055 109.059c47.067 47.066 47.067 124.097 0 171.163l-2.013 2.012z" />
<glyph unicode="&#xe60b;" d="M421.647 480h-361.412c-33.13 0-60.236-30.118-60.236-66.259v-228.894c0-36.141 27.106-66.259 60.236-66.259h30.118v-150.588l144.565 150.588h186.73c33.13 0 60.236 30.118 60.236 66.259v228.894c0 36.141-27.106 66.259-60.236 66.259zM361.412 269.177h-90.353v-90.353h-60.236v90.353h-90.353v60.236h90.353v90.353h60.236v-90.353h90.353v-60.236z" />
<glyph unicode="&#xe60c;" d="M480 64l-124.563 124.563c17.781 29.031 28.563 62.859 28.563 99.438 0 106.015-86 192-192 192-106.016 0-192-85.985-192-192 0-106 85.984-192 192-192 36.578 0 70.406 10.781 99.438 28.5l124.563-124.5c8.75-8.75 23.25-8.688 32 0l32 32c8.75 8.75 8.75 23.25 0 32zM192 160c-70.688 0-128 57.313-128 128s57.313 128 128 128 128-57.313 128-128-57.313-128-128-128z" horiz-adv-x="487" />
<glyph unicode="&#xf000;" d="M0 480l512-512h-512z" />
<glyph unicode="&#xf06e;" d="M475.429 205.714q-43.429 67.429-108.857 100.857 17.429-29.714 17.429-64.286 0-52.857-37.571-90.429t-90.429-37.571-90.429 37.571-37.571 90.429q0 34.571 17.429 64.286-65.429-33.429-108.857-100.857 38-58.571 95.286-93.286t124.143-34.714 124.143 34.714 95.286 93.286zM269.714 315.429q0 5.714-4 9.714t-9.714 4q-35.714 0-61.286-25.571t-25.571-61.286q0-5.714 4-9.714t9.714-4 9.714 4 4 9.714q0 24.571 17.429 42t42 17.429q5.714 0 9.714 4t4 9.714zM512 205.714q0-9.714-5.714-19.714-40-65.714-107.571-105.286t-142.714-39.571-142.714 39.714-107.571 105.143q-5.714 10-5.714 19.714t5.714 19.714q40 65.429 107.571 105.143t142.714 39.714 142.714-39.714 107.571-105.143q5.714-10 5.714-19.714z" />
<glyph unicode="&#xf070;" d="M158.571 98.571l22.286 40.286q-24.857 18-38.857 45.429t-14 58q0 34.571 17.429 64.286-65.429-33.429-108.857-100.857 47.714-73.714 122-107.143zM269.714 315.429q0 5.714-4 9.714t-9.714 4q-35.714 0-61.286-25.571t-25.571-61.286q0-5.714 4-9.714t9.714-4 9.714 4 4 9.714q0 24.571 17.429 42t42 17.429q5.714 0 9.714 4t4 9.714zM373.429 370q0-2-0.286-2.571-30-53.714-90-161.714t-90.286-162l-14-25.429q-2.857-4.571-8-4.571-3.429 0-38.286 20-4.571 2.857-4.571 8 0 3.429 12.571 24.857-40.857 18.571-75.286 49.429t-59.571 70q-5.714 8.857-5.714 19.714t5.714 19.714q43.714 67.143 108.571 106t141.714 38.857q25.429 0 51.429-4.857l15.429 27.714q2.857 4.571 8 4.571 1.429 0 5.143-1.714t8.857-4.429 9.429-5.286 9-5.286 5.571-3.286q4.571-2.857 4.571-7.714zM384 242.286q0-39.714-22.571-72.429t-59.714-47l80 143.429q2.286-12.857 2.286-24zM512 205.714q0-10-5.714-19.714-11.143-18.286-31.143-41.429-42.857-49.143-99.286-76.286t-119.857-27.143l21.143 37.714q60.571 5.143 112.143 39.143t86.143 87.714q-32.857 51.143-80.571 84l18 32q27.143-18.286 52.143-43.714t41.286-52.571q5.714-9.714 5.714-19.714z" />
<glyph unicode="&#xf0d0;" d="M340 314l83.714 83.714-30.571 30.571-83.714-83.714zM467.714 397.714q0-7.714-5.143-12.857l-367.429-367.429q-5.143-5.143-12.857-5.143t-12.857 5.143l-56.571 56.571q-5.143 5.143-5.143 12.857t5.143 12.857l367.429 367.429q5.143 5.143 12.857 5.143t12.857-5.143l56.571-56.571q5.143-5.143 5.143-12.857zM81.714 452l28-8.571-28-8.571-8.571-28-8.571 28-28 8.571 28 8.571 8.571 28zM181.714 405.714l56-17.143-56-17.143-17.143-56-17.143 56-56 17.143 56 17.143 17.143 56zM447.429 269.143l28-8.571-28-8.571-8.571-28-8.571 28-28 8.571 28 8.571 8.571 28zM264.571 452l28-8.571-28-8.571-8.571-28-8.571 28-28 8.571 28 8.571 8.571 28z" horiz-adv-x="475" />
<glyph unicode="&#xf0fe;" d="M365.714 205.714v36.571q0 7.429-5.429 12.857t-12.857 5.429h-91.429v91.429q0 7.429-5.429 12.857t-12.857 5.429h-36.571q-7.429 0-12.857-5.429t-5.429-12.857v-91.429h-91.429q-7.429 0-12.857-5.429t-5.429-12.857v-36.571q0-7.429 5.429-12.857t12.857-5.429h91.429v-91.429q0-7.429 5.429-12.857t12.857-5.429h36.571q7.429 0 12.857 5.429t5.429 12.857v91.429h91.429q7.429 0 12.857 5.429t5.429 12.857zM438.857 361.143v-274.286q0-34-24.143-58.143t-58.143-24.143h-274.286q-34 0-58.143 24.143t-24.143 58.143v274.286q0 34 24.143 58.143t58.143 24.143h274.286q34 0 58.143-24.143t24.143-58.143z" horiz-adv-x="438" />
</font></defs></svg>
\ No newline at end of file
No preview for this file type
No preview for this file type
......@@ -21,7 +21,9 @@
"code": 46,
"name": "window",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 0
},
{
"icon": {
......@@ -44,7 +46,9 @@
"code": 47,
"name": "triangle",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 1
},
{
"icon": {
......@@ -66,7 +70,9 @@
"code": 51,
"name": "copy",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 2
},
{
"icon": {
......@@ -88,7 +94,9 @@
"code": 54,
"name": "x",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 3
},
{
"icon": {
......@@ -111,7 +119,9 @@
"code": 56,
"name": "cancel",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 4
},
{
"icon": {
......@@ -132,7 +142,9 @@
"code": 57,
"name": "checkmark_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 5
},
{
"icon": {
......@@ -152,7 +164,9 @@
"code": 63,
"name": "checkbox_checked_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 6
},
{
"icon": {
......@@ -172,7 +186,9 @@
"code": 60,
"name": "triangle2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 7
},
{
"icon": {
......@@ -192,7 +208,9 @@
"code": 57344,
"name": "unlocked",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 8
},
{
"icon": {
......@@ -212,7 +230,9 @@
"code": 57345,
"name": "locked",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 9
},
{
"icon": {
......@@ -232,7 +252,9 @@
"code": 57346,
"name": "views",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 10
},
{
"icon": {
......@@ -253,7 +275,9 @@
"code": 57347,
"name": "browser",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 11
},
{
"icon": {
......@@ -273,7 +297,9 @@
"code": 57354,
"name": "trileft",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 12
},
{
"icon": {
......@@ -293,7 +319,9 @@
"code": 57355,
"name": "comment",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 13
},
{
"icon": {
......@@ -313,7 +341,9 @@
"code": 57356,
"name": "comment2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 14
},
{
"icon": {
......@@ -333,7 +363,9 @@
"code": 57358,
"name": "comment22",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 15
},
{
"icon": {
......@@ -353,7 +385,9 @@
"code": 57357,
"name": "comment23",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 16
},
{
"icon": {
......@@ -375,7 +409,9 @@
"code": 57359,
"name": "settings",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 17
},
{
"icon": {
......@@ -397,7 +433,9 @@
"code": 57362,
"name": "plus",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 18
},
{
"icon": {
......@@ -420,7 +458,9 @@
"code": 61694,
"name": "plus-sign",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 19
},
{
"icon": {
......@@ -441,7 +481,9 @@
"code": 57366,
"name": "eye",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 20
},
{
"icon": {
......@@ -464,7 +506,9 @@
"code": 61648,
"name": "magic",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 21
},
{
"icon": {
......@@ -486,7 +530,9 @@
"code": 57367,
"name": "eye2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 22
},
{
"icon": {
......@@ -507,7 +553,9 @@
"code": 57368,
"name": "download",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 23
},
{
"icon": {
......@@ -527,7 +575,9 @@
"code": 57363,
"name": "comment24",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 24
},
{
"icon": {
......@@ -547,7 +597,9 @@
"code": 57374,
"name": "comment25",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 25
},
{
"icon": {
......@@ -567,7 +619,9 @@
"code": 39,
"name": "pen_1",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 26
},
{
"icon": {
......@@ -587,7 +641,9 @@
"code": 40,
"name": "flag",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 27
},
{
"icon": {
......@@ -607,7 +663,9 @@
"code": 41,
"name": "delete_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 28
},
{
"icon": {
......@@ -627,7 +685,9 @@
"code": 43,
"name": "heart_empty_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 29
},
{
"icon": {
......@@ -647,7 +707,9 @@
"code": 44,
"name": "heart_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 30
},
{
"icon": {
......@@ -669,7 +731,9 @@
"code": 45,
"name": "window2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 31
},
{
"icon": {
......@@ -689,7 +753,9 @@
"code": 42,
"name": "export",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 32
},
{
"icon": {
......@@ -709,7 +775,9 @@
"code": 62,
"name": "checkbox_unchecked_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 33
},
{
"icon": {
......@@ -730,7 +798,9 @@
"code": 57350,
"name": "star_fav_empty_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 34
},
{
"icon": {
......@@ -750,7 +820,9 @@
"code": 57353,
"name": "triright",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 35
},
{
"icon": {
......@@ -772,7 +844,9 @@
"code": 61550,
"name": "eye-open",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 36
},
{
"icon": {
......@@ -796,7 +870,9 @@
"code": 57361,
"name": "comments",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 37
},
{
"icon": {
......@@ -816,7 +892,9 @@
"code": 57372,
"name": "highlighter-blacktip",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 38
},
{
"icon": {
......@@ -836,7 +914,9 @@
"code": 57369,
"name": "highlighter-01",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 39
},
{
"icon": {
......@@ -856,7 +936,9 @@
"code": 57370,
"name": "highlighter-nolines",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 40
},
{
"icon": {
......@@ -877,7 +959,9 @@
"code": 57365,
"name": "eye3",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 41
},
{
"icon": {
......@@ -897,7 +981,9 @@
"code": 57373,
"name": "highlighter",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 42
},
{
"icon": {
......@@ -917,7 +1003,9 @@
"code": 57371,
"name": "eye4",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 43
},
{
"icon": {
......@@ -938,7 +1026,9 @@
"code": 57376,
"name": "wrench",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 44
},
{
"icon": {
......@@ -958,7 +1048,9 @@
"code": 57360,
"name": "commentflip",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 45
},
{
"icon": {
......@@ -978,7 +1070,9 @@
"code": 57364,
"name": "hyp-logo4",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 46
},
{
"icon": {
......@@ -1000,7 +1094,9 @@
"code": 61552,
"name": "eye-close",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 47
},
{
"icon": {
......@@ -1021,7 +1117,9 @@
"code": 57349,
"name": "star_fav_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 48
},
{
"icon": {
......@@ -1041,7 +1139,9 @@
"code": 57348,
"name": "reply",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 49
},
{
"icon": {
......@@ -1061,7 +1161,9 @@
"code": 57352,
"name": "ddbutton_empty",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 50
},
{
"icon": {
......@@ -1081,7 +1183,9 @@
"code": 57351,
"name": "ddbutton_filled",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 51
},
{
"icon": {
......@@ -1104,7 +1208,9 @@
"code": 58,
"name": "checkmark",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 52
},
{
"icon": {
......@@ -1129,7 +1235,9 @@
"code": 61,
"name": "cog",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 53
},
{
"icon": {
......@@ -1150,7 +1258,9 @@
"code": 53,
"name": "checkmark2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 54
},
{
"icon": {
......@@ -1171,7 +1281,9 @@
"code": 55,
"name": "clock",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 55
},
{
"icon": {
......@@ -1192,7 +1304,9 @@
"code": 38,
"name": "clipboard_copy_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 56
},
{
"icon": {
......@@ -1212,7 +1326,9 @@
"code": 37,
"name": "export_icon",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 57
},
{
"icon": {
......@@ -1230,7 +1346,9 @@
"code": 36,
"name": "uni24",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 58
},
{
"icon": {
......@@ -1248,7 +1366,9 @@
"code": 35,
"name": "uni23",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 59
},
{
"icon": {
......@@ -1274,14 +1394,15 @@
"code": 34,
"name": "cog2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 60
},
{
"icon": {
"paths": [
"M0 0l1024 1024h-1024z"
],
"width": 0,
"defaultCode": 61440,
"grid": 0
},
......@@ -1292,17 +1413,19 @@
"code": 61440,
"name": "uniF000",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 61
},
{
"icon": {
"paths": [
"M903.529 0h-783.059c-66.5 0-120.471 58.85-120.471 131.433v460.017c0 72.523 53.971 131.373 120.471 131.373h90.353v263.529l263.529-263.529h429.176c66.5 0 120.471-58.85 120.471-131.433v-459.957c0-72.584-53.971-131.433-120.471-131.433zM722.824 384h-180.706v180.706h-60.235v-180.706h-180.706v-60.235h180.706v-180.706h60.235v180.706h180.706v60.235z"
],
"grid": 0,
"tags": [
"comment"
]
],
"grid": 0
},
"properties": {
"order": 63,
......@@ -1311,7 +1434,9 @@
"code": 58880,
"name": "comment3",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 62
},
{
"icon": {
......@@ -1320,10 +1445,10 @@
"M468.932 775.951l-260.578 44.755 44.695-260.578z",
"M0 788.058h361.412v120.471h-361.412v-120.471z"
],
"grid": 0,
"tags": [
"highlighter"
]
],
"grid": 0
},
"properties": {
"order": 64,
......@@ -1332,17 +1457,19 @@
"code": 58881,
"name": "highlighter2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 63
},
{
"icon": {
"paths": [
"M0 475.196c560.911 662.588 1024 0 1024 0-560.911-662.588-1024 0-1024 0zM512 660.902c-102.581 0-185.705-83.125-185.705-185.705 0-102.581 83.125-185.705 185.705-185.705s185.705 83.125 185.705 185.705c0 102.581-83.125 185.705-185.705 185.705z"
],
"grid": 0,
"tags": [
"visible"
]
],
"grid": 0
},
"properties": {
"order": 65,
......@@ -1351,7 +1478,9 @@
"code": 58882,
"name": "visible",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 64
},
{
"icon": {
......@@ -1375,7 +1504,9 @@
"code": 58883,
"name": "users",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 65
},
{
"icon": {
......@@ -1399,7 +1530,9 @@
"code": 58884,
"name": "user",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 66
},
{
"icon": {
......@@ -1422,7 +1555,9 @@
"code": 58885,
"name": "bullhorn",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 67
},
{
"icon": {
......@@ -1445,7 +1580,9 @@
"code": 58886,
"name": "bubbles",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 68
},
{
"icon": {
......@@ -1468,7 +1605,9 @@
"code": 58887,
"name": "bubbles2",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 69
},
{
"icon": {
......@@ -1489,7 +1628,9 @@
"code": 58888,
"name": "bell",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 70
},
{
"icon": {
......@@ -1513,7 +1654,9 @@
"code": 58889,
"name": "earth",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 71
},
{
"icon": {
......@@ -1536,17 +1679,19 @@
"code": 58890,
"name": "link",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 72
},
{
"icon": {
"paths": [
"M843.294 0h-722.824c-66.259 0-120.471 60.235-120.471 132.518v457.788c0 72.282 54.212 132.518 120.471 132.518h60.235v301.176l289.129-301.176h373.459c66.259 0 120.471-60.235 120.471-132.518v-457.788c0-72.282-54.212-132.518-120.471-132.518zM722.824 421.647h-180.706v180.706h-120.471v-180.706h-180.706v-120.471h180.706v-180.706h120.471v180.706h180.706v120.471z"
],
"grid": 0,
"tags": [
"icon-comment"
]
],
"grid": 0
},
"properties": {
"order": 74,
......@@ -1555,7 +1700,28 @@
"code": 58891,
"name": "icon-comment",
"ligatures": ""
}
},
"setIdx": 1,
"iconIdx": 73
},
{
"icon": {
"paths": [
"M960 832l-249.125-249.125c35.563-58.063 57.125-125.719 57.125-198.875 0-212.031-172-384-384-384-212.031 0-384 171.969-384 384 0 212 171.969 384 384 384 73.156 0 140.812-21.562 198.875-57l249.125 249c17.5 17.5 46.5 17.375 64 0l64-64c17.5-17.5 17.5-46.5 0-64zM384 640c-141.375 0-256-114.625-256-256s114.625-256 256-256 256 114.625 256 256-114.625 256-256 256z"
],
"width": 973,
"grid": 16
},
"properties": {
"order": 75,
"id": 197,
"prevSize": 32,
"code": 58892,
"name": "search",
"ligatures": ""
},
"setIdx": 1,
"iconIdx": 74
}
],
"height": 1024,
......@@ -1567,22 +1733,38 @@
"prefix": "icon-",
"metadata": {
"fontFamily": "h",
"majorVersion": 0,
"minorVersion": 0
"majorVersion": 1,
"minorVersion": 1
},
"showGlyphs": true,
"metrics": {
"emSize": 1024,
"emSize": 512,
"baseline": 6.25,
"whitespace": 50
},
"resetPoint": 58880
"resetPoint": 58880,
"showQuickUse": true,
"quickUsageToken": false,
"showMetrics": true,
"showMetadata": true,
"postfix": "",
"showVersion": true
},
"imagePref": {
"color": 0,
"height": 32,
"columns": 16,
"margin": 16,
"png": false,
"sprites": true
},
"imagePref": {},
"historySize": 100,
"showCodes": true,
"gridSize": 16,
"showLiga": false,
"showGrid": true,
"showGlyphs": true,
"showQuickUse": true
"showQuickUse": true,
"search": ""
}
}
\ No newline at end of file
@font-face {
font-family: 'h';
src:url('fonts/h.eot');
src:url('fonts/h.eot?#iefix') format('embedded-opentype'),
url('fonts/h.woff') format('woff'),
url('fonts/h.ttf') format('truetype'),
url('fonts/h.svg#h') format('svg');
src:url('fonts/h.eot?jsvc9t');
src:url('fonts/h.eot?#iefixjsvc9t') format('embedded-opentype'),
url('fonts/h.woff?jsvc9t') format('woff'),
url('fonts/h.ttf?jsvc9t') format('truetype'),
url('fonts/h.svg?jsvc9t#h') format('svg');
font-weight: normal;
font-style: normal;
}
......@@ -245,3 +245,6 @@
.icon-link:before {
content: "\e60a";
}
.icon-search:before {
content: "\e60c";
}
......@@ -2,16 +2,9 @@ imports = [
'bootstrap'
'h.helpers'
'h.socket'
'h.streamfilter'
'h.searchfilters'
]
SEARCH_FACETS = ['text', 'tags', 'uri', 'quote', 'since', 'user', 'results']
SEARCH_VALUES =
group: ['Public', 'Private'],
since: ['5 min', '30 min', '1 hour', '12 hours',
'1 day', '1 week', '1 month', '1 year']
class App
scope:
frame:
......@@ -47,8 +40,6 @@ class App
socialView: annotator.socialView
ongoingHighlightSwitch: false
search:
facets: SEARCH_FACETS
values: SEARCH_VALUES
query: $location.search()
show: not angular.equals($location.search(), {})
session: session
......@@ -228,38 +219,35 @@ class App
$rootScope.applySort "Location"
$scope.query = $location.search()
$scope.search = {}
$scope.search.update = angular.noop
$scope.search.clear = angular.noop
#$scope.show_search = Object.keys($scope.query).length > 0
$rootScope.$on '$routeChangeSuccess', (event, next, current) ->
unless next.$$route? then return
$scope.search.query = $location.search()
$scope.search.show = not angular.equals($location.search(), {})
if next.$$route.originalPath is '/viewer'
$rootScope.viewState.show = true
else
$rootScope.viewState.show = false
unless next.$$route.originalPath is '/stream'
if current and next.$$route.originalPath is '/a/:id'
$scope.reloadAnnotations()
$scope.search.update = (searchCollection) ->
return unless annotator.discardDrafts()
return unless searchCollection.models.length
models = searchCollection.models
matched = []
query =
tags: []
quote: []
for item in models
{category, value} = item.attributes
# Stuff we need to collect
switch
when category in ['text', 'user', 'time', 'group']
query[category] = value
when category == 'tags'
# Tags are specials, because we collect those into an array
query.tags.push value.toLowerCase()
when category == 'quote'
query.quote = query.quote.concat(value.split(/\s+/))
query = {query: searchCollection}
unless angular.equals $location.search(), query
if $location.path() == '/viewer'
if $location.path() == '/viewer' or $location.path() == '/page_search'
$location.path('/page_search').search(query)
else
$location.path('/stream').search(query)
......@@ -846,12 +834,18 @@ class Search
refresh()
refresh = =>
$scope.matches = viewFilter.filter $rootScope.annotations, $routeParams
[$scope.matches, $scope.filters] = viewFilter.filter $rootScope.annotations, $routeParams
# Create the regexps for highlighting the matches inside the annotations' bodies
$scope.text_tokens = $routeParams.text?.split(/\s+/) or []
$scope.text_tokens = $scope.filters.text.terms.slice()
$scope.text_regexp = []
$scope.quote_tokens = $routeParams.quote
$scope.quote_tokens = $scope.filters.quote.terms.slice()
$scope.quote_regexp = []
# Highligh any matches
for term in $scope.filters.any.terms
$scope.text_tokens.push term
$scope.quote_tokens.push term
# Saving the regexps and higlighter to the annotator for highlighttext regeneration
for token in $scope.text_tokens
regexp = new RegExp(token,"ig")
......
......@@ -326,11 +326,10 @@ username = ['$filter', '$window', ($filter, $window) ->
link: (scope, elem, attr) ->
scope.$watch 'user', ->
scope.uname = $filter('persona')(scope.user, 'username')
scope.provider = $filter('persona')(scope.user, 'provider')
scope.uclick = (event) ->
event.preventDefault()
$window.open "/u/#{scope.uname}@#{scope.provider}"
$window.open "/u/#{scope.uname}"
return
scope:
......@@ -399,44 +398,34 @@ fuzzytime = ['$filter', '$window', ($filter, $window) ->
]
visualSearch = ['$parse', ($parse) ->
simpleSearch = ['$parse', ($parse) ->
link: (scope, elem, attr, ctrl) ->
_search = $parse(attr.onsearch)
_clear = $parse(attr.onclear)
_facets = $parse(attr.facets)
_values = $parse(attr.values)
_vs = VS.init
container: elem
callbacks:
search: (query, modelCollection) ->
scope.$apply ->
_search(scope, {"this": modelCollection})
clearSearch: (original) ->
_vs.searchBox.value('')
if attr.onclear
scope.$apply ->
_clear(scope)
else
original()
facetMatches: (callback) ->
facets = _facets(scope) or []
callback(facets or [], preserveOrder: true)
valueMatches: (facet, term, callback) ->
values = _values(scope)?[facet]
callback(values or [], preserveOrder: true)
scope.dosearch = ->
_search(scope, {"this": scope.searchtext})
scope.reset = (event) ->
event.preventDefault()
scope.searchtext = ''
_clear(scope) if attr.onclear
scope.$watch attr.query, (query) ->
p = 0
_vs.searchBox.value('')
for k, values of query
continue unless values?.length
unless angular.isArray values then values = [values]
for v in values
_vs.searchBox.addFacet(k, v, p++)
_search(scope, {"this": _vs.searchQuery})
if query.query?
scope.searchtext = query.query
_search(scope, {"this": scope.searchtext})
restrict: 'C'
template: '''
<form class="simple-search-form" ng-class="!searchtext && 'simple-search-inactive'" name="searchBox" ng-submit="dosearch()">
<input class="simple-search-input" type="text" ng-model="searchtext" name="searchText" placeholder="Search…" />
<i class="simple-search-icon icon-search"></i>
<button class="simple-search-clear" type="reset" ng-hide="!searchtext" ng-click="reset($event)">
<i class="icon-x"></i>
</button>
</form>
'''
]
whenscrolled = ['$window', ($window) ->
......@@ -462,5 +451,5 @@ angular.module('h.directives', ['ngSanitize'])
.directive('username', username)
.directive('userPicker', userPicker)
.directive('repeatAnim', repeatAnim)
.directive('visualSearch', visualSearch)
.directive('simpleSearch', simpleSearch)
.directive('whenscrolled', whenscrolled)
......@@ -4,7 +4,7 @@ imports = [
'h.directives'
'h.helpers'
'h.socket'
'h.streamfilter'
'h.searchfilters'
]
......
# This class will parse the search filter and produce a faceted search filter object
# It expects a search query string where the search term are separated by space character
# and collects them into the given term arrays
class SearchFilter
# This function will slice the search-text input
# Slice character: space,
# but an expression between quotes (' or ") is considered one
# I.e from the string: "text user:john 'to be or not to be' it will produce:
# ["text", "user:john", "to be or not to be"]
_tokenize: (searchtext) ->
return [] unless searchtext
tokens = searchtext.match /(?:[^\s"']+|"[^"]*"|'[^']*')+/g
# Cut the opening and closing quote characters
for token, index in tokens
start = token.slice 0,1
end = token.slice -1
if (start is '"' or start is "'") and (start is end)
tokens[index] = token.slice 1, token.length - 1
tokens
# This function will generate the facets from the search-text input
# It'll first tokenize it and then sorts them into facet lists
# The output will be a dict with the following structure:
# An object with facet_names as keys.
# A value for a key:
# [facet_name]:
# [operator]: 'and'|'or'|'min' (for the elements of the facet terms list)
# [lowercase]: true|false
# [terms]: an array for the matched terms for this facet
# The facet selection is done by analyzing each token.
# It generally expects a <facet_name>:<facet_term> structure for a token
# Where the facet names are: 'quote', 'result', 'since', 'tag', 'text', 'uri', 'user
# Anything that didn't match go to the 'any' facet
# For the 'since' facet the the time string is scanned and is converted to seconds
# So i.e the 'since:7min' token will be converted to 7*60 = 420 for the since facet value
generateFacetedFilter: (searchtext) ->
any = []
quote = []
result = []
since = []
tag = []
text = []
uri = []
user = []
if searchtext
terms = @_tokenize(searchtext)
for term in terms
filter = term.slice 0, term.indexOf ":"
unless filter? then filter = ""
switch filter
when 'quote' then quote.push term[6..]
when 'result' then result.push term[7..]
when 'since'
# We'll turn this into seconds
time = term[6..].toLowerCase()
if time.match /^\d+$/
# Only digits, assuming seconds
since.push time
if time.match /^\d+sec$/
# Time given in seconds
t = /^(\d+)sec$/.exec(time)[1]
since.push t
if time.match /^\d+min$/
# Time given in minutes
t = /^(\d+)min$/.exec(time)[1]
since.push t * 60
if time.match /^\d+hour$/
# Time given in hours
t = /^(\d+)hour$/.exec(time)[1]
since.push t * 60 * 60
if time.match /^\d+day$/
# Time given in days
t = /^(\d+)day$/.exec(time)[1]
since.push t * 60 * 60 * 24
if time.match /^\d+week$/
# Time given in week
t = /^(\d+)week$/.exec(time)[1]
since.push t * 60 * 60 * 24 * 7
if time.match /^\d+month$/
# Time given in month
t = /^(\d+)month$/.exec(time)[1]
since.push t * 60 * 60 * 24 * 30
if time.match /^\d+year$/
# Time given in year
t = /^(\d+)year$/.exec(time)[1]
since.push t * 60 * 60 * 24 * 365
when 'tag' then tag.push term[4..]
when 'text' then text.push term[5..]
when 'uri' then uri.push term[4..]
when 'user' then user.push term[5..]
else any.push term
any:
terms: any
operator: 'and'
lowercase: true
quote:
terms: quote
operator: 'and'
lowercase: true
result:
terms: result
operator: 'min'
lowercase: false
since:
terms: since
operator: 'and'
lowercase: false
tag:
terms: tag
operator: 'and'
lowercase: true
text:
terms: text
operator: 'and'
lowercase: true
uri:
terms: uri
operator: 'or'
lowercase: true
user:
terms: user
operator: 'or'
lowercase: true
# This class will process the results of search and generate the correct filter
# It expects the following dict format as rules
# { facet_name : {
......@@ -10,10 +140,12 @@
#
# options: backend specific options
# options.es: elasticsearch specific options
# options.es.query_type : can be: simple, query_string, match
# options.es.query_type : can be: simple, query_string, match, multi_match
# defaults to: simple, determines which es query type to use
# options.es.cutoff_frequency: if set, the query will be given a cutoff_frequency for this facet
# options.es.and_or: match queries can use this, defaults to and
# options.es.and_or: match and multi_match queries can use this, defaults to and
# options.es.match_type: multi_match query type
# options.es.fields: fields to search for in multi-match query
# }
# The models is the direct output from visualsearch
class QueryParser
......@@ -70,6 +202,18 @@ class QueryParser
case_sensitive: true
and_or: 'and'
operator: 'ge'
any:
exact_match: false
case_sensitive: false
and_or: 'and'
path: ['/quote', '/tags', '/text', '/uri', '/user']
options:
es:
query_type: 'multi_match'
match_type: 'cross_fields'
and_or: 'and'
fields: ['quote', 'tags', 'text', 'uri', 'user']
parseModels: (models) ->
# Cluster facets together
......@@ -85,13 +229,12 @@ class QueryParser
populateFilter: (filter, query) =>
# Populate a filter with a query object
for category, values of query
for category, value of query
unless @rules[category]? then continue
unless values.length then continue
terms = value.terms
unless terms.length then continue
rule = @rules[category]
unless angular.isArray values
values = [values]
# Now generate the clause with the help of the rule
exact_match = if rule.exact_match? then rule.exact_match else true
......@@ -102,7 +245,7 @@ class QueryParser
if and_or is 'or'
val_list = ''
first = true
for val in values
for val in terms
unless first then val_list += ',' else first = false
value_part = if rule.formatter then rule.formatter val else val
val_list += value_part
......@@ -114,7 +257,7 @@ class QueryParser
oper_part =
if rule.operator? then rule.operator
else if exact_match then 'equals' else 'matches'
for val in values
for val in terms
value_part = if rule.formatter then rule.formatter val else val
filter.addClause mapped_field, oper_part, value_part, case_sensitive, rule.options
......@@ -220,6 +363,7 @@ class StreamFilter
this
angular.module('h.streamfilter', [])
angular.module('h.searchfilters', [])
.service('searchfilter', SearchFilter)
.service('queryparser', QueryParser)
.service('streamfilter', StreamFilter)
imports = [
'h.filters'
'h.filters',
'h.searchfilters'
]
......@@ -674,36 +675,133 @@ class DraftProvider
class ViewFilter
# This object is the filter matching configuration used by the filter() function
checkers:
quote:
autofalse: (annotation) -> return annotation.references?
value: (annotation) ->
for target in annotation.target
return target.quote if target.quote?
''
match: (term, value) -> return value.indexOf(term) > -1
since:
autofalse: (annotation) -> return not annotation.updated?
value: (annotation) -> return annotation.updated
match: (term, value) ->
delta = Math.round((+new Date - new Date(value)) / 1000)
return delta <= term
tag:
autofalse: (annotation) -> return not annotation.tags?
value: (annotation) -> return annotation.tags
match: (term, value) -> return value in term
text:
autofalse: (annotation) -> return not annotation.text?
value: (annotation) -> return annotation.text
match: (term, value) -> return value.indexOf(term) > -1
uri:
autofalse: (annotation) -> return not annotation.uri?
value: (annotation) -> return annotation.uri
match: (term, value) -> return value is term
user:
autofalse: (annotation) -> return not annotation.user?
value: (annotation) ->
# XXX: Hopefully there is a cleaner solution
# XXX: To reach persona filter from here
return (annotation.user?.match /^acct:([^@]+)@(.+)/)?[1]
match: (term, value) -> return value is term
any:
fields: ['quote', 'text', 'tag', 'user']
this.$inject = ['searchfilter']
constructor: (searchfilter) ->
@searchfilter = searchfilter
_matches: (filter, value, match) ->
matches = true
for term in filter.terms
unless match term, value
matches = false
if filter.operator is 'and'
break
else
matches = true
if filter.operator is 'or'
break
matches
_arrayMatches: (filter, value, match) ->
matches = true
# Make copy for filtering
copy = value.slice()
copy.filter (e) ->
not match filter.terms, e
if (filter.operator is 'and' and copy.length < value.length) or
(filter.operator is 'or' and not copy.length)
matches = false
matches
_anyMatches: (filter, value, match) ->
matchresult = []
for term in filter.terms
if angular.isArray value
matchresult.push match value, term
else
matchresult.push match term, value
matchresult
_checkMatch: (filter, annotation, checker) ->
autofalsefn = checker.autofalse
return false if autofalsefn? and autofalsefn annotation
value = checker.value annotation
if angular.isArray value
if filter.lowercase then value = value.map (e) -> e.toLowerCase()
return @_arrayMatches filter, value, checker.match
else
value = value.toLowerCase() if filter.lowercase
return @_matches filter, value, checker.match
this.$inject = ['$filter']
constructor: ($filter) ->
@user_filter = $filter('persona')
# Filters a set of annotations, according to a given query.
#
# Inputs:
# annotations is the input list of annotations (array)
# query is the query; it's a map. Supported key values are:
# user: username to search for
# text: text to search for in the body (all the words must be present)
# quote: text to search for in the quote (exact phrease must be present)
# tags: list of tags to search for. (all must be present)
# time: maximum age of annotation. Accepted values:
# '5 min', '30 min', '1 hour', '12 hours',
# '1 day', '1 week', '1 month', '1 year'
# query is the query string. It will be converted to faceted filter by the SearchFilter
#
# All search is case insensitive.
# It'll handle the annotation matching by the returned facet configuration (operator, lowercase, etc.)
# and the here configured @checkers. This @checkers object contains instructions how to verify the match.
# Structure:
# [facet_name]:
# autofalse: a function for a preliminary false match result
# (i.e. if the annotation does not even have a 'text' field, do not try to match the 'text' facet)
# value: a function to extract to facet value for the annotation.
# (i.e. for the quote facet it is the annotation.target.quote from the right target from the annotations)
# match: a function to check if the extracted value matches with the facet value
# (i.e. for the text facet it has to check that if the facet is a substring of the annotation.text or not.
#
# Returns the list of matching annotation IDs.
filter: (annotations, query) ->
# Returns a two-element list:
# [
# matched annotation IDs list,
# the faceted filters
# ]
filter: (annotations, query) =>
filters = @searchfilter.generateFacetedFilter query.query
results = []
# Convert these fields to lower case, if they exist
for key in ['text', 'user']
if query[key]?
query[key] = query[key].toLowerCase()
# Check for given limit
# Find the minimal
limit = 0
if filters.result.terms.length
limit = filter.result.terms[0]
for term in filter.result.terms
if limit > term then limit = term
# We expect a list for quotes
query.quote.map (e) -> e.toLowerCase()
# Convert terms to lowercase if needed
for _, filter of filters
if filter.lowercase then filter.terms.map (e) -> e.toLowerCase()
# Now that this filter is called with the top level annotations, we have to add the children too
annotationsWithChildren = []
......@@ -716,104 +814,54 @@ class ViewFilter
for annotation in annotationsWithChildren
matches = true
for category, value of query
#ToDo: What about given zero limit?
# Limit reached
if limit and results.length >= limit then break
for category, filter of filters
break unless matches
terms = filter.terms
# No condition for this category
continue unless terms.length
switch category
when 'user'
username = @user_filter annotation.user
unless username?.toLowerCase() is value
matches = false
break
when 'text'
unless annotation.text?
matches = false
break
lowerCaseText = annotation.text.toLowerCase()
for token in value.split ' '
if lowerCaseText.indexOf(token) is -1
matches = false
break
when 'quote'
unless value.length > 0 then continue
# Reply annotations does not have a quote in this aspect
if annotation.references?
matches = false
break
else
found = false
for target in annotation.target
if target.quote?
quote = target.quote.toLowerCase()
for val in value
if quote.indexOf(val) > -1
found = true
when 'result'
# Handled above
continue
when 'any'
# Special case
matchterms = []
matchterms.push false for term in terms
for field in @checkers.any.fields
conf = @checkers[field]
continue if conf.autofalse? and conf.autofalse annotation
value = conf.value annotation
if angular.isArray value
if filter.lowercase
value = value.map (e) -> e.toLowerCase()
else
found = false
break
unless found
matches = false
break
when 'tags'
# Don't bother if we got an empty list for required tags
break unless value.length
value = value.toLowerCase() if filter.lowercase
matchresult = @_anyMatches filter, value, conf.match
matchterms = matchterms.map (t, i) -> t or matchresult[i]
# If this has no tags, this is in instant failure
if value.length and not annotation.tags?
matches = false
break
# OK, there are some tags, and we need some tags.
# Gotta check for each wanted tag
for wantedTag in value
found = false
for existingTag in annotation.tags
if existingTag.toLowerCase().indexOf(wantedTag) > -1
found = true
break
unless found
matches = false
break
# Now let's see what we got.
matched = 0
for _, value of matchterms
matched++ if value
when 'time'
delta = Math.round((+new Date - new Date(annotation.updated)) / 1000)
switch value
when '5 min'
unless delta <= 60*5
matches = false
when '30 min'
unless delta <= 60*30
matches = false
when '1 hour'
unless delta <= 60*60
matches = false
when '12 hours'
unless delta <= 60*60*12
matches = false
when '1 day'
unless delta <= 60*60*24
matches = false
when '1 week'
unless delta <= 60*60*24*7
matches = false
when '1 month'
unless delta <= 60*60*24*31
matches = false
when '1 year'
unless delta <= 60*60*24*366
matches = false
when 'group'
priv_public = 'group:__world__' in (annotation.permissions.read or [])
switch value
when 'Public'
unless priv_public
matches = false
when 'Private'
if priv_public
if (filter.operator is 'or' and matched > 0) or (filter.operator is 'and' and matched is terms.length)
matches = true
else
matches = false
else
# For all other categories
matches = @_checkMatch filter, annotation, @checkers[category]
if matches
results.push annotation.id
results
[results, filters]
angular.module('h.services', imports)
......
......@@ -6,23 +6,17 @@ imports = [
'h.flash'
'h.helpers'
'h.session'
'h.streamfilter'
'h.searchfilters'
]
SEARCH_FACETS = ['text', 'tags', 'uri', 'quote', 'since', 'user', 'results']
SEARCH_VALUES =
group: ['Public', 'Private'],
since: ['5 min', '30 min', '1 hour', '12 hours',
'1 day', '1 week', '1 month', '1 year']
class StreamSearch
this.inject = [
'$location', '$scope', '$rootScope',
'queryparser', 'session', 'streamfilter'
'queryparser', 'session', 'searchfilter', 'streamfilter'
]
constructor: (
$location, $scope, $rootScope,
queryparser, session, streamfilter
queryparser, session, searchfilter, streamfilter
) ->
# Initialize the base filter
streamfilter
......@@ -31,7 +25,9 @@ class StreamSearch
.setPastDataHits(50)
# Apply query clauses
queryparser.populateFilter streamfilter, $location.search()
$scope.query = $location.search()['query']
terms = searchfilter.generateFacetedFilter $scope.query
queryparser.populateFilter streamfilter, terms
$scope.updater?.then (sock) ->
filter = streamfilter.getFilter()
......@@ -44,11 +40,9 @@ class StreamSearch
$scope.search.query = $location.search()
$scope.search.show = not angular.equals($location.search(), {})
$scope.search.update = (searchCollection) ->
# Update the query parameters
query = queryparser.parseModels searchCollection.models
unless angular.equals $location.search(), query
$location.search query
$scope.search.update = (query) ->
unless angular.equals $location.search(query), query
$location.search {query:query}
$scope.search.clear = ->
$location.search({})
......@@ -66,6 +60,4 @@ class StreamSearch
angular.module('h.streamsearch', imports, configure)
.constant('searchFacets', SEARCH_FACETS)
.constant('searchValues', SEARCH_VALUES)
.controller('StreamSearchController', StreamSearch)
// Backbone.js 0.9.10
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){
// Initial Setup
// -------------
// Save a reference to the global object (`window` in the browser, `exports`
// on the server).
var root = this;
// Save the previous value of the `Backbone` variable, so that it can be
// restored later on, if `noConflict` is used.
var previousBackbone = root.Backbone;
// Create a local reference to array methods.
var array = [];
var push = array.push;
var slice = array.slice;
var splice = array.splice;
// The top-level namespace. All public Backbone classes and modules will
// be attached to this. Exported for both CommonJS and the browser.
var Backbone;
if (typeof exports !== 'undefined') {
Backbone = exports;
} else {
Backbone = root.Backbone = {};
}
// Current version of the library. Keep in sync with `package.json`.
Backbone.VERSION = '0.9.10';
// Require Underscore, if we're on the server, and it's not already present.
var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
// For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
Backbone.$ = root.jQuery || root.Zepto || root.ender;
// Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
// to its previous owner. Returns a reference to this Backbone object.
Backbone.noConflict = function() {
root.Backbone = previousBackbone;
return this;
};
// Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
// will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
// set a `X-Http-Method-Override` header.
Backbone.emulateHTTP = false;
// Turn on `emulateJSON` to support legacy servers that can't deal with direct
// `application/json` requests ... will encode the body as
// `application/x-www-form-urlencoded` instead and will send the model in a
// form param named `model`.
Backbone.emulateJSON = false;
// Backbone.Events
// ---------------
// Regular expression used to split event strings.
var eventSplitter = /\s+/;
// Implement fancy features of the Events API such as multiple event
// names `"change blur"` and jQuery-style event maps `{change: action}`
// in terms of the existing API.
var eventsApi = function(obj, action, name, rest) {
if (!name) return true;
if (typeof name === 'object') {
for (var key in name) {
obj[action].apply(obj, [key, name[key]].concat(rest));
}
} else if (eventSplitter.test(name)) {
var names = name.split(eventSplitter);
for (var i = 0, l = names.length; i < l; i++) {
obj[action].apply(obj, [names[i]].concat(rest));
}
} else {
return true;
}
};
// Optimized internal dispatch function for triggering events. Tries to
// keep the usual cases speedy (most Backbone events have 3 arguments).
var triggerEvents = function(events, args) {
var ev, i = -1, l = events.length;
switch (args.length) {
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx);
return;
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0]);
return;
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1]);
return;
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, args[0], args[1], args[2]);
return;
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args);
}
};
// A module that can be mixed in to *any object* in order to provide it with
// custom events. You may bind with `on` or remove with `off` callback
// functions to an event; `trigger`-ing an event fires all callbacks in
// succession.
//
// var object = {};
// _.extend(object, Backbone.Events);
// object.on('expand', function(){ alert('expanded'); });
// object.trigger('expand');
//
var Events = Backbone.Events = {
// Bind one or more space separated events, or an events map,
// to a `callback` function. Passing `"all"` will bind the callback to
// all events fired.
on: function(name, callback, context) {
if (!(eventsApi(this, 'on', name, [callback, context]) && callback)) return this;
this._events || (this._events = {});
var list = this._events[name] || (this._events[name] = []);
list.push({callback: callback, context: context, ctx: context || this});
return this;
},
// Bind events to only be triggered a single time. After the first time
// the callback is invoked, it will be removed.
once: function(name, callback, context) {
if (!(eventsApi(this, 'once', name, [callback, context]) && callback)) return this;
var self = this;
var once = _.once(function() {
self.off(name, once);
callback.apply(this, arguments);
});
once._callback = callback;
this.on(name, once, context);
return this;
},
// Remove one or many callbacks. If `context` is null, removes all
// callbacks with that function. If `callback` is null, removes all
// callbacks for the event. If `name` is null, removes all bound
// callbacks for all events.
off: function(name, callback, context) {
var list, ev, events, names, i, l, j, k;
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
if (!name && !callback && !context) {
this._events = {};
return this;
}
names = name ? [name] : _.keys(this._events);
for (i = 0, l = names.length; i < l; i++) {
name = names[i];
if (list = this._events[name]) {
events = [];
if (callback || context) {
for (j = 0, k = list.length; j < k; j++) {
ev = list[j];
if ((callback && callback !== ev.callback &&
callback !== ev.callback._callback) ||
(context && context !== ev.context)) {
events.push(ev);
}
}
}
this._events[name] = events;
}
}
return this;
},
// Trigger one or many events, firing all bound callbacks. Callbacks are
// passed the same arguments as `trigger` is, apart from the event name
// (unless you're listening on `"all"`, which will cause your callback to
// receive the true name of the event as the first argument).
trigger: function(name) {
if (!this._events) return this;
var args = slice.call(arguments, 1);
if (!eventsApi(this, 'trigger', name, args)) return this;
var events = this._events[name];
var allEvents = this._events.all;
if (events) triggerEvents(events, args);
if (allEvents) triggerEvents(allEvents, arguments);
return this;
},
// An inversion-of-control version of `on`. Tell *this* object to listen to
// an event in another object ... keeping track of what it's listening to.
listenTo: function(obj, name, callback) {
var listeners = this._listeners || (this._listeners = {});
var id = obj._listenerId || (obj._listenerId = _.uniqueId('l'));
listeners[id] = obj;
obj.on(name, typeof name === 'object' ? this : callback, this);
return this;
},
// Tell this object to stop listening to either specific events ... or
// to every object it's currently listening to.
stopListening: function(obj, name, callback) {
var listeners = this._listeners;
if (!listeners) return;
if (obj) {
obj.off(name, typeof name === 'object' ? this : callback, this);
if (!name && !callback) delete listeners[obj._listenerId];
} else {
if (typeof name === 'object') callback = this;
for (var id in listeners) {
listeners[id].off(name, callback, this);
}
this._listeners = {};
}
return this;
}
};
// Aliases for backwards compatibility.
Events.bind = Events.on;
Events.unbind = Events.off;
// Allow the `Backbone` object to serve as a global event bus, for folks who
// want global "pubsub" in a convenient place.
_.extend(Backbone, Events);
// Backbone.Model
// --------------
// Create a new model, with defined attributes. A client id (`cid`)
// is automatically generated and assigned for you.
var Model = Backbone.Model = function(attributes, options) {
var defaults;
var attrs = attributes || {};
this.cid = _.uniqueId('c');
this.attributes = {};
if (options && options.collection) this.collection = options.collection;
if (options && options.parse) attrs = this.parse(attrs, options) || {};
if (defaults = _.result(this, 'defaults')) {
attrs = _.defaults({}, attrs, defaults);
}
this.set(attrs, options);
this.changed = {};
this.initialize.apply(this, arguments);
};
// Attach all inheritable methods to the Model prototype.
_.extend(Model.prototype, Events, {
// A hash of attributes whose current and previous value differ.
changed: null,
// The default name for the JSON `id` attribute is `"id"`. MongoDB and
// CouchDB users may want to set this to `"_id"`.
idAttribute: 'id',
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
// Return a copy of the model's `attributes` object.
toJSON: function(options) {
return _.clone(this.attributes);
},
// Proxy `Backbone.sync` by default.
sync: function() {
return Backbone.sync.apply(this, arguments);
},
// Get the value of an attribute.
get: function(attr) {
return this.attributes[attr];
},
// Get the HTML-escaped value of an attribute.
escape: function(attr) {
return _.escape(this.get(attr));
},
// Returns `true` if the attribute contains a value that is not null
// or undefined.
has: function(attr) {
return this.get(attr) != null;
},
// ----------------------------------------------------------------------
// Set a hash of model attributes on the object, firing `"change"` unless
// you choose to silence it.
set: function(key, val, options) {
var attr, attrs, unset, changes, silent, changing, prev, current;
if (key == null) return this;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
options || (options = {});
// Run validation.
if (!this._validate(attrs, options)) return false;
// Extract attributes and options.
unset = options.unset;
silent = options.silent;
changes = [];
changing = this._changing;
this._changing = true;
if (!changing) {
this._previousAttributes = _.clone(this.attributes);
this.changed = {};
}
current = this.attributes, prev = this._previousAttributes;
// Check for changes of `id`.
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
// For each `set` attribute, update or delete the current value.
for (attr in attrs) {
val = attrs[attr];
if (!_.isEqual(current[attr], val)) changes.push(attr);
if (!_.isEqual(prev[attr], val)) {
this.changed[attr] = val;
} else {
delete this.changed[attr];
}
unset ? delete current[attr] : current[attr] = val;
}
// Trigger all relevant attribute changes.
if (!silent) {
if (changes.length) this._pending = true;
for (var i = 0, l = changes.length; i < l; i++) {
this.trigger('change:' + changes[i], this, current[changes[i]], options);
}
}
if (changing) return this;
if (!silent) {
while (this._pending) {
this._pending = false;
this.trigger('change', this, options);
}
}
this._pending = false;
this._changing = false;
return this;
},
// Remove an attribute from the model, firing `"change"` unless you choose
// to silence it. `unset` is a noop if the attribute doesn't exist.
unset: function(attr, options) {
return this.set(attr, void 0, _.extend({}, options, {unset: true}));
},
// Clear all attributes on the model, firing `"change"` unless you choose
// to silence it.
clear: function(options) {
var attrs = {};
for (var key in this.attributes) attrs[key] = void 0;
return this.set(attrs, _.extend({}, options, {unset: true}));
},
// Determine if the model has changed since the last `"change"` event.
// If you specify an attribute name, determine if that attribute has changed.
hasChanged: function(attr) {
if (attr == null) return !_.isEmpty(this.changed);
return _.has(this.changed, attr);
},
// Return an object containing all the attributes that have changed, or
// false if there are no changed attributes. Useful for determining what
// parts of a view need to be updated and/or what attributes need to be
// persisted to the server. Unset attributes will be set to undefined.
// You can also pass an attributes object to diff against the model,
// determining if there *would be* a change.
changedAttributes: function(diff) {
if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
var val, changed = false;
var old = this._changing ? this._previousAttributes : this.attributes;
for (var attr in diff) {
if (_.isEqual(old[attr], (val = diff[attr]))) continue;
(changed || (changed = {}))[attr] = val;
}
return changed;
},
// Get the previous value of an attribute, recorded at the time the last
// `"change"` event was fired.
previous: function(attr) {
if (attr == null || !this._previousAttributes) return null;
return this._previousAttributes[attr];
},
// Get all of the attributes of the model at the time of the previous
// `"change"` event.
previousAttributes: function() {
return _.clone(this._previousAttributes);
},
// ---------------------------------------------------------------------
// Fetch the model from the server. If the server's representation of the
// model differs from its current attributes, they will be overriden,
// triggering a `"change"` event.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
var success = options.success;
options.success = function(model, resp, options) {
if (!model.set(model.parse(resp, options), options)) return false;
if (success) success(model, resp, options);
};
return this.sync('read', this, options);
},
// Set a hash of model attributes, and sync the model to the server.
// If the server returns an attributes hash that differs, the model's
// state will be `set` again.
save: function(key, val, options) {
var attrs, success, method, xhr, attributes = this.attributes;
// Handle both `"key", value` and `{key: value}` -style arguments.
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} else {
(attrs = {})[key] = val;
}
// If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`.
if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false;
options = _.extend({validate: true}, options);
// Do not persist invalid models.
if (!this._validate(attrs, options)) return false;
// Set temporary attributes if `{wait: true}`.
if (attrs && options.wait) {
this.attributes = _.extend({}, attributes, attrs);
}
// After a successful server-side save, the client is (optionally)
// updated with the server-side state.
if (options.parse === void 0) options.parse = true;
success = options.success;
options.success = function(model, resp, options) {
// Ensure attributes are restored during synchronous saves.
model.attributes = attributes;
var serverAttrs = model.parse(resp, options);
if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
return false;
}
if (success) success(model, resp, options);
};
// Finish configuring and sending the Ajax request.
method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
if (method === 'patch') options.attrs = attrs;
xhr = this.sync(method, this, options);
// Restore attributes.
if (attrs && options.wait) this.attributes = attributes;
return xhr;
},
// Destroy this model on the server if it was already persisted.
// Optimistically removes the model from its collection, if it has one.
// If `wait: true` is passed, waits for the server to respond before removal.
destroy: function(options) {
options = options ? _.clone(options) : {};
var model = this;
var success = options.success;
var destroy = function() {
model.trigger('destroy', model, model.collection, options);
};
options.success = function(model, resp, options) {
if (options.wait || model.isNew()) destroy();
if (success) success(model, resp, options);
};
if (this.isNew()) {
options.success(this, null, options);
return false;
}
var xhr = this.sync('delete', this, options);
if (!options.wait) destroy();
return xhr;
},
// Default URL for the model's representation on the server -- if you're
// using Backbone's restful methods, override this to change the endpoint
// that will be called.
url: function() {
var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
},
// **parse** converts a response into the hash of attributes to be `set` on
// the model. The default implementation is just to pass the response along.
parse: function(resp, options) {
return resp;
},
// Create a new model with identical attributes to this one.
clone: function() {
return new this.constructor(this.attributes);
},
// A model is new if it has never been saved to the server, and lacks an id.
isNew: function() {
return this.id == null;
},
// Check if the model is currently in a valid state.
isValid: function(options) {
return !this.validate || !this.validate(this.attributes, options);
},
// Run validation against the next complete set of model attributes,
// returning `true` if all is well. Otherwise, fire a general
// `"error"` event and call the error callback, if specified.
_validate: function(attrs, options) {
if (!options.validate || !this.validate) return true;
attrs = _.extend({}, this.attributes, attrs);
var error = this.validationError = this.validate(attrs, options) || null;
if (!error) return true;
this.trigger('invalid', this, error, options || {});
return false;
}
});
// Backbone.Collection
// -------------------
// Provides a standard collection class for our sets of models, ordered
// or unordered. If a `comparator` is specified, the Collection will maintain
// its models in sort order, as they're added and removed.
var Collection = Backbone.Collection = function(models, options) {
options || (options = {});
if (options.model) this.model = options.model;
if (options.comparator !== void 0) this.comparator = options.comparator;
this.models = [];
this._reset();
this.initialize.apply(this, arguments);
if (models) this.reset(models, _.extend({silent: true}, options));
};
// Define the Collection's inheritable methods.
_.extend(Collection.prototype, Events, {
// The default model for a collection is just a **Backbone.Model**.
// This should be overridden in most cases.
model: Model,
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
// The JSON representation of a Collection is an array of the
// models' attributes.
toJSON: function(options) {
return this.map(function(model){ return model.toJSON(options); });
},
// Proxy `Backbone.sync` by default.
sync: function() {
return Backbone.sync.apply(this, arguments);
},
// Add a model, or list of models to the set.
add: function(models, options) {
models = _.isArray(models) ? models.slice() : [models];
options || (options = {});
var i, l, model, attrs, existing, doSort, add, at, sort, sortAttr;
add = [];
at = options.at;
sort = this.comparator && (at == null) && options.sort != false;
sortAttr = _.isString(this.comparator) ? this.comparator : null;
// Turn bare objects into model references, and prevent invalid models
// from being added.
for (i = 0, l = models.length; i < l; i++) {
if (!(model = this._prepareModel(attrs = models[i], options))) {
this.trigger('invalid', this, attrs, options);
continue;
}
// If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model.
if (existing = this.get(model)) {
if (options.merge) {
existing.set(attrs === model ? model.attributes : attrs, options);
if (sort && !doSort && existing.hasChanged(sortAttr)) doSort = true;
}
continue;
}
// This is a new model, push it to the `add` list.
add.push(model);
// Listen to added models' events, and index models for lookup by
// `id` and by `cid`.
model.on('all', this._onModelEvent, this);
this._byId[model.cid] = model;
if (model.id != null) this._byId[model.id] = model;
}
// See if sorting is needed, update `length` and splice in new models.
if (add.length) {
if (sort) doSort = true;
this.length += add.length;
if (at != null) {
splice.apply(this.models, [at, 0].concat(add));
} else {
push.apply(this.models, add);
}
}
// Silently sort the collection if appropriate.
if (doSort) this.sort({silent: true});
if (options.silent) return this;
// Trigger `add` events.
for (i = 0, l = add.length; i < l; i++) {
(model = add[i]).trigger('add', model, this, options);
}
// Trigger `sort` if the collection was sorted.
if (doSort) this.trigger('sort', this, options);
return this;
},
// Remove a model, or a list of models from the set.
remove: function(models, options) {
models = _.isArray(models) ? models.slice() : [models];
options || (options = {});
var i, l, index, model;
for (i = 0, l = models.length; i < l; i++) {
model = this.get(models[i]);
if (!model) continue;
delete this._byId[model.id];
delete this._byId[model.cid];
index = this.indexOf(model);
this.models.splice(index, 1);
this.length--;
if (!options.silent) {
options.index = index;
model.trigger('remove', model, this, options);
}
this._removeReference(model);
}
return this;
},
// Add a model to the end of the collection.
push: function(model, options) {
model = this._prepareModel(model, options);
this.add(model, _.extend({at: this.length}, options));
return model;
},
// Remove a model from the end of the collection.
pop: function(options) {
var model = this.at(this.length - 1);
this.remove(model, options);
return model;
},
// Add a model to the beginning of the collection.
unshift: function(model, options) {
model = this._prepareModel(model, options);
this.add(model, _.extend({at: 0}, options));
return model;
},
// Remove a model from the beginning of the collection.
shift: function(options) {
var model = this.at(0);
this.remove(model, options);
return model;
},
// Slice out a sub-array of models from the collection.
slice: function(begin, end) {
return this.models.slice(begin, end);
},
// Get a model from the set by id.
get: function(obj) {
if (obj == null) return void 0;
this._idAttr || (this._idAttr = this.model.prototype.idAttribute);
return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj];
},
// Get the model at the given index.
at: function(index) {
return this.models[index];
},
// Return models with matching attributes. Useful for simple cases of `filter`.
where: function(attrs) {
if (_.isEmpty(attrs)) return [];
return this.filter(function(model) {
for (var key in attrs) {
if (attrs[key] !== model.get(key)) return false;
}
return true;
});
},
// Force the collection to re-sort itself. You don't need to call this under
// normal circumstances, as the set will maintain sort order as each item
// is added.
sort: function(options) {
if (!this.comparator) {
throw new Error('Cannot sort a set without a comparator');
}
options || (options = {});
// Run sort based on type of `comparator`.
if (_.isString(this.comparator) || this.comparator.length === 1) {
this.models = this.sortBy(this.comparator, this);
} else {
this.models.sort(_.bind(this.comparator, this));
}
if (!options.silent) this.trigger('sort', this, options);
return this;
},
// Pluck an attribute from each model in the collection.
pluck: function(attr) {
return _.invoke(this.models, 'get', attr);
},
// Smartly update a collection with a change set of models, adding,
// removing, and merging as necessary.
update: function(models, options) {
options = _.extend({add: true, merge: true, remove: true}, options);
if (options.parse) models = this.parse(models, options);
var model, i, l, existing;
var add = [], remove = [], modelMap = {};
// Allow a single model (or no argument) to be passed.
if (!_.isArray(models)) models = models ? [models] : [];
// Proxy to `add` for this case, no need to iterate...
if (options.add && !options.remove) return this.add(models, options);
// Determine which models to add and merge, and which to remove.
for (i = 0, l = models.length; i < l; i++) {
model = models[i];
existing = this.get(model);
if (options.remove && existing) modelMap[existing.cid] = true;
if ((options.add && !existing) || (options.merge && existing)) {
add.push(model);
}
}
if (options.remove) {
for (i = 0, l = this.models.length; i < l; i++) {
model = this.models[i];
if (!modelMap[model.cid]) remove.push(model);
}
}
// Remove models (if applicable) before we add and merge the rest.
if (remove.length) this.remove(remove, options);
if (add.length) this.add(add, options);
return this;
},
// When you have more items than you want to add or remove individually,
// you can reset the entire set with a new list of models, without firing
// any `add` or `remove` events. Fires `reset` when finished.
reset: function(models, options) {
options || (options = {});
if (options.parse) models = this.parse(models, options);
for (var i = 0, l = this.models.length; i < l; i++) {
this._removeReference(this.models[i]);
}
options.previousModels = this.models.slice();
this._reset();
if (models) this.add(models, _.extend({silent: true}, options));
if (!options.silent) this.trigger('reset', this, options);
return this;
},
// Fetch the default set of models for this collection, resetting the
// collection when they arrive. If `update: true` is passed, the response
// data will be passed through the `update` method instead of `reset`.
fetch: function(options) {
options = options ? _.clone(options) : {};
if (options.parse === void 0) options.parse = true;
var success = options.success;
options.success = function(collection, resp, options) {
var method = options.update ? 'update' : 'reset';
collection[method](resp, options);
if (success) success(collection, resp, options);
};
return this.sync('read', this, options);
},
// Create a new instance of a model in this collection. Add the model to the
// collection immediately, unless `wait: true` is passed, in which case we
// wait for the server to agree.
create: function(model, options) {
options = options ? _.clone(options) : {};
if (!(model = this._prepareModel(model, options))) return false;
if (!options.wait) this.add(model, options);
var collection = this;
var success = options.success;
options.success = function(model, resp, options) {
if (options.wait) collection.add(model, options);
if (success) success(model, resp, options);
};
model.save(null, options);
return model;
},
// **parse** converts a response into a list of models to be added to the
// collection. The default implementation is just to pass it through.
parse: function(resp, options) {
return resp;
},
// Create a new collection with an identical list of models as this one.
clone: function() {
return new this.constructor(this.models);
},
// Reset all internal state. Called when the collection is reset.
_reset: function() {
this.length = 0;
this.models.length = 0;
this._byId = {};
},
// Prepare a model or hash of attributes to be added to this collection.
_prepareModel: function(attrs, options) {
if (attrs instanceof Model) {
if (!attrs.collection) attrs.collection = this;
return attrs;
}
options || (options = {});
options.collection = this;
var model = new this.model(attrs, options);
if (!model._validate(attrs, options)) return false;
return model;
},
// Internal method to remove a model's ties to a collection.
_removeReference: function(model) {
if (this === model.collection) delete model.collection;
model.off('all', this._onModelEvent, this);
},
// Internal method called every time a model in the set fires an event.
// Sets need to update their indexes when models change ids. All other
// events simply proxy through. "add" and "remove" events that originate
// in other collections are ignored.
_onModelEvent: function(event, model, collection, options) {
if ((event === 'add' || event === 'remove') && collection !== this) return;
if (event === 'destroy') this.remove(model, options);
if (model && event === 'change:' + model.idAttribute) {
delete this._byId[model.previous(model.idAttribute)];
if (model.id != null) this._byId[model.id] = model;
}
this.trigger.apply(this, arguments);
},
sortedIndex: function (model, value, context) {
value || (value = this.comparator);
var iterator = _.isFunction(value) ? value : function(model) {
return model.get(value);
};
return _.sortedIndex(this.models, model, iterator, context);
}
});
// Underscore methods that we want to implement on the Collection.
var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf',
'isEmpty', 'chain'];
// Mix in each Underscore method as a proxy to `Collection#models`.
_.each(methods, function(method) {
Collection.prototype[method] = function() {
var args = slice.call(arguments);
args.unshift(this.models);
return _[method].apply(_, args);
};
});
// Underscore methods that take a property name as an argument.
var attributeMethods = ['groupBy', 'countBy', 'sortBy'];
// Use attributes instead of properties.
_.each(attributeMethods, function(method) {
Collection.prototype[method] = function(value, context) {
var iterator = _.isFunction(value) ? value : function(model) {
return model.get(value);
};
return _[method](this.models, iterator, context);
};
});
// Backbone.Router
// ---------------
// Routers map faux-URLs to actions, and fire events when routes are
// matched. Creating a new one sets its `routes` hash, if not set statically.
var Router = Backbone.Router = function(options) {
options || (options = {});
if (options.routes) this.routes = options.routes;
this._bindRoutes();
this.initialize.apply(this, arguments);
};
// Cached regular expressions for matching named param parts and splatted
// parts of route strings.
var optionalParam = /\((.*?)\)/g;
var namedParam = /(\(\?)?:\w+/g;
var splatParam = /\*\w+/g;
var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
// Set up all inheritable **Backbone.Router** properties and methods.
_.extend(Router.prototype, Events, {
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
// Manually bind a single named route to a callback. For example:
//
// this.route('search/:query/p:num', 'search', function(query, num) {
// ...
// });
//
route: function(route, name, callback) {
if (!_.isRegExp(route)) route = this._routeToRegExp(route);
if (!callback) callback = this[name];
Backbone.history.route(route, _.bind(function(fragment) {
var args = this._extractParameters(route, fragment);
callback && callback.apply(this, args);
this.trigger.apply(this, ['route:' + name].concat(args));
this.trigger('route', name, args);
Backbone.history.trigger('route', this, name, args);
}, this));
return this;
},
// Simple proxy to `Backbone.history` to save a fragment into the history.
navigate: function(fragment, options) {
Backbone.history.navigate(fragment, options);
return this;
},
// Bind all defined routes to `Backbone.history`. We have to reverse the
// order of the routes here to support behavior where the most general
// routes can be defined at the bottom of the route map.
_bindRoutes: function() {
if (!this.routes) return;
var route, routes = _.keys(this.routes);
while ((route = routes.pop()) != null) {
this.route(route, this.routes[route]);
}
},
// Convert a route string into a regular expression, suitable for matching
// against the current location hash.
_routeToRegExp: function(route) {
route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional){
return optional ? match : '([^\/]+)';
})
.replace(splatParam, '(.*?)');
return new RegExp('^' + route + '$');
},
// Given a route, and a URL fragment that it matches, return the array of
// extracted parameters.
_extractParameters: function(route, fragment) {
return route.exec(fragment).slice(1);
}
});
// Backbone.History
// ----------------
// Handles cross-browser history management, based on URL fragments. If the
// browser does not support `onhashchange`, falls back to polling.
var History = Backbone.History = function() {
this.handlers = [];
_.bindAll(this, 'checkUrl');
// Ensure that `History` can be used outside of the browser.
if (typeof window !== 'undefined') {
this.location = window.location;
this.history = window.history;
}
};
// Cached regex for stripping a leading hash/slash and trailing space.
var routeStripper = /^[#\/]|\s+$/g;
// Cached regex for stripping leading and trailing slashes.
var rootStripper = /^\/+|\/+$/g;
// Cached regex for detecting MSIE.
var isExplorer = /msie [\w.]+/;
// Cached regex for removing a trailing slash.
var trailingSlash = /\/$/;
// Has the history handling already been started?
History.started = false;
// Set up all inheritable **Backbone.History** properties and methods.
_.extend(History.prototype, Events, {
// The default interval to poll for hash changes, if necessary, is
// twenty times a second.
interval: 50,
// Gets the true hash value. Cannot use location.hash directly due to bug
// in Firefox where location.hash will always be decoded.
getHash: function(window) {
var match = (window || this).location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
// Get the cross-browser normalized URL fragment, either from the URL,
// the hash, or the override.
getFragment: function(fragment, forcePushState) {
if (fragment == null) {
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
fragment = this.location.pathname;
var root = this.root.replace(trailingSlash, '');
if (!fragment.indexOf(root)) fragment = fragment.substr(root.length);
} else {
fragment = this.getHash();
}
}
return fragment.replace(routeStripper, '');
},
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start: function(options) {
if (History.started) throw new Error("Backbone.history has already been started");
History.started = true;
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
this.options = _.extend({}, {root: '/'}, this.options, options);
this.root = this.options.root;
this._wantsHashChange = this.options.hashChange !== false;
this._wantsPushState = !!this.options.pushState;
this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
var fragment = this.getFragment();
var docMode = document.documentMode;
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
// Normalize root to always include a leading and trailing slash.
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
if (oldIE && this._wantsHashChange) {
this.iframe = Backbone.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
this.navigate(fragment);
}
// Depending on whether we're using pushState or hashes, and whether
// 'onhashchange' is supported, determine how we check the URL state.
if (this._hasPushState) {
Backbone.$(window).on('popstate', this.checkUrl);
} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
Backbone.$(window).on('hashchange', this.checkUrl);
} else if (this._wantsHashChange) {
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
}
// Determine if we need to change the base url, for a pushState link
// opened by a non-pushState browser.
this.fragment = fragment;
var loc = this.location;
var atRoot = loc.pathname.replace(/[^\/]$/, '$&/') === this.root;
// If we've started off with a route from a `pushState`-enabled browser,
// but we're currently in a browser that doesn't support it...
if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
this.fragment = this.getFragment(null, true);
this.location.replace(this.root + this.location.search + '#' + this.fragment);
// Return immediately as browser will do redirect to new url
return true;
// Or if we've started out with a hash-based route, but we're currently
// in a browser where it could be `pushState`-based instead...
} else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
this.fragment = this.getHash().replace(routeStripper, '');
this.history.replaceState({}, document.title, this.root + this.fragment + loc.search);
}
if (!this.options.silent) return this.loadUrl();
},
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
// but possibly useful for unit testing Routers.
stop: function() {
Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
clearInterval(this._checkUrlInterval);
History.started = false;
},
// Add a route to be tested when the fragment changes. Routes added later
// may override previous routes.
route: function(route, callback) {
this.handlers.unshift({route: route, callback: callback});
},
// Checks the current URL to see if it has changed, and if it has,
// calls `loadUrl`, normalizing across the hidden iframe.
checkUrl: function(e) {
var current = this.getFragment();
if (current === this.fragment && this.iframe) {
current = this.getFragment(this.getHash(this.iframe));
}
if (current === this.fragment) return false;
if (this.iframe) this.navigate(current);
this.loadUrl() || this.loadUrl(this.getHash());
},
// Attempt to load the current URL fragment. If a route succeeds with a
// match, returns `true`. If no defined routes matches the fragment,
// returns `false`.
loadUrl: function(fragmentOverride) {
var fragment = this.fragment = this.getFragment(fragmentOverride);
var matched = _.any(this.handlers, function(handler) {
if (handler.route.test(fragment)) {
handler.callback(fragment);
return true;
}
});
return matched;
},
// Save a fragment into the hash history, or replace the URL state if the
// 'replace' option is passed. You are responsible for properly URL-encoding
// the fragment in advance.
//
// The options object can contain `trigger: true` if you wish to have the
// route callback be fired (not usually desirable), or `replace: true`, if
// you wish to modify the current URL without adding an entry to the history.
navigate: function(fragment, options) {
if (!History.started) return false;
if (!options || options === true) options = {trigger: options};
fragment = this.getFragment(fragment || '');
if (this.fragment === fragment) return;
this.fragment = fragment;
var url = this.root + fragment;
// If pushState is available, we use it to set the fragment as a real URL.
if (this._hasPushState) {
this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
// If hash changes haven't been explicitly disabled, update the hash
// fragment to store history.
} else if (this._wantsHashChange) {
this._updateHash(this.location, fragment, options.replace);
if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
// Opening and closing the iframe tricks IE7 and earlier to push a
// history entry on hash-tag change. When replace is true, we don't
// want this.
if(!options.replace) this.iframe.document.open().close();
this._updateHash(this.iframe.location, fragment, options.replace);
}
// If you've told us that you explicitly don't want fallback hashchange-
// based history, then `navigate` becomes a page refresh.
} else {
return this.location.assign(url);
}
if (options.trigger) this.loadUrl(fragment);
},
// Update the hash location, either replacing the current entry, or adding
// a new one to the browser history.
_updateHash: function(location, fragment, replace) {
if (replace) {
var href = location.href.replace(/(javascript:|#).*$/, '');
location.replace(href + '#' + fragment);
} else {
// Some browsers require that `hash` contains a leading #.
location.hash = '#' + fragment;
}
}
});
// Create the default Backbone.history.
Backbone.history = new History;
// Backbone.View
// -------------
// Creating a Backbone.View creates its initial element outside of the DOM,
// if an existing element is not provided...
var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view');
this._configure(options || {});
this._ensureElement();
this.initialize.apply(this, arguments);
this.delegateEvents();
};
// Cached regex to split keys for `delegate`.
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
// List of view options to be merged as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
// Set up all inheritable **Backbone.View** properties and methods.
_.extend(View.prototype, Events, {
// The default `tagName` of a View's element is `"div"`.
tagName: 'div',
// jQuery delegate for element lookup, scoped to DOM elements within the
// current view. This should be prefered to global lookups where possible.
$: function(selector) {
return this.$el.find(selector);
},
// Initialize is an empty function by default. Override it with your own
// initialization logic.
initialize: function(){},
// **render** is the core function that your view should override, in order
// to populate its element (`this.el`), with the appropriate HTML. The
// convention is for **render** to always return `this`.
render: function() {
return this;
},
// Remove this view by taking the element out of the DOM, and removing any
// applicable Backbone.Events listeners.
remove: function() {
this.$el.remove();
this.stopListening();
return this;
},
// Change the view's element (`this.el` property), including event
// re-delegation.
setElement: function(element, delegate) {
if (this.$el) this.undelegateEvents();
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
this.el = this.$el[0];
if (delegate !== false) this.delegateEvents();
return this;
},
// Set callbacks, where `this.events` is a hash of
//
// *{"event selector": "callback"}*
//
// {
// 'mousedown .title': 'edit',
// 'click .button': 'save'
// 'click .open': function(e) { ... }
// }
//
// pairs. Callbacks will be bound to the view, with `this` set properly.
// Uses event delegation for efficiency.
// Omitting the selector binds the event to `this.el`.
// This only works for delegate-able events: not `focus`, `blur`, and
// not `change`, `submit`, and `reset` in Internet Explorer.
delegateEvents: function(events) {
if (!(events || (events = _.result(this, 'events')))) return;
this.undelegateEvents();
for (var key in events) {
var method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
if (!method) throw new Error('Method "' + events[key] + '" does not exist');
var match = key.match(delegateEventSplitter);
var eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.delegateEvents' + this.cid;
if (selector === '') {
this.$el.on(eventName, method);
} else {
this.$el.on(eventName, selector, method);
}
}
},
// Clears all callbacks previously bound to the view with `delegateEvents`.
// You usually don't need to use this, but may wish to if you have multiple
// Backbone views attached to the same DOM element.
undelegateEvents: function() {
this.$el.off('.delegateEvents' + this.cid);
},
// Performs the initial configuration of a View with a set of options.
// Keys with special meaning *(model, collection, id, className)*, are
// attached directly to the view.
_configure: function(options) {
if (this.options) options = _.extend({}, _.result(this, 'options'), options);
_.extend(this, _.pick(options, viewOptions));
this.options = options;
},
// Ensure that the View has a DOM element to render into.
// If `this.el` is a string, pass it through `$()`, take the first
// matching element, and re-assign it to `el`. Otherwise, create
// an element from the `id`, `className` and `tagName` properties.
_ensureElement: function() {
if (!this.el) {
var attrs = _.extend({}, _.result(this, 'attributes'));
if (this.id) attrs.id = _.result(this, 'id');
if (this.className) attrs['class'] = _.result(this, 'className');
var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
this.setElement($el, false);
} else {
this.setElement(_.result(this, 'el'), false);
}
}
});
// Backbone.sync
// -------------
// Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var methodMap = {
'create': 'POST',
'update': 'PUT',
'patch': 'PATCH',
'delete': 'DELETE',
'read': 'GET'
};
// Override this function to change the manner in which Backbone persists
// models to the server. You will be passed the type of request, and the
// model in question. By default, makes a RESTful Ajax request
// to the model's `url()`. Some possible customizations could be:
//
// * Use `setTimeout` to batch rapid-fire updates into a single request.
// * Send up the models as XML instead of JSON.
// * Persist models via WebSockets instead of Ajax.
//
// Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
// as `POST`, with a `_method` parameter containing the true HTTP method,
// as well as all requests with the body as `application/x-www-form-urlencoded`
// instead of `application/json` with the model in a param named `model`.
// Useful when interfacing with server-side languages like **PHP** that make
// it difficult to read the body of `PUT` requests.
Backbone.sync = function(method, model, options) {
var type = methodMap[method];
// Default options, unless specified.
_.defaults(options || (options = {}), {
emulateHTTP: Backbone.emulateHTTP,
emulateJSON: Backbone.emulateJSON
});
// Default JSON-request options.
var params = {type: type, dataType: 'json'};
// Ensure that we have a URL.
if (!options.url) {
params.url = _.result(model, 'url') || urlError();
}
// Ensure that we have the appropriate request data.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.contentType = 'application/json';
params.data = JSON.stringify(options.attrs || model.toJSON(options));
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if (options.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? {model: params.data} : {};
}
// For older servers, emulate HTTP by mimicking the HTTP method with `_method`
// And an `X-HTTP-Method-Override` header.
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
params.type = 'POST';
if (options.emulateJSON) params.data._method = type;
var beforeSend = options.beforeSend;
options.beforeSend = function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
if (beforeSend) return beforeSend.apply(this, arguments);
};
}
// Don't process data on a non-GET request.
if (params.type !== 'GET' && !options.emulateJSON) {
params.processData = false;
}
var success = options.success;
options.success = function(resp) {
if (success) success(model, resp, options);
model.trigger('sync', model, resp, options);
};
var error = options.error;
options.error = function(xhr) {
if (error) error(model, xhr, options);
model.trigger('error', model, xhr, options);
};
// Make the request, allowing the user to override any Ajax options.
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
model.trigger('request', model, xhr, options);
return xhr;
};
// Set the default implementation of `Backbone.ajax` to proxy through to `$`.
Backbone.ajax = function() {
return Backbone.$.ajax.apply(Backbone.$, arguments);
};
// Helpers
// -------
// Helper function to correctly set up the prototype chain, for subclasses.
// Similar to `goog.inherits`, but uses a hash of prototype properties and
// class properties to be extended.
var extend = function(protoProps, staticProps) {
var parent = this;
var child;
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent's constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// Add static properties to the constructor function, if supplied.
_.extend(child, parent, staticProps);
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function.
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;
// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);
// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;
return child;
};
// Set up inheritance for the model, collection, router, view and history.
Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
// Throw an error when a URL is needed, and none is supplied.
var urlError = function() {
throw new Error('A "url" property or function must be specified');
};
}).call(this);
\ No newline at end of file
// Underscore.js 1.4.3
// http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.4.3';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (_.isFunction(method) ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Convenience version of a common use case of `filter`: selecting only objects
// with specific `key:value` pairs.
_.where = function(obj, attrs) {
if (_.isEmpty(attrs)) return [];
return _.filter(obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
return true;
});
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array.
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, value, context) {
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
index : index,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index < right.index ? -1 : 1;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(obj, value, context, behavior) {
var result = {};
var iterator = lookupIterator(value || _.identity);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, value, context) {
return group(obj, value, context, function(result, key, value) {
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
};
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = function(obj, value, context) {
return group(obj, value, context, function(result, key) {
if (!_.has(result, key)) result[key] = 0;
result[key]++;
});
};
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = iterator == null ? _.identity : lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
return array[array.length - 1];
}
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
each(input, function(value) {
if (_.isArray(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(concat.apply(ArrayProto, arguments));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(args, "" + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, l = list.length; i < l; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, l = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < l; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(len);
while(idx < len) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function(func, context) {
var args, bound;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, result;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) result = func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) result = func.apply(context, args);
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
if (times <= 0) return func();
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var values = [];
for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var pairs = [];
for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(n);
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + (0 | Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;'
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named property is a function then invoke it;
// otherwise, return it.
_.result = function(object, property) {
if (object == null) return null;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = '' + ++idCounter;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
}).call(this);
\ No newline at end of file
// This is the annotated source code for
// [VisualSearch.js](http://documentcloud.github.com/visualsearch/),
// a rich search box for real data.
//
// The annotated source HTML is generated by
// [Docco](http://jashkenas.github.com/docco/).
/** @license VisualSearch.js 0.4.0
* (c) 2011 Samuel Clay, @samuelclay, DocumentCloud Inc.
* VisualSearch.js may be freely distributed under the MIT license.
* For all details and documentation:
* http://documentcloud.github.com/visualsearch
*/
(function() {
var $ = jQuery; // Handle namespaced jQuery
// Setting up VisualSearch globals. These will eventually be made instance-based.
if (!window.VS) window.VS = {};
if (!VS.app) VS.app = {};
if (!VS.ui) VS.ui = {};
if (!VS.model) VS.model = {};
if (!VS.utils) VS.utils = {};
// Sets the version for VisualSearch to be used programatically elsewhere.
VS.VERSION = '0.4.0';
VS.VisualSearch = function(options) {
var defaults = {
container : '',
query : '',
autosearch : true,
unquotable : [],
remainder : 'text',
showFacets : true,
callbacks : {
search : $.noop,
focus : $.noop,
blur : $.noop,
facetMatches : $.noop,
valueMatches : $.noop
}
};
this.options = _.extend({}, defaults, options);
this.options.callbacks = _.extend({}, defaults.callbacks, options.callbacks);
VS.app.hotkeys.initialize();
this.searchQuery = new VS.model.SearchQuery();
this.searchBox = new VS.ui.SearchBox({
app: this,
showFacets: this.options.showFacets
});
if (options.container) {
var searchBox = this.searchBox.render().el;
$(this.options.container).html(searchBox);
}
this.searchBox.value(this.options.query || '');
// Disable page caching for browsers that incorrectly cache the visual search inputs.
// This is forced the browser to re-render the page when it is retrieved in its history.
$(window).bind('unload', function(e) {});
// Gives the user back a reference to the `searchBox` so they
// can use public methods.
return this;
};
// Entry-point used to tie all parts of VisualSearch together. It will either attach
// itself to `options.container`, or pass back the `searchBox` so it can be rendered
// at will.
VS.init = function(options) {
return new VS.VisualSearch(options);
};
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// The search box is responsible for managing the many facet views and input views.
VS.ui.SearchBox = Backbone.View.extend({
id : 'search',
events : {
'click .VS-cancel-search-box' : 'clearSearch',
'mousedown .VS-search-box' : 'maybeFocusSearch',
'dblclick .VS-search-box' : 'highlightSearch',
'click .VS-search-box' : 'maybeTripleClick'
},
// Creating a new SearchBox registers handlers for re-rendering facets when necessary,
// as well as handling typing when a facet is selected.
initialize : function() {
this.app = this.options.app;
this.flags = {
allSelected : false
};
this.facetViews = [];
this.inputViews = [];
_.bindAll(this, 'renderFacets', '_maybeDisableFacets', 'disableFacets',
'deselectAllFacets', 'addedFacet', 'removedFacet', 'changedFacet');
this.app.searchQuery
.bind('reset', this.renderFacets)
.bind('add', this.addedFacet)
.bind('remove', this.removedFacet)
.bind('change', this.changedFacet);
$(document).bind('keydown', this._maybeDisableFacets);
},
// Renders the search box, but requires placement on the page through `this.el`.
render : function() {
$(this.el).append(JST['search_box']({}));
$(document.body).setMode('no', 'search');
return this;
},
// # Querying Facets #
// Either gets a serialized query string or sets the faceted query from a query string.
value : function(query) {
if (query == null) return this.serialize();
return this.setQuery(query);
},
// Uses the VS.app.searchQuery collection to serialize the current query from the various
// facets that are in the search box.
serialize : function() {
var query = [];
var inputViewsCount = this.inputViews.length;
this.app.searchQuery.each(_.bind(function(facet, i) {
query.push(this.inputViews[i].value());
query.push(facet.serialize());
}, this));
if (inputViewsCount) {
query.push(this.inputViews[inputViewsCount-1].value());
}
return _.compact(query).join(' ');
},
// Returns any facet views that are currently selected. Useful for changing the value
// callbacks based on what else is in the search box and which facet is being edited.
selected: function() {
return _.select(this.facetViews, function(view) {
return view.modes.editing == 'is' || view.modes.selected == 'is';
});
},
// Similar to `this.selected`, returns any facet models that are currently selected.
selectedModels: function() {
return _.pluck(this.selected(), 'model');
},
// Takes a query string and uses the SearchParser to parse and render it. Note that
// `VS.app.SearchParser` refreshes the `VS.app.searchQuery` collection, which is bound
// here to call `this.renderFacets`.
setQuery : function(query) {
this.currentQuery = query;
VS.app.SearchParser.parse(this.app, query);
},
// Returns the position of a facet/input view. Useful when moving between facets.
viewPosition : function(view) {
var views = view.type == 'facet' ? this.facetViews : this.inputViews;
var position = _.indexOf(views, view);
if (position == -1) position = 0;
return position;
},
// Used to launch a search. Hitting enter or clicking the search button.
searchEvent : function(e) {
var query = this.value();
this.focusSearch(e);
this.value(query);
this.app.options.callbacks.search(query, this.app.searchQuery);
},
// # Rendering Facets #
// Add a new facet. Facet will be focused and ready to accept a value. Can also
// specify position, in the case of adding facets from an inbetween input.
addFacet : function(category, initialQuery, position) {
category = VS.utils.inflector.trim(category);
initialQuery = VS.utils.inflector.trim(initialQuery || '');
if (!category) return;
var model = new VS.model.SearchFacet({
category : category,
value : initialQuery || '',
app : this.app
});
this.app.searchQuery.add(model, {at: position});
},
// Renders a newly added facet, and selects it.
addedFacet : function (model) {
this.renderFacets();
var facetView = _.detect(this.facetViews, function(view) {
if (view.model == model) return true;
});
_.defer(function() {
facetView.enableEdit();
});
},
// Changing a facet programmatically re-renders it.
changedFacet: function () {
this.renderFacets();
},
// When removing a facet, potentially do something. For now, the adjacent
// remaining facet is selected, but this is handled by the facet's view,
// since its position is unknown by the time the collection triggers this
// remove callback.
removedFacet : function (facet, query, options) {},
// Renders each facet as a searchFacet view.
renderFacets : function() {
this.facetViews = [];
this.inputViews = [];
this.$('.VS-search-inner').empty();
this.app.searchQuery.each(_.bind(this.renderFacet, this));
// Add on an n+1 empty search input on the very end.
this.renderSearchInput();
this.renderPlaceholder();
},
// Render a single facet, using its category and query value.
renderFacet : function(facet, position) {
var view = new VS.ui.SearchFacet({
app : this.app,
model : facet,
order : position
});
// Input first, facet second.
this.renderSearchInput();
this.facetViews.push(view);
this.$('.VS-search-inner').children().eq(position*2).after(view.render().el);
view.calculateSize();
_.defer(_.bind(view.calculateSize, view));
return view;
},
// Render a single input, used to create and autocomplete facets
renderSearchInput : function() {
var input = new VS.ui.SearchInput({
position: this.inputViews.length,
app: this.app,
showFacets: this.options.showFacets
});
this.$('.VS-search-inner').append(input.render().el);
this.inputViews.push(input);
},
// Handles showing/hiding the placeholder text
renderPlaceholder : function() {
var $placeholder = this.$('.VS-placeholder');
if (this.app.searchQuery.length) {
$placeholder.addClass("VS-hidden");
} else {
$placeholder.removeClass("VS-hidden")
.text(this.app.options.placeholder);
}
},
// # Modifying Facets #
// Clears out the search box. Command+A + delete can trigger this, as can a cancel button.
//
// If a `clearSearch` callback was provided, the callback is invoked and
// provided with a function performs the actual removal of the data. This
// allows third-party developers to either clear data asynchronously, or
// prior to performing their custom "clear" logic.
clearSearch : function(e) {
var actualClearSearch = _.bind(function() {
this.disableFacets();
this.value('');
this.flags.allSelected = false;
this.searchEvent(e);
this.focusSearch(e);
}, this);
if (this.app.options.callbacks.clearSearch) {
this.app.options.callbacks.clearSearch(actualClearSearch);
} else {
actualClearSearch();
}
},
// Command+A selects all facets.
selectAllFacets : function() {
this.flags.allSelected = true;
$(document).one('click.selectAllFacets', this.deselectAllFacets);
_.each(this.facetViews, function(facetView, i) {
facetView.selectFacet();
});
_.each(this.inputViews, function(inputView, i) {
inputView.selectText();
});
},
// Used by facets and input to see if all facets are currently selected.
allSelected : function(deselect) {
if (deselect) this.flags.allSelected = false;
return this.flags.allSelected;
},
// After `selectAllFacets` is engaged, this method is bound to the entire document.
// This immediate disables and deselects all facets, but it also checks if the user
// has clicked on either a facet or an input, and properly selects the view.
deselectAllFacets : function(e) {
this.disableFacets();
if (this.$(e.target).is('.category,input')) {
var el = $(e.target).closest('.search_facet,.search_input');
var view = _.detect(this.facetViews.concat(this.inputViews), function(v) {
return v.el == el[0];
});
if (view.type == 'facet') {
view.selectFacet();
} else if (view.type == 'input') {
_.defer(function() {
view.enableEdit(true);
});
}
}
},
// Disables all facets except for the passed in view. Used when switching between
// facets, so as not to have to keep state of active facets.
disableFacets : function(keepView) {
_.each(this.inputViews, function(view) {
if (view && view != keepView &&
(view.modes.editing == 'is' || view.modes.selected == 'is')) {
view.disableEdit();
}
});
_.each(this.facetViews, function(view) {
if (view && view != keepView &&
(view.modes.editing == 'is' || view.modes.selected == 'is')) {
view.disableEdit();
view.deselectFacet();
}
});
this.flags.allSelected = false;
this.removeFocus();
$(document).unbind('click.selectAllFacets');
},
// Resize all inputs to account for extra keystrokes which may be changing the facet
// width incorrectly. This is a safety check to ensure inputs are correctly sized.
resizeFacets : function(view) {
_.each(this.facetViews, function(facetView, i) {
if (!view || facetView == view) {
facetView.resize();
}
});
},
// Handles keydown events on the document. Used to complete the Cmd+A deletion, and
// blurring focus.
_maybeDisableFacets : function(e) {
if (this.flags.allSelected && VS.app.hotkeys.key(e) == 'backspace') {
e.preventDefault();
this.clearSearch(e);
return false;
} else if (this.flags.allSelected && VS.app.hotkeys.printable(e)) {
this.clearSearch(e);
}
},
// # Focusing Facets #
// Move focus between facets and inputs. Takes a direction as well as many options
// for skipping over inputs and only to facets, placement of cursor position in facet
// (i.e. at the end), and selecting the text in the input/facet.
focusNextFacet : function(currentView, direction, options) {
options = options || {};
var viewCount = this.facetViews.length;
var viewPosition = options.viewPosition || this.viewPosition(currentView);
if (!options.skipToFacet) {
// Correct for bouncing between matching text and facet arrays.
if (currentView.type == 'text' && direction > 0) direction -= 1;
if (currentView.type == 'facet' && direction < 0) direction += 1;
} else if (options.skipToFacet && currentView.type == 'text' &&
viewCount == viewPosition && direction >= 0) {
// Special case of looping around to a facet from the last search input box.
return false;
}
var view, next = Math.min(viewCount, viewPosition + direction);
if (currentView.type == 'text') {
if (next >= 0 && next < viewCount) {
view = this.facetViews[next];
} else if (next == viewCount) {
view = this.inputViews[this.inputViews.length-1];
}
if (view && options.selectFacet && view.type == 'facet') {
view.selectFacet();
} else if (view) {
view.enableEdit();
view.setCursorAtEnd(direction || options.startAtEnd);
}
} else if (currentView.type == 'facet') {
if (options.skipToFacet) {
if (next >= viewCount || next < 0) {
view = _.last(this.inputViews);
view.enableEdit();
} else {
view = this.facetViews[next];
view.enableEdit();
view.setCursorAtEnd(direction || options.startAtEnd);
}
} else {
view = this.inputViews[next];
view.enableEdit();
}
}
if (options.selectText) view.selectText();
this.resizeFacets();
return true;
},
maybeFocusSearch : function(e) {
if ($(e.target).is('.VS-search-box') ||
$(e.target).is('.VS-search-inner') ||
e.type == 'keydown') {
this.focusSearch(e);
}
},
// Bring focus to last input field.
focusSearch : function(e, selectText) {
var view = this.inputViews[this.inputViews.length-1];
view.enableEdit(selectText);
if (!selectText) view.setCursorAtEnd(-1);
if (e.type == 'keydown') {
view.keydown(e);
view.box.trigger('keydown');
}
_.defer(_.bind(function() {
if (!this.$('input:focus').length) {
view.enableEdit(selectText);
}
}, this));
},
// Double-clicking on the search wrapper should select the existing text in
// the last search input. Also start the triple-click timer.
highlightSearch : function(e) {
if ($(e.target).is('.VS-search-box') ||
$(e.target).is('.VS-search-inner') ||
e.type == 'keydown') {
var lastinput = this.inputViews[this.inputViews.length-1];
lastinput.startTripleClickTimer();
this.focusSearch(e, true);
}
},
maybeTripleClick : function(e) {
var lastinput = this.inputViews[this.inputViews.length-1];
return lastinput.maybeTripleClick(e);
},
// Used to show the user is focused on some input inside the search box.
addFocus : function() {
this.app.options.callbacks.focus();
this.$('.VS-search-box').addClass('VS-focus');
},
// User is no longer focused on anything in the search box.
removeFocus : function() {
this.app.options.callbacks.blur();
var focus = _.any(this.facetViews.concat(this.inputViews), function(view) {
return view.isFocused();
});
if (!focus) this.$('.VS-search-box').removeClass('VS-focus');
},
// Show a menu which adds pre-defined facets to the search box. This is unused for now.
showFacetCategoryMenu : function(e) {
e.preventDefault();
e.stopPropagation();
if (this.facetCategoryMenu && this.facetCategoryMenu.modes.open == 'is') {
return this.facetCategoryMenu.close();
}
var items = [
{title: 'Account', onClick: _.bind(this.addFacet, this, 'account', '')},
{title: 'Project', onClick: _.bind(this.addFacet, this, 'project', '')},
{title: 'Filter', onClick: _.bind(this.addFacet, this, 'filter', '')},
{title: 'Access', onClick: _.bind(this.addFacet, this, 'access', '')}
];
var menu = this.facetCategoryMenu || (this.facetCategoryMenu = new dc.ui.Menu({
items : items,
standalone : true
}));
this.$('.VS-icon-search').after(menu.render().open().content);
return false;
}
});
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// This is the visual search facet that holds the category and its autocompleted
// input field.
VS.ui.SearchFacet = Backbone.View.extend({
type : 'facet',
className : 'search_facet',
events : {
'click .category' : 'selectFacet',
'keydown input' : 'keydown',
'mousedown input' : 'enableEdit',
'mouseover .VS-icon-cancel' : 'showDelete',
'mouseout .VS-icon-cancel' : 'hideDelete',
'click .VS-icon-cancel' : 'remove'
},
initialize : function(options) {
this.flags = {
canClose : false
};
_.bindAll(this, 'set', 'keydown', 'deselectFacet', 'deferDisableEdit');
},
// Rendering the facet sets up autocompletion, events on blur, and populates
// the facet's input with its starting value.
render : function() {
$(this.el).html(JST['search_facet']({
model : this.model
}));
this.setMode('not', 'editing');
this.setMode('not', 'selected');
this.box = this.$('input');
this.box.val(this.model.label());
this.box.bind('blur', this.deferDisableEdit);
// Handle paste events with `propertychange`
this.box.bind('input propertychange', this.keydown);
this.setupAutocomplete();
return this;
},
// This method is used to setup the facet's input to auto-grow.
// This is defered in the searchBox so it can be attached to the
// DOM to get the correct font-size.
calculateSize : function() {
this.box.autoGrowInput();
this.box.unbind('updated.autogrow');
this.box.bind('updated.autogrow', _.bind(this.moveAutocomplete, this));
},
// Forces a recalculation of this facet's input field's value. Called when
// the facet is focused, removed, or otherwise modified.
resize : function(e) {
this.box.trigger('resize.autogrow', e);
},
// Watches the facet's input field to see if it matches the beginnings of
// words in `autocompleteValues`, which is different for every category.
// If the value, when selected from the autocompletion menu, is different
// than what it was, commit the facet and search for it.
setupAutocomplete : function() {
this.box.autocomplete({
source : _.bind(this.autocompleteValues, this),
minLength : 0,
delay : 0,
autoFocus : true,
position : {offset : "0 5"},
create : _.bind(function(e, ui) {
$(this.el).find('.ui-autocomplete-input').css('z-index','auto');
}, this),
select : _.bind(function(e, ui) {
e.preventDefault();
var originalValue = this.model.get('value');
this.set(ui.item.value);
if (originalValue != ui.item.value || this.box.val() != ui.item.value) {
if (this.options.app.options.autosearch) {
this.search(e);
} else {
this.options.app.searchBox.renderFacets();
this.options.app.searchBox.focusNextFacet(this, 1, {viewPosition: this.options.order});
}
}
return false;
}, this),
open : _.bind(function(e, ui) {
var box = this.box;
this.box.autocomplete('widget').find('.ui-menu-item').each(function() {
var $value = $(this),
autoCompleteData = $value.data('item.autocomplete') || $value.data('ui-autocomplete-item');
if (autoCompleteData['value'] == box.val() && box.data('uiAutocomplete').menu.activate) {
box.data('uiAutocomplete').menu.activate(new $.Event("mouseover"), $value);
}
});
}, this)
});
this.box.autocomplete('widget').addClass('VS-interface');
},
// As the facet's input field grows, it may move to the next line in the
// search box. `autoGrowInput` triggers an `updated` event on the input
// field, which is bound to this method to move the autocomplete menu.
moveAutocomplete : function() {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) {
autocomplete.menu.element.position({
my : "left top",
at : "left bottom",
of : this.box.data('uiAutocomplete').element,
collision : "flip",
offset : "0 5"
});
}
},
// When a user enters a facet and it is being edited, immediately show
// the autocomplete menu and size it to match the contents.
searchAutocomplete : function(e) {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) {
var menu = autocomplete.menu.element;
autocomplete.search();
// Resize the menu based on the correctly measured width of what's bigger:
// the menu's original size or the menu items' new size.
menu.outerWidth(Math.max(
menu.width('').outerWidth(),
autocomplete.element.outerWidth()
));
}
},
// Closes the autocomplete menu. Called on disabling, selecting, deselecting,
// and anything else that takes focus out of the facet's input field.
closeAutocomplete : function() {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) autocomplete.close();
},
// Search terms used in the autocomplete menu. These are specific to the facet,
// and only match for the facet's category. The values are then matched on the
// first letter of any word in matches, and finally sorted according to the
// value's own category. You can pass `preserveOrder` as an option in the
// `facetMatches` callback to skip any further ordering done client-side.
autocompleteValues : function(req, resp) {
var category = this.model.get('category');
var value = this.model.get('value');
var searchTerm = req.term;
this.options.app.options.callbacks.valueMatches(category, searchTerm, function(matches, options) {
options = options || {};
matches = matches || [];
if (searchTerm && value != searchTerm) {
if (options.preserveMatches) {
resp(matches);
} else {
var re = VS.utils.inflector.escapeRegExp(searchTerm || '');
var matcher = new RegExp('\\b' + re, 'i');
matches = $.grep(matches, function(item) {
return matcher.test(item) ||
matcher.test(item.value) ||
matcher.test(item.label);
});
}
}
if (options.preserveOrder) {
resp(matches);
} else {
resp(_.sortBy(matches, function(match) {
if (match == value || match.value == value) return '';
else return match;
}));
}
});
},
// Sets the facet's model's value.
set : function(value) {
if (!value) return;
this.model.set({'value': value});
},
// Before the searchBox performs a search, we need to close the
// autocomplete menu.
search : function(e, direction) {
if (!direction) direction = 1;
this.closeAutocomplete();
this.options.app.searchBox.searchEvent(e);
_.defer(_.bind(function() {
this.options.app.searchBox.focusNextFacet(this, direction, {viewPosition: this.options.order});
}, this));
},
// Begin editing the facet's input. This is called when the user enters
// the input either from another facet or directly clicking on it.
//
// This method tells all other facets and inputs to disable so it can have
// the sole focus. It also prepares the autocompletion menu.
enableEdit : function() {
if (this.modes.editing != 'is') {
this.setMode('is', 'editing');
this.deselectFacet();
if (this.box.val() == '') {
this.box.val(this.model.get('value'));
}
}
this.flags.canClose = false;
this.options.app.searchBox.disableFacets(this);
this.options.app.searchBox.addFocus();
_.defer(_.bind(function() {
this.options.app.searchBox.addFocus();
}, this));
this.resize();
this.searchAutocomplete();
this.box.focus();
},
// When the user blurs the input, they may either be going to another input
// or off the search box entirely. If they go to another input, this facet
// will be instantly disabled, and the canClose flag will be turned back off.
//
// However, if the user clicks elsewhere on the page, this method starts a timer
// that checks if any of the other inputs are selected or are being edited. If
// not, then it can finally close itself and its autocomplete menu.
deferDisableEdit : function() {
this.flags.canClose = true;
_.delay(_.bind(function() {
if (this.flags.canClose && !this.box.is(':focus') &&
this.modes.editing == 'is' && this.modes.selected != 'is') {
this.disableEdit();
}
}, this), 250);
},
// Called either by other facets receiving focus or by the timer in `deferDisableEdit`,
// this method will turn off the facet, remove any text selection, and close
// the autocomplete menu.
disableEdit : function() {
var newFacetQuery = VS.utils.inflector.trim(this.box.val());
if (newFacetQuery != this.model.get('value')) {
this.set(newFacetQuery);
}
this.flags.canClose = false;
this.box.selectRange(0, 0);
this.box.blur();
this.setMode('not', 'editing');
this.closeAutocomplete();
this.options.app.searchBox.removeFocus();
},
// Selects the facet, which blurs the facet's input and highlights the facet.
// If this is the only facet being selected (and not part of a select all event),
// we attach a mouse/keyboard watcher to check if the next action by the user
// should delete this facet or just deselect it.
selectFacet : function(e) {
if (e) e.preventDefault();
var allSelected = this.options.app.searchBox.allSelected();
if (this.modes.selected == 'is') return;
if (this.box.is(':focus')) {
this.box.setCursorPosition(0);
this.box.blur();
}
this.flags.canClose = false;
this.closeAutocomplete();
this.setMode('is', 'selected');
this.setMode('not', 'editing');
if (!allSelected || e) {
$(document).unbind('keydown.facet', this.keydown);
$(document).unbind('click.facet', this.deselectFacet);
_.defer(_.bind(function() {
$(document).unbind('keydown.facet').bind('keydown.facet', this.keydown);
$(document).unbind('click.facet').one('click.facet', this.deselectFacet);
}, this));
this.options.app.searchBox.disableFacets(this);
this.options.app.searchBox.addFocus();
}
return false;
},
// Turns off highlighting on the facet. Called in a variety of ways, this
// only deselects the facet if it is selected, and then cleans up the
// keyboard/mouse watchers that were created when the facet was first
// selected.
deselectFacet : function(e) {
if (e) e.preventDefault();
if (this.modes.selected == 'is') {
this.setMode('not', 'selected');
this.closeAutocomplete();
this.options.app.searchBox.removeFocus();
}
$(document).unbind('keydown.facet', this.keydown);
$(document).unbind('click.facet', this.deselectFacet);
return false;
},
// Is the user currently focused in this facet's input field?
isFocused : function() {
return this.box.is(':focus');
},
// Hovering over the delete button styles the facet so the user knows that
// the delete button will kill the entire facet.
showDelete : function() {
$(this.el).addClass('search_facet_maybe_delete');
},
// On `mouseout`, the user is no longer hovering on the delete button.
hideDelete : function() {
$(this.el).removeClass('search_facet_maybe_delete');
},
// When switching between facets, depending on the direction the cursor is
// coming from, the cursor in this facet's input field should match the original
// direction.
setCursorAtEnd : function(direction) {
if (direction == -1) {
this.box.setCursorPosition(this.box.val().length);
} else {
this.box.setCursorPosition(0);
}
},
// Deletes the facet and sends the cursor over to the nearest input field.
remove : function(e) {
var committed = this.model.get('value');
this.deselectFacet();
this.disableEdit();
this.options.app.searchQuery.remove(this.model);
if (committed && this.options.app.options.autosearch) {
this.search(e, -1);
} else {
this.options.app.searchBox.renderFacets();
this.options.app.searchBox.focusNextFacet(this, -1, {viewPosition: this.options.order});
}
},
// Selects the text in the facet's input field. When the user tabs between
// facets, convention is to highlight the entire field.
selectText: function() {
this.box.selectRange(0, this.box.val().length);
},
// Handles all keyboard inputs when in the facet's input field. This checks
// for movement between facets and inputs, entering a new value that needs
// to be autocompleted, as well as the removal of this facet.
keydown : function(e) {
var key = VS.app.hotkeys.key(e);
if (key == 'enter' && this.box.val()) {
this.disableEdit();
this.search(e);
} else if (key == 'left') {
if (this.modes.selected == 'is') {
this.deselectFacet();
this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
} else if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
this.selectFacet();
}
} else if (key == 'right') {
if (this.modes.selected == 'is') {
e.preventDefault();
this.deselectFacet();
this.setCursorAtEnd(0);
this.enableEdit();
} else if (this.box.getCursorPosition() == this.box.val().length) {
e.preventDefault();
this.disableEdit();
this.options.app.searchBox.focusNextFacet(this, 1);
}
} else if (VS.app.hotkeys.shift && key == 'tab') {
e.preventDefault();
this.options.app.searchBox.focusNextFacet(this, -1, {
startAtEnd : -1,
skipToFacet : true,
selectText : true
});
} else if (key == 'tab') {
e.preventDefault();
this.options.app.searchBox.focusNextFacet(this, 1, {
skipToFacet : true,
selectText : true
});
} else if (VS.app.hotkeys.command && (e.which == 97 || e.which == 65)) {
e.preventDefault();
this.options.app.searchBox.selectAllFacets();
return false;
} else if (VS.app.hotkeys.printable(e) && this.modes.selected == 'is') {
this.options.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
this.remove(e);
} else if (key == 'backspace') {
if (this.modes.selected == 'is') {
e.preventDefault();
this.remove(e);
} else if (this.box.getCursorPosition() == 0 &&
!this.box.getSelection().length) {
e.preventDefault();
this.selectFacet();
}
}
// Handle paste events
if (e.which == null) {
// this.searchAutocomplete(e);
_.defer(_.bind(this.resize, this, e));
} else {
this.resize(e);
}
}
});
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// This is the visual search input that is responsible for creating new facets.
// There is one input placed in between all facets.
VS.ui.SearchInput = Backbone.View.extend({
type : 'text',
className : 'search_input ui-menu',
events : {
'keypress input' : 'keypress',
'keydown input' : 'keydown',
'click input' : 'maybeTripleClick',
'dblclick input' : 'startTripleClickTimer'
},
initialize : function() {
this.app = this.options.app;
this.flags = {
canClose : false
};
_.bindAll(this, 'removeFocus', 'addFocus', 'moveAutocomplete', 'deferDisableEdit');
},
// Rendering the input sets up autocomplete, events on focusing and blurring
// the input, and the auto-grow of the input.
render : function() {
$(this.el).html(JST['search_input']({}));
this.setMode('not', 'editing');
this.setMode('not', 'selected');
this.box = this.$('input');
this.box.autoGrowInput();
this.box.bind('updated.autogrow', this.moveAutocomplete);
this.box.bind('blur', this.deferDisableEdit);
this.box.bind('focus', this.addFocus);
this.setupAutocomplete();
return this;
},
// Watches the input and presents an autocompleted menu, taking the
// remainder of the input field and adding a separate facet for it.
//
// See `addTextFacetRemainder` for explanation on how the remainder works.
setupAutocomplete : function() {
this.box.autocomplete({
minLength : this.options.showFacets ? 0 : 1,
delay : 50,
autoFocus : true,
position : {offset : "0 -1"},
source : _.bind(this.autocompleteValues, this),
create : _.bind(function(e, ui) {
$(this.el).find('.ui-autocomplete-input').css('z-index','auto');
}, this),
select : _.bind(function(e, ui) {
e.preventDefault();
// stopPropogation does weird things in jquery-ui 1.9
// e.stopPropagation();
var remainder = this.addTextFacetRemainder(ui.item.value);
var position = this.options.position + (remainder ? 1 : 0);
this.app.searchBox.addFacet(ui.item instanceof String ? ui.item : ui.item.value, '', position);
return false;
}, this)
});
// Renders the results grouped by the categories they belong to.
this.box.data('uiAutocomplete')._renderMenu = function(ul, items) {
var category = '';
_.each(items, _.bind(function(item, i) {
if (item.category && item.category != category) {
ul.append('<li class="ui-autocomplete-category">'+item.category+'</li>');
category = item.category;
}
if(this._renderItemData) {
this._renderItemData(ul, item);
} else {
this._renderItem(ul, item);
}
}, this));
};
this.box.autocomplete('widget').addClass('VS-interface');
},
// Search terms used in the autocomplete menu. The values are matched on the
// first letter of any word in matches, and finally sorted according to the
// value's own category. You can pass `preserveOrder` as an option in the
// `facetMatches` callback to skip any further ordering done client-side.
autocompleteValues : function(req, resp) {
var searchTerm = req.term;
var lastWord = searchTerm.match(/\w+\*?$/); // Autocomplete only last word.
var re = VS.utils.inflector.escapeRegExp(lastWord && lastWord[0] || '');
this.app.options.callbacks.facetMatches(function(prefixes, options) {
options = options || {};
prefixes = prefixes || [];
// Only match from the beginning of the word.
var matcher = new RegExp('^' + re, 'i');
var matches = $.grep(prefixes, function(item) {
return item && matcher.test(item.label || item);
});
if (options.preserveOrder) {
resp(matches);
} else {
resp(_.sortBy(matches, function(match) {
if (match.label) return match.category + '-' + match.label;
else return match;
}));
}
});
},
// Closes the autocomplete menu. Called on disabling, selecting, deselecting,
// and anything else that takes focus out of the facet's input field.
closeAutocomplete : function() {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) autocomplete.close();
},
// As the input field grows, it may move to the next line in the
// search box. `autoGrowInput` triggers an `updated` event on the input
// field, which is bound to this method to move the autocomplete menu.
moveAutocomplete : function() {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) {
autocomplete.menu.element.position({
my : "left top",
at : "left bottom",
of : this.box.data('uiAutocomplete').element,
collision : "none",
offset : '0 -1'
});
}
},
// When a user enters a facet and it is being edited, immediately show
// the autocomplete menu and size it to match the contents.
searchAutocomplete : function(e) {
var autocomplete = this.box.data('uiAutocomplete');
if (autocomplete) {
var menu = autocomplete.menu.element;
autocomplete.search();
// Resize the menu based on the correctly measured width of what's bigger:
// the menu's original size or the menu items' new size.
menu.outerWidth(Math.max(
menu.width('').outerWidth(),
autocomplete.element.outerWidth()
));
}
},
// If a user searches for "word word category", the category would be
// matched and autocompleted, and when selected, the "word word" would
// also be caught as the remainder and then added in its own facet.
addTextFacetRemainder : function(facetValue) {
var boxValue = this.box.val();
var lastWord = boxValue.match(/\b(\w+)$/);
if (!lastWord) {
return '';
}
var matcher = new RegExp(lastWord[0], "i");
if (facetValue.search(matcher) == 0) {
boxValue = boxValue.replace(/\b(\w+)$/, '');
}
boxValue = boxValue.replace('^\s+|\s+$', '');
if (boxValue) {
this.app.searchBox.addFacet(this.app.options.remainder, boxValue, this.options.position);
}
return boxValue;
},
// Directly called to focus the input. This is different from `addFocus`
// because this is not called by a focus event. This instead calls a
// focus event causing the input to become focused.
enableEdit : function(selectText) {
this.addFocus();
if (selectText) {
this.selectText();
}
this.box.focus();
},
// Event called on user focus on the input. Tells all other input and facets
// to give up focus, and starts revving the autocomplete.
addFocus : function() {
this.flags.canClose = false;
if (!this.app.searchBox.allSelected()) {
this.app.searchBox.disableFacets(this);
}
this.app.searchBox.addFocus();
this.setMode('is', 'editing');
this.setMode('not', 'selected');
if (!this.app.searchBox.allSelected()) {
this.searchAutocomplete();
}
},
// Directly called to blur the input. This is different from `removeFocus`
// because this is not called by a blur event.
disableEdit : function() {
this.box.blur();
this.removeFocus();
},
// Event called when user blur's the input, either through the keyboard tabbing
// away or the mouse clicking off. Cleans up
removeFocus : function() {
this.flags.canClose = false;
this.app.searchBox.removeFocus();
this.setMode('not', 'editing');
this.setMode('not', 'selected');
this.closeAutocomplete();
},
// When the user blurs the input, they may either be going to another input
// or off the search box entirely. If they go to another input, this facet
// will be instantly disabled, and the canClose flag will be turned back off.
//
// However, if the user clicks elsewhere on the page, this method starts a timer
// that checks if any of the other inputs are selected or are being edited. If
// not, then it can finally close itself and its autocomplete menu.
deferDisableEdit : function() {
this.flags.canClose = true;
_.delay(_.bind(function() {
if (this.flags.canClose &&
!this.box.is(':focus') &&
this.modes.editing == 'is') {
this.disableEdit();
}
}, this), 250);
},
// Starts a timer that will cause a triple-click, which highlights all facets.
startTripleClickTimer : function() {
this.tripleClickTimer = setTimeout(_.bind(function() {
this.tripleClickTimer = null;
}, this), 500);
},
// Event on click that checks if a triple click is in play. The
// `tripleClickTimer` is counting down, ready to be engaged and intercept
// the click event to force a select all instead.
maybeTripleClick : function(e) {
if (!!this.tripleClickTimer) {
e.preventDefault();
this.app.searchBox.selectAllFacets();
return false;
}
},
// Is the user currently focused in the input field?
isFocused : function() {
return this.box.is(':focus');
},
// When serializing the facets, the inputs need to also have their values represented,
// in case they contain text that is not yet faceted (but will be once the search is
// completed).
value : function() {
return this.box.val();
},
// When switching between facets and inputs, depending on the direction the cursor
// is coming from, the cursor in this facet's input field should match the original
// direction.
setCursorAtEnd : function(direction) {
if (direction == -1) {
this.box.setCursorPosition(this.box.val().length);
} else {
this.box.setCursorPosition(0);
}
},
// Selects the entire range of text in the input. Useful when tabbing between inputs
// and facets.
selectText : function() {
this.box.selectRange(0, this.box.val().length);
if (!this.app.searchBox.allSelected()) {
this.box.focus();
} else {
this.setMode('is', 'selected');
}
},
// Before the searchBox performs a search, we need to close the
// autocomplete menu.
search : function(e, direction) {
if (!direction) direction = 0;
this.closeAutocomplete();
this.app.searchBox.searchEvent(e);
_.defer(_.bind(function() {
this.app.searchBox.focusNextFacet(this, direction);
}, this));
},
// Callback fired on key press in the search box. We search when they hit return.
keypress : function(e) {
var key = VS.app.hotkeys.key(e);
if (key == 'enter') {
return this.search(e, 100);
} else if (VS.app.hotkeys.colon(e)) {
this.box.trigger('resize.autogrow', e);
var query = this.box.val();
var prefixes = [];
if (this.app.options.callbacks.facetMatches) {
this.app.options.callbacks.facetMatches(function(p) {
prefixes = p;
});
}
var labels = _.map(prefixes, function(prefix) {
if (prefix.label) return prefix.label;
else return prefix;
});
if (_.contains(labels, query)) {
e.preventDefault();
var remainder = this.addTextFacetRemainder(query);
var position = this.options.position + (remainder?1:0);
this.app.searchBox.addFacet(query, '', position);
return false;
}
} else if (key == 'backspace') {
if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
this.app.searchBox.resizeFacets();
return false;
}
}
},
// Handles all keyboard inputs when in the input field. This checks
// for movement between facets and inputs, entering a new value that needs
// to be autocompleted, as well as stepping between facets with backspace.
keydown : function(e) {
var key = VS.app.hotkeys.key(e);
if (key == 'left') {
if (this.box.getCursorPosition() == 0) {
e.preventDefault();
this.app.searchBox.focusNextFacet(this, -1, {startAtEnd: -1});
}
} else if (key == 'right') {
if (this.box.getCursorPosition() == this.box.val().length) {
e.preventDefault();
this.app.searchBox.focusNextFacet(this, 1, {selectFacet: true});
}
} else if (VS.app.hotkeys.shift && key == 'tab') {
e.preventDefault();
this.app.searchBox.focusNextFacet(this, -1, {selectText: true});
} else if (key == 'tab') {
var value = this.box.val();
if (value.length) {
e.preventDefault();
var remainder = this.addTextFacetRemainder(value);
var position = this.options.position + (remainder?1:0);
if (value != remainder) {
this.app.searchBox.addFacet(value, '', position);
}
} else {
var foundFacet = this.app.searchBox.focusNextFacet(this, 0, {
skipToFacet: true,
selectText: true
});
if (foundFacet) {
e.preventDefault();
}
}
} else if (VS.app.hotkeys.command &&
String.fromCharCode(e.which).toLowerCase() == 'a') {
e.preventDefault();
this.app.searchBox.selectAllFacets();
return false;
} else if (key == 'backspace' && !this.app.searchBox.allSelected()) {
if (this.box.getCursorPosition() == 0 && !this.box.getSelection().length) {
e.preventDefault();
this.app.searchBox.focusNextFacet(this, -1, {backspace: true});
return false;
}
} else if (key == 'end') {
var view = this.app.searchBox.inputViews[this.app.searchBox.inputViews.length-1];
view.setCursorAtEnd(-1);
} else if (key == 'home') {
var view = this.app.searchBox.inputViews[0];
view.setCursorAtEnd(-1);
}
this.box.trigger('resize.autogrow', e);
}
});
})();
(function(){
var $ = jQuery; // Handle namespaced jQuery
// Makes the view enter a mode. Modes have both a 'mode' and a 'group',
// and are mutually exclusive with any other modes in the same group.
// Setting will update the view's modes hash, as well as set an HTML class
// of *[mode]_[group]* on the view's element. Convenient way to swap styles
// and behavior.
Backbone.View.prototype.setMode = function(mode, group) {
this.modes || (this.modes = {});
if (this.modes[group] === mode) return;
$(this.el).setMode(mode, group);
this.modes[group] = mode;
};
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// DocumentCloud workspace hotkeys. To tell if a key is currently being pressed,
// just ask `VS.app.hotkeys.[key]` on `keypress`, or ask `VS.app.hotkeys.key(e)`
// on `keydown`.
//
// For the most headache-free way to use this utility, check modifier keys,
// like shift and command, with `VS.app.hotkeys.shift`, and check every other
// key with `VS.app.hotkeys.key(e) == 'key_name'`.
VS.app.hotkeys = {
// Keys that will be mapped to the `hotkeys` namespace.
KEYS: {
'16': 'shift',
'17': 'command',
'91': 'command',
'93': 'command',
'224': 'command',
'13': 'enter',
'37': 'left',
'38': 'upArrow',
'39': 'right',
'40': 'downArrow',
'46': 'delete',
'8': 'backspace',
'35': 'end',
'36': 'home',
'9': 'tab',
'188': 'comma'
},
// Binds global keydown and keyup events to listen for keys that match `this.KEYS`.
initialize : function() {
_.bindAll(this, 'down', 'up', 'blur');
$(document).bind('keydown', this.down);
$(document).bind('keyup', this.up);
$(window).bind('blur', this.blur);
},
// On `keydown`, turn on all keys that match.
down : function(e) {
var key = this.KEYS[e.which];
if (key) this[key] = true;
},
// On `keyup`, turn off all keys that match.
up : function(e) {
var key = this.KEYS[e.which];
if (key) this[key] = false;
},
// If an input is blurred, all keys need to be turned off, since they are no longer
// able to modify the document.
blur : function(e) {
for (var key in this.KEYS) this[this.KEYS[key]] = false;
},
// Check a key from an event and return the common english name.
key : function(e) {
return this.KEYS[e.which];
},
// Colon is special, since the value is different between browsers.
colon : function(e) {
var charCode = e.which;
return charCode && String.fromCharCode(charCode) == ":";
},
// Check a key from an event and match it against any known characters.
// The `keyCode` is different depending on the event type: `keydown` vs. `keypress`.
//
// These were determined by looping through every `keyCode` and `charCode` that
// resulted from `keydown` and `keypress` events and counting what was printable.
printable : function(e) {
var code = e.which;
if (e.type == 'keydown') {
if (code == 32 || // space
(code >= 48 && code <= 90) || // 0-1a-z
(code >= 96 && code <= 111) || // 0-9+-/*.
(code >= 186 && code <= 192) || // ;=,-./^
(code >= 219 && code <= 222)) { // (\)'
return true;
}
} else {
// [space]!"#$%&'()*+,-.0-9:;<=>?@A-Z[\]^_`a-z{|} and unicode characters
if ((code >= 32 && code <= 126) ||
(code >= 160 && code <= 500) ||
(String.fromCharCode(code) == ":")) {
return true;
}
}
return false;
}
};
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// Naive English transformations on words. Only used for a few transformations
// in VisualSearch.js.
VS.utils.inflector = {
// Delegate to the ECMA5 String.prototype.trim function, if available.
trim : function(s) {
return s.trim ? s.trim() : s.replace(/^\s+|\s+$/g, '');
},
// Escape strings that are going to be used in a regex. Escapes punctuation
// that would be incorrect in a regex.
escapeRegExp : function(s) {
return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1');
}
};
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
$.fn.extend({
// Makes the selector enter a mode. Modes have both a 'mode' and a 'group',
// and are mutually exclusive with any other modes in the same group.
// Setting will update the view's modes hash, as well as set an HTML class
// of *[mode]_[group]* on the view's element. Convenient way to swap styles
// and behavior.
setMode : function(state, group) {
group = group || 'mode';
var re = new RegExp("\\w+_" + group + "(\\s|$)", 'g');
var mode = (state === null) ? "" : state + "_" + group;
this.each(function() {
this.className = (this.className.replace(re, '')+' '+mode)
.replace(/\s\s/g, ' ');
});
return mode;
},
// When attached to an input element, this will cause the width of the input
// to match its contents. This calculates the width of the contents of the input
// by measuring a hidden shadow div that should match the styling of the input.
autoGrowInput: function() {
return this.each(function() {
var $input = $(this);
var $tester = $('<div />').css({
opacity : 0,
top : -9999,
left : -9999,
position : 'absolute',
whiteSpace : 'nowrap'
}).addClass('VS-input-width-tester').addClass('VS-interface');
// Watch for input value changes on all of these events. `resize`
// event is called explicitly when the input has been changed without
// a single keypress.
var events = 'keydown.autogrow keypress.autogrow ' +
'resize.autogrow change.autogrow';
$input.next('.VS-input-width-tester').remove();
$input.after($tester);
$input.unbind(events).bind(events, function(e, realEvent) {
if (realEvent) e = realEvent;
var value = $input.val();
// Watching for the backspace key is tricky because it may not
// actually be deleting the character, but instead the key gets
// redirected to move the cursor from facet to facet.
if (VS.app.hotkeys.key(e) == 'backspace') {
var position = $input.getCursorPosition();
if (position > 0) value = value.slice(0, position-1) +
value.slice(position, value.length);
} else if (VS.app.hotkeys.printable(e) &&
!VS.app.hotkeys.command) {
value += String.fromCharCode(e.which);
}
value = value.replace(/&/g, '&amp;')
.replace(/\s/g,'&nbsp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
$tester.html(value);
$input.width($tester.width() + 3 + parseInt($input.css('min-width')));
$input.trigger('updated.autogrow');
});
// Sets the width of the input on initialization.
$input.trigger('resize.autogrow');
});
},
// Cross-browser method used for calculating where the cursor is in an
// input field.
getCursorPosition: function() {
var position = 0;
var input = this.get(0);
if (document.selection) { // IE
input.focus();
var sel = document.selection.createRange();
var selLen = document.selection.createRange().text.length;
sel.moveStart('character', -input.value.length);
position = sel.text.length - selLen;
} else if (input && $(input).is(':visible') &&
input.selectionStart != null) { // Firefox/Safari
position = input.selectionStart;
}
return position;
},
// A simple proxy for `selectRange` that sets the cursor position in an
// input field.
setCursorPosition: function(position) {
return this.each(function() {
return $(this).selectRange(position, position);
});
},
// Cross-browser way to select text in an input field.
selectRange: function(start, end) {
return this.filter(':visible').each(function() {
if (this.setSelectionRange) { // FF/Webkit
this.focus();
this.setSelectionRange(start, end);
} else if (this.createTextRange) { // IE
var range = this.createTextRange();
range.collapse(true);
range.moveEnd('character', end);
range.moveStart('character', start);
if (end - start >= 0) range.select();
}
});
},
// Returns an object that contains the text selection range values for
// an input field.
getSelection: function() {
var input = this[0];
if (input.selectionStart != null) { // FF/Webkit
var start = input.selectionStart;
var end = input.selectionEnd;
return {
start : start,
end : end,
length : end-start,
text : input.value.substr(start, end-start)
};
} else if (document.selection) { // IE
var range = document.selection.createRange();
if (range) {
var textRange = input.createTextRange();
var copyRange = textRange.duplicate();
textRange.moveToBookmark(range.getBookmark());
copyRange.setEndPoint('EndToStart', textRange);
var start = copyRange.text.length;
var end = start + range.text.length;
return {
start : start,
end : end,
length : end-start,
text : range.text
};
}
}
return {start: 0, end: 0, length: 0};
}
});
// Debugging in Internet Explorer. This allows you to use
// `console.log(['message', var1, var2, ...])`. Just remove the `false` and
// add your console.logs. This will automatically stringify objects using
// `JSON.stringify', so you can read what's going out. Think of this as a
// *Diet Firebug Lite Zero with Lemon*.
if (false) {
window.console = {};
var _$ied;
window.console.log = function(msg) {
if (_.isArray(msg)) {
var message = msg[0];
var vars = _.map(msg.slice(1), function(arg) {
return JSON.stringify(arg);
}).join(' - ');
}
if(!_$ied){
_$ied = $('<div><ol></ol></div>').css({
'position': 'fixed',
'bottom': 10,
'left': 10,
'zIndex': 20000,
'width': $('body').width() - 80,
'border': '1px solid #000',
'padding': '10px',
'backgroundColor': '#fff',
'fontFamily': 'arial,helvetica,sans-serif',
'fontSize': '11px'
});
$('body').append(_$ied);
}
var $message = $('<li>'+message+' - '+vars+'</li>').css({
'borderBottom': '1px solid #999999'
});
_$ied.find('ol').append($message);
_.delay(function() {
$message.fadeOut(500);
}, 5000);
};
}
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// Used to extract keywords and facets from the free text search.
var QUOTES_RE = "('[^']+'|\"[^\"]+\")";
var FREETEXT_RE = "('[^']+'|\"[^\"]+\"|[^'\"\\s]\\S*)";
var CATEGORY_RE = FREETEXT_RE + ':\\s*';
VS.app.SearchParser = {
// Matches `category: "free text"`, with and without quotes.
ALL_FIELDS : new RegExp(CATEGORY_RE + FREETEXT_RE, 'g'),
// Matches a single category without the text. Used to correctly extract facets.
CATEGORY : new RegExp(CATEGORY_RE),
// Called to parse a query into a collection of `SearchFacet` models.
parse : function(instance, query) {
var searchFacets = this._extractAllFacets(instance, query);
instance.searchQuery.reset(searchFacets);
return searchFacets;
},
// Walks the query and extracts facets, categories, and free text.
_extractAllFacets : function(instance, query) {
var facets = [];
var originalQuery = query;
while (query) {
var category, value;
originalQuery = query;
var field = this._extractNextField(query);
if (!field) {
category = instance.options.remainder;
value = this._extractSearchText(query);
query = VS.utils.inflector.trim(query.replace(value, ''));
} else if (field.indexOf(':') != -1) {
category = field.match(this.CATEGORY)[1].replace(/(^['"]|['"]$)/g, '');
value = field.replace(this.CATEGORY, '').replace(/(^['"]|['"]$)/g, '');
query = VS.utils.inflector.trim(query.replace(field, ''));
} else if (field.indexOf(':') == -1) {
category = instance.options.remainder;
value = field;
query = VS.utils.inflector.trim(query.replace(value, ''));
}
if (category && value) {
var searchFacet = new VS.model.SearchFacet({
category : category,
value : VS.utils.inflector.trim(value),
app : instance
});
facets.push(searchFacet);
}
if (originalQuery == query) break;
}
return facets;
},
// Extracts the first field found, capturing any free text that comes
// before the category.
_extractNextField : function(query) {
var textRe = new RegExp('^\\s*(\\S+)\\s+(?=' + QUOTES_RE + FREETEXT_RE + ')');
var textMatch = query.match(textRe);
if (textMatch && textMatch.length >= 1) {
return textMatch[1];
} else {
return this._extractFirstField(query);
}
},
// If there is no free text before the facet, extract the category and value.
_extractFirstField : function(query) {
var fields = query.match(this.ALL_FIELDS);
return fields && fields.length && fields[0];
},
// If the found match is not a category and facet, extract the trimmed free text.
_extractSearchText : function(query) {
query = query || '';
var text = VS.utils.inflector.trim(query.replace(this.ALL_FIELDS, ''));
return text;
}
};
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// The model that holds individual search facets and their categories.
// Held in a collection by `VS.app.searchQuery`.
VS.model.SearchFacet = Backbone.Model.extend({
// Extract the category and value and serialize it in preparation for
// turning the entire searchBox into a search query that can be sent
// to the server for parsing and searching.
serialize : function() {
var category = this.quoteCategory(this.get('category'));
var value = VS.utils.inflector.trim(this.get('value'));
var remainder = this.get("app").options.remainder;
if (!value) return '';
if (!_.contains(this.get("app").options.unquotable || [], category) && category != remainder) {
value = this.quoteValue(value);
}
if (category != remainder) {
category = category + ': ';
} else {
category = "";
}
return category + value;
},
// Wrap categories that have spaces or any kind of quote with opposite matching
// quotes to preserve the complex category during serialization.
quoteCategory : function(category) {
var hasDoubleQuote = (/"/).test(category);
var hasSingleQuote = (/'/).test(category);
var hasSpace = (/\s/).test(category);
if (hasDoubleQuote && !hasSingleQuote) {
return "'" + category + "'";
} else if (hasSpace || (hasSingleQuote && !hasDoubleQuote)) {
return '"' + category + '"';
} else {
return category;
}
},
// Wrap values that have quotes in opposite matching quotes. If a value has
// both single and double quotes, just use the double quotes.
quoteValue : function(value) {
var hasDoubleQuote = (/"/).test(value);
var hasSingleQuote = (/'/).test(value);
if (hasDoubleQuote && !hasSingleQuote) {
return "'" + value + "'";
} else {
return '"' + value + '"';
}
},
// If provided, use a custom label instead of the raw value.
label : function() {
return this.get('label') || this.get('value');
}
});
})();
(function() {
var $ = jQuery; // Handle namespaced jQuery
// Collection which holds all of the individual facets (category: value).
// Used for finding and removing specific facets.
VS.model.SearchQuery = Backbone.Collection.extend({
// Model holds the category and value of the facet.
model : VS.model.SearchFacet,
// Turns all of the facets into a single serialized string.
serialize : function() {
return this.map(function(facet){ return facet.serialize(); }).join(' ');
},
facets : function() {
return this.map(function(facet) {
var value = {};
value[facet.get('category')] = facet.get('value');
return value;
});
},
// Find a facet by its category. Multiple facets with the same category
// is fine, but only the first is returned.
find : function(category) {
var facet = this.detect(function(facet) {
return facet.get('category').toLowerCase() == category.toLowerCase();
});
return facet && facet.get('value');
},
// Counts the number of times a specific category is in the search query.
count : function(category) {
return this.select(function(facet) {
return facet.get('category').toLowerCase() == category.toLowerCase();
}).length;
},
// Returns an array of extracted values from each facet in a category.
values : function(category) {
var facets = this.select(function(facet) {
return facet.get('category').toLowerCase() == category.toLowerCase();
});
return _.map(facets, function(facet) { return facet.get('value'); });
},
// Checks all facets for matches of either a category or both category and value.
has : function(category, value) {
return this.any(function(facet) {
var categoryMatched = facet.get('category').toLowerCase() == category.toLowerCase();
if (!value) return categoryMatched;
return categoryMatched && facet.get('value') == value;
});
},
// Used to temporarily hide specific categories and serialize the search query.
withoutCategory : function() {
var categories = _.map(_.toArray(arguments), function(cat) { return cat.toLowerCase(); });
return this.map(function(facet) {
if (!_.include(categories, facet.get('category').toLowerCase())) {
return facet.serialize();
};
}).join(' ');
}
});
})();
(function(){
window.JST = window.JST || {};
window.JST['search_box'] = _.template('<div class="VS-search">\n <div class="VS-search-box-wrapper VS-search-box">\n <div class="VS-icon VS-icon-search"></div>\n <div class="VS-placeholder"></div>\n <div class="VS-search-inner"></div>\n <div class="VS-icon VS-icon-cancel VS-cancel-search-box" title="clear search"></div>\n </div>\n</div>');
window.JST['search_facet'] = _.template('<% if (model.has(\'category\')) { %>\n <div class="category"><%= model.get(\'category\') %>:</div>\n<% } %>\n\n<div class="search_facet_input_container">\n <input type="text" class="search_facet_input ui-menu VS-interface" value="" />\n</div>\n\n<div class="search_facet_remove VS-icon VS-icon-cancel"></div>');
window.JST['search_input'] = _.template('<input type="text" class="ui-menu" />');
})();
\ No newline at end of file
......@@ -160,6 +160,42 @@ $input-border-radius: 2px;
background-image: url("../images/#{$icon}");
}
//FONTICON////////////////////////////////
@mixin fonticon($char, $iconside, $offset: .5em) {
text-decoration: none;
cursor: pointer;
&:hover {
opacity: 1;
}
@if $iconside == left {
&:before {
content: $char !important;
font-family: 'icomoon';
margin-right: $offset;
speak: none;
font-weight: normal;
@content; // Allow additonal styles for the icon.
}
&:after {
content: "" !important;
}
}
@if $iconside == right {
&:before {
content: "" !important;
}
&:after {
content: $char !important;
font-family: 'icomoon';
margin-left: $offset;
speak: none;
font-weight: normal;
}
}
}
//NOISE///////////
//Provides the noise background
.noise {
......
......@@ -6,6 +6,7 @@
@import 'spinner';
@import 'responsive';
@import 'yui_grid';
@import 'simple-search';
$base-font-size: 16px;
......@@ -41,8 +42,8 @@ p {
em { font-style: italic; }
html {
font-size: $base-font-size / 16px * 1em;
line-height: $base-line-height / 16px * 1em;
font-size: $base-font-size;
line-height: $base-line-height;
@include yui_grid();
......@@ -350,6 +351,61 @@ blockquote {
//ICON CLASSES////////////////////////////////
.flag-icon {
@include fonticon("\28", left);
}
.fave-icon {
@include fonticon("\e006", left);
&.checked:before {
content: "\e005";
}
}
.reply-icon {
@include fonticon("\e004", left);
}
.share-icon {
@include fonticon("\25", left);
}
.down-icon {
@include fonticon("\e007", left);
}
.clipboard-icon {
@include fonticon("\33", left);
}
.check-icon {
@include fonticon("\35", left);
}
.plus-icon {
@include fonticon("\e012", left);
}
.x-icon {
@include fonticon("\36", left);
}
.vis-icon {
@include fonticon("\e001", left);
}
.highlight-icon {
@include fonticon("\e601", left);
}
.comment-icon {
@include fonticon("\e600", left);
}
.launch-icon {
@include fonticon("\2a", left);
}
.loading-icon {
text-align: center
}
......@@ -752,53 +808,6 @@ pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
}
}
//Visual Search////////////////////////////////
//Override their css here
.VS-search {
overflow: hidden;
* {
@include box-sizing(content-box);
}
.search_facet input,
.search_input input,
.VS-input-width-tester {
@include box-shadow(none);
border: 0;
}
.VS-search-box {
border: 0;
height: 26px;
margin: 1px 0;
min-height: 26px;
}
&.VS-search-collapsed .VS-search-box {
@include box-shadow(none);
background: inherit;
}
.VS-icon-cancel {
background-image: url(../images/svg/cancel.svg);
background-size: contain;
&:hover {
background-image: url(../images/svg/cancel_black.svg);
background-position: center 0;
}
}
.VS-icon-search {
background-image: url(../images/svg/search.svg);
background-size: contain;
&:hover {
background-image: url(../images/svg/search_dark.svg);
cursor: pointer;
}
}
}
// View and Sort tabs ////////////////////
.viewsort {
@include single-transition(top, .25s);
......
@import "base.scss";
.simple-search {
overflow: hidden;
}
.simple-search-form {
position: relative;
color: $gray;
}
.simple-search-icon {
position: absolute;
font-size: 1.1em;
top: 50%;
left: .6em;
margin-top: -.5em;
pointer-events: none;
:not(:focus) ~ & {
color: $gray-lighter;
}
}
.simple-search-input {
outline: none;
color: $text-color;
line-height: 24px;
width: 100%;
border-radius: 2em;
border: 1px solid $gray-lighter !important; // Override base input styles.
padding: 0 2em;
.simple-search-inactive &:not(:focus) {
border-color: #f3f3f3 !important;
}
}
.simple-search-clear {
[class^="icon-"], [class*=" icon-"] {
position: absolute;
top: 50%;
left: 50%;
margin-top: -.5em;
margin-left: -.5em;
font-size: .7em;
}
position: absolute;
outline: none;
right: .35em;
top: 50%;
border: none;
border-radius: 50%;
background: $gray-light;
color: #fff;
width: 1.33em;
height: 1.33em;
padding: 0;
margin-top: -.65em;
@include box-shadow(none);
&:focus, &:hover, &:active:not([disabled]) {
@include transition(background-color 200ms ease-in);
background-color: $gray-light;
color: #fff;
border: none;
background-image: none;
box-shadow: none;
}
}
......@@ -57,6 +57,9 @@ body {
font-family: $sans-font-family;
}
& > * { margin: 0 .72em; }
& > * {
line-height: 28px;
margin: 0 .72em;
}
}
}
.VS-search .VS-icon {
background-repeat: no-repeat;
background-position: center center;
vertical-align: middle;
width: 16px; height: 16px;
}
.VS-search .VS-icon-cancel {
width: 11px; height: 11px;
background-position: center 0;
background-image: url(../images/embed/icons/cancel_search.png?1311104738);
cursor: pointer;
}
.VS-search .VS-icon-cancel:hover {
background-position: center -11px;
}
.VS-search .VS-icon-search {
width: 12px; height: 12px;
background-image: url(../images/embed/icons/search_glyph.png?1311104738);
}
/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
/*
Eric Meyer's final reset.css
Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
.VS-search div, .VS-search span, .VS-search a, .VS-search img,
.VS-search ul, .VS-search li, .VS-search form, .VS-search label,
.VS-interface ul, .VS-interface li, .VS-interface {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
.VS-search :focus {
outline: 0;
}
.VS-search {
line-height: 1;
color: black;
}
.VS-search ol, .VS-search ul {
list-style: none;
}
/* ===================== */
/* = General and Reset = */
/* ===================== */
.VS-search {
font-family: Arial, sans-serif;
color: #373737;
font-size: 12px;
}
.VS-search input {
display: block;
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
outline: none;
margin: 0; padding: 4px;
background: transparent;
font-size: 16px;
line-height: 20px;
width: 100%;
}
.VS-interface, .VS-search .dialog, .VS-search input {
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif !important;
line-height: 1.1em;
}
/* ========== */
/* = Layout = */
/* ========== */
.VS-search .VS-search-box {
cursor: text;
position: relative;
background: transparent;
border: 2px solid #ccc;
border-radius: 16px; -webkit-border-radius: 16px; -moz-border-radius: 16px;
background-color: #fafafa;
-webkit-box-shadow: inset 0px 0px 3px #ccc;
-moz-box-shadow: inset 0px 0px 3px #ccc;
box-shadow: inset 0px 0px 3px #ccc;
min-height: 28px;
height: auto;
}
.VS-search .VS-search-box.VS-focus {
border-color: #acf;
-webkit-box-shadow: inset 0px 0px 3px #acf;
-moz-box-shadow: inset 0px 0px 3px #acf;
box-shadow: inset 0px 0px 3px #acf;
}
.VS-search .VS-placeholder {
position: absolute;
top: 7px;
left: 4px;
margin: 0 20px 0 22px;
color: #808080;
font-size: 14px;
}
.VS-search .VS-search-box.VS-focus .VS-placeholder,
.VS-search .VS-search-box .VS-placeholder.VS-hidden {
display: none;
}
.VS-search .VS-search-inner {
position: relative;
margin: 0 20px 0 22px;
overflow: hidden;
}
.VS-search input {
width: 100px;
}
.VS-search input,
.VS-search .VS-input-width-tester {
padding: 6px 0;
float: left;
color: #808080;
font: 13px/17px Helvetica, Arial;
}
.VS-search.VS-focus input {
color: #606060;
}
.VS-search .VS-icon-search {
position: absolute;
left: 9px; top: 8px;
}
.VS-search .VS-icon-cancel {
position: absolute;
right: 9px; top: 8px;
}
/* ================ */
/* = Search Facet = */
/* ================ */
.VS-search .search_facet {
float: left;
margin: 0;
padding: 0 0 0 14px;
position: relative;
border: 1px solid transparent;
height: 20px;
margin: 3px -3px 3px 0;
}
.VS-search .search_facet.is_selected {
margin-left: -3px;
-webkit-border-radius: 16px;
-moz-border-radius: 16px;
border-radius: 16px;
background-color: #d2e6fd;
background-image: -moz-linear-gradient(top, #d2e6fd, #b0d1f9); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#d2e6fd), to(#b0d1f9)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #d2e6fd, #b0d1f9);
border: 1px solid #6eadf5;
}
.VS-search .search_facet .category {
float: left;
text-transform: uppercase;
font-weight: bold;
font-size: 10px;
color: #808080;
padding: 8px 0 5px;
line-height: 13px;
cursor: pointer;
padding: 4px 0 0;
}
.VS-search .search_facet.is_selected .category {
margin-left: 3px;
}
.VS-search .search_facet .search_facet_input_container {
float: left;
}
.VS-search .search_facet input {
margin: 0;
padding: 0;
color: #000;
font-size: 13px;
line-height: 16px;
padding: 5px 0 5px 4px;
height: 16px;
width: auto;
z-index: 100;
position: relative;
padding-top: 1px;
padding-bottom: 2px;
padding-right: 3px;
}
.VS-search .search_facet.is_editing input,
.VS-search .search_facet.is_selected input {
color: #000;
}
.VS-search .search_facet .search_facet_remove {
position: absolute;
left: 0;
top: 4px;
}
.VS-search .search_facet.is_selected .search_facet_remove {
opacity: 0.4;
left: 3px;
filter: alpha(opacity=40);
background-position: center -11px;
}
.VS-search .search_facet .search_facet_remove:hover {
opacity: 1;
}
.VS-search .search_facet.is_editing .category,
.VS-search .search_facet.is_selected .category {
color: #000;
}
.VS-search .search_facet.search_facet_maybe_delete .category,
.VS-search .search_facet.search_facet_maybe_delete input {
color: darkred;
}
/* ================ */
/* = Search Input = */
/* ================ */
.VS-search .search_input {
height: 28px;
float: left;
margin-left: -1px;
}
.VS-search .search_input input {
padding: 6px 3px 6px 2px;
line-height: 10px;
height: 22px;
margin-top: -4px;
width: 10px;
z-index: 100;
min-width: 4px;
position: relative;
}
.VS-search .search_input.is_editing input {
color: #202020;
}
/* ================ */
/* = Autocomplete = */
/* ================ */
.ui-helper-hidden-accessible {
display: none;
}
.VS-interface.ui-autocomplete {
position: absolute;
border: 1px solid #C0C0C0;
border-top: 1px solid #D9D9D9;
background-color: #F6F6F6;
cursor: pointer;
z-index: 10000;
padding: 0;
margin: 0;
width: auto;
min-width: 80px;
max-width: 220px;
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
font-size: 13px;
top: 5px;
opacity: 0.97;
box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -webkit-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5); -moz-box-shadow: 3px 4px 5px -2px rgba(0, 0, 0, 0.5);
}
.VS-interface.ui-autocomplete .ui-autocomplete-category {
text-transform: capitalize;
font-size: 11px;
padding: 4px 4px 4px;
border-top: 1px solid #A2A2A2;
border-bottom: 1px solid #A2A2A2;
background-color: #B7B7B7;
text-shadow: 0 -1px 0 #999;
font-weight: bold;
color: white;
cursor: default;
}
.VS-interface.ui-autocomplete .ui-menu-item {
float: none;
}
.VS-interface.ui-autocomplete .ui-menu-item a {
color: #000;
outline: none;
display: block;
padding: 3px 4px 5px;
border-radius: none;
line-height: 1;
background-color: #F8F8F8;
background-image: -moz-linear-gradient(top, #F8F8F8, #F3F3F3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#F8F8F8), to(#F3F3F3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #F8F8F8, #F3F3F3);
border-top: 1px solid #FAFAFA;
border-bottom: 1px solid #f0f0f0;
}
.VS-interface.ui-autocomplete .ui-menu-item a:active {
outline: none;
}
.VS-interface.ui-autocomplete .ui-menu-item .ui-state-hover, .VS-interface.ui-autocomplete .ui-menu-item .ui-state-focus {
background-color: #6483F7;
background-image: -moz-linear-gradient(top, #648bF5, #2465f3); /* FF3.6 */
background-image: -webkit-gradient(linear, left top, left bottom, from(#648bF5), to(#2465f3)); /* Saf4+, Chrome */
background-image: linear-gradient(top, #648bF5, #2465f3);
border-top: 1px solid #5b83ec;
border-bottom: 1px solid #1459e9;
border-left: none;
border-right: none;
color: white;
margin: 0;
}
.VS-interface.ui-autocomplete .ui-corner-all {
border-radius: 0;
}
.VS-interface.ui-autocomplete li {
list-style: none;
width: auto;
}
......@@ -54,9 +54,6 @@ module.exports = function(config) {
'h/static/scripts/vendor/jquery.ui.effect-highlight.js',
'h/static/scripts/vendor/tag-it.js',
'h/static/scripts/vendor/uuid.js',
'h/static/scripts/vendor/underscore-1.4.3.js',
'h/static/scripts/vendor/backbone-0.9.10.js',
'h/static/scripts/vendor/visualsearch.js',
'h/static/scripts/hypothesis.js',
'h/static/scripts/vendor/sinon.js',
'h/static/scripts/vendor/chai.js',
......
......@@ -112,7 +112,7 @@ describe 'h.directives', ->
it 'opens a new window for the user when clicked', ->
$element.find('.user').click()
sinon.assert.calledWith(fakeWindow.open, '/u/bill@127.0.0.1')
sinon.assert.calledWith(fakeWindow.open, '/u/bill')
it 'prevents the default browser action on click', ->
event = jQuery.Event('click')
......@@ -129,6 +129,56 @@ describe 'h.directives', ->
text = $element.find('.user').text()
assert.equal(text, 'jim')
it 'keeps the url in sync', ->
it 'opens with only the username', ->
$element.find('.user').click()
sinon.assert.calledWith(fakeWindow.open, '/u/jim@hypothesis')
sinon.assert.calledWith(fakeWindow.open, '/u/jim')
describe '.simpleSearch', ->
$element = null
beforeEach ->
$scope.query = {}
$scope.update = sinon.spy()
$scope.clear = sinon.spy()
template= '''
<div class="simpleSearch"
query="query"
onsearch="update(this)"
onclear="clear()">
</div>
'''
$element = $compile(angular.element(template))($scope)
$scope.$digest()
it 'updates the search-bar', ->
$scope.query = {query: "Test query"}
$scope.$digest()
assert.equal($scope.searchtext, $scope.query.query)
it 'calls the given search function', ->
$scope.query = {query: "Test query"}
$scope.$digest()
$element.trigger('submit')
sinon.assert.calledWith($scope.update, "Test query")
it 'calls the given clear function', ->
$element.find('.simple-search-clear').click()
assert($scope.clear.called)
it 'clears the search-bar', ->
$scope.query = {query: "Test query"}
$scope.$digest()
$element.find('.simple-search-clear').click()
assert.equal($scope.searchtext, '')
it 'adds a class to the form when there is no input value', ->
$form = $element.find('.simple-search-form')
assert.include($form.prop('className'), 'simple-search-inactive')
it 'removes the class from the form when there is an input value', ->
$scope.query = {query: "Test query"}
$scope.$digest()
$form = $element.find('.simple-search-form')
assert.notInclude($form.prop('className'), 'simple-search-inactive')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment