diff options
Diffstat (limited to 'app')
504 files changed, 16284 insertions, 4304 deletions
diff --git a/app/assets/fonts/delicious-bold-webfont.eot b/app/assets/fonts/delicious-bold-webfont.eot Binary files differnew file mode 100755 index 000000000..90d2e95fa --- /dev/null +++ b/app/assets/fonts/delicious-bold-webfont.eot diff --git a/app/assets/fonts/delicious-bold-webfont.svg b/app/assets/fonts/delicious-bold-webfont.svg new file mode 100755 index 000000000..51a5d9080 --- /dev/null +++ b/app/assets/fonts/delicious-bold-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : copyright 19941996 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousBold" horiz-adv-x="921" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="	" horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="!" horiz-adv-x="540" d="M143 104.5q0 61.5 48.5 106.5t109.5 45t101 -37t40 -96q0 -61 -48 -107.5t-109.5 -46.5t-101.5 37t-40 98.5zM180 1049q0 254 29 356l221 29q-39 -217 -39 -416v-637h-184q-27 510 -27 668z" /> +<glyph unicode=""" horiz-adv-x="708" d="M109 1317l30 156h152l33 -156l-48 -342h-122zM377 1317l31 156h149l35 -156l-47 -342h-123z" /> +<glyph unicode="#" horiz-adv-x="1138" d="M98 416l29 196h168l27 226h-140l27 196h137l43 385h184l-43 -385h154l41 346h184l-41 -346h164l-16 -196h-170l-29 -226h135l-18 -196h-143l-52 -412h-186l53 412h-153l-52 -361h-186l53 361h-170zM479 612h154l29 226h-154z" /> +<glyph unicode="$" horiz-adv-x="888" d="M88 979q0 143 91 236.5t235 103.5v113h92v-115q78 -10 156.5 -53t123.5 -105l-122 -121q-63 70 -158 95v-377l123 -84q96 -63 143 -138t47 -198q0 -139 -87 -237.5t-226 -121.5v-127h-92v119q-76 2 -159 38t-140 87l102 162q106 -86 197 -94v387l-138 90 q-88 57 -138 147.5t-50 192.5zM307 989q0 -109 107 -178v319q-49 -12 -78 -51t-29 -90zM506 178q94 43 94 160q0 82 -94 147v-307z" /> +<glyph unicode="%" horiz-adv-x="1431" d="M90 1032q0 119 84 204t205 85q55 0 137 -35q31 -18 37 -20q39 -12 98 -13q154 0 322 66l153 -39l-643 -1280h-200l589 1167q-78 -61 -215 -61q10 -41 11 -74q0 -119 -84 -204t-205 -85t-205 85t-84 204zM266 1032q0 -45 33 -78.5t80 -33.5t80 33.5t33 78.5t-33 79t-80 34 t-80 -34t-33 -79zM756 258q0 119 85 204t203.5 85t203.5 -85t85 -204t-85 -204t-203.5 -85t-203.5 85t-85 204zM932 258q0 -45 33.5 -79t79 -34t79 34t33.5 79t-33.5 79t-79 34t-79 -34t-33.5 -79z" /> +<glyph unicode="&" horiz-adv-x="1290" d="M82 489q0 168 84 307.5t240 161.5q-55 20 -91 70.5t-36 108.5q0 129 109.5 204.5t244.5 75.5q154 0 263.5 -81t109.5 -228v-104h192v-177h-188v-600q0 -41 10 -61.5t47 -20.5q41 0 113 29l51 -139q-53 -27 -79 -38t-64.5 -19.5t-88.5 -8.5q-104 0 -154 71t-50 179v608 h-82q-162 0 -203 -10q-193 -53 -193 -319q0 -98 24 -226.5t77 -128.5q16 0 43.5 29t70.5 58.5t101 29.5q37 0 82 -10l-39 -144q-35 8 -61.5 -3t-46.5 -33l-39 -45q-19 -23 -48.5 -39.5t-66.5 -16.5q-113 0 -193 92.5t-109.5 204.5t-29.5 223zM489 1120q0 -117 173 -116h149 v57q0 92 -34 142t-120 50q-168 0 -168 -133z" /> +<glyph unicode="'" horiz-adv-x="425" d="M100 1317l33 156h150l34 -156l-47 -342h-125z" /> +<glyph unicode="(" horiz-adv-x="688" d="M76 678q0 281 149.5 522.5t399.5 394.5l26 -184q-182 -117 -287.5 -316.5t-105.5 -420.5q0 -473 393 -789l-22 -198q-279 211 -416 440t-137 551z" /> +<glyph unicode=")" horiz-adv-x="694" d="M41 -115q393 315 393 789q0 221 -104.5 420.5t-286.5 316.5l25 184q250 -154 399 -395.5t149 -521.5q0 -322 -137 -552t-416 -439z" /> +<glyph unicode="*" horiz-adv-x="868" d="M55 1100v190l273 -114l-64 221h318l-58 -221l271 114v-190l-236 -76l215 -332h-221l-127 209l-129 -209h-219l215 332z" /> +<glyph unicode="+" horiz-adv-x="964" d="M78 420v184h313v336h180v-336h316v-184h-316v-336h-180v336h-313z" /> +<glyph unicode="," horiz-adv-x="528" d="M27 -229q137 199 137 327q0 35 -4 52q61 49 123 49q43 0 89 -37t74 -82q-109 -213 -290 -397z" /> +<glyph unicode="-" horiz-adv-x="618" d="M86 430v184h440v-184h-440z" /> +<glyph unicode="." horiz-adv-x="528" d="M121 104q0 61 47 106.5t111 45.5q59 0 99 -37t40 -96q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38t-40 97z" /> +<glyph unicode="/" horiz-adv-x="729" d="M8 -289l522 1825h191l-522 -1825h-191z" /> +<glyph unicode="0" d="M49 500q0 233 104.5 383.5t307.5 150.5t307 -152.5t104 -381.5q0 -137 -42 -254t-138 -197t-231 -80q-199 0 -305.5 160t-106.5 371zM256 500q0 -141 56.5 -240.5t148.5 -99.5q205 0 205 340q0 154 -55.5 249t-149.5 95t-149.5 -94t-55.5 -250z" /> +<glyph unicode="1" d="M201 799v155q117 0 250 41l26 9h174v-1004h-213v799h-237z" /> +<glyph unicode="2" d="M111 160q141 80 307 259t166 306q0 57 -39 88t-99 31q-70 0 -145.5 -32t-126.5 -81l-45 172q59 61 155.5 96t184.5 35q152 0 242 -75.5t90 -225.5q0 -154 -102.5 -300t-247.5 -240h385v-193h-686z" /> +<glyph unicode="3" d="M131 -178q111 2 213 34.5t181 114.5t79 199q0 152 -239 152q-90 0 -199 -11v164l108 17q100 55 236 163q70 57 70 115q0 35 -27 57.5t-61 22.5q-41 0 -114 -27.5t-124 -56.5l-53 -29l-54 162q74 43 121 66.5t118 45t136 21.5q125 0 199 -67.5t74 -171.5q0 -94 -80 -182.5 t-170 -123.5q115 -10 193.5 -93t78.5 -193q0 -141 -56 -250t-152.5 -171.5t-214.5 -92t-251 -29.5h-12v164z" /> +<glyph unicode="4" d="M59 166q145 373 494 838h209v-816h117v-188h-121v-283h-205v283h-483zM281 188h272v502q-186 -281 -272 -502z" /> +<glyph unicode="5" d="M123 -176q113 0 217 33.5t182 116.5t78 198q0 72 -49 109.5t-121 37.5q-102 0 -278 -49l61 734h547v-185h-354l-19 -323q98 12 100 12q141 0 234.5 -82t93.5 -223q0 -170 -96 -297t-243 -187q-142 -57 -301 -57h-11h-41v162z" /> +<glyph unicode="6" d="M84 530q0 506 598 795l82 -145q-164 -102 -257 -187.5t-159 -224.5q106 43 189 43q137 0 229 -107.5t92 -248.5q0 -201 -99.5 -343.5t-291.5 -142.5q-104 0 -183 52.5t-120 139.5t-60.5 179t-19.5 190zM295 489q0 -47 8 -99t27.5 -107.5t58.5 -92t90 -36.5q168 0 168 307 q0 92 -52 139t-144 47q-51 0 -146 -37q-10 -51 -10 -121z" /> +<glyph unicode="7" d="M119 811v193h680v-115q0 -285 -129 -650.5t-305 -598.5l-183 92q166 236 286 531.5t128 547.5h-477z" /> +<glyph unicode="8" d="M72 334q0 117 54 196.5t161 145.5q-180 109 -181 280q0 154 101.5 244t257.5 90q162 0 253 -83t91 -238q0 -98 -49 -165t-142 -122q117 -59 179.5 -133t62.5 -189q0 -178 -115.5 -284.5t-295.5 -106.5q-156 0 -266.5 104.5t-110.5 260.5zM285 354q0 -82 51 -146.5 t131 -64.5q78 0 128 64.5t50 144.5q0 43 -16.5 78t-55 66.5t-59.5 44.5l-74 43q-155 -95 -155 -230zM313 948q0 -63 37 -103t103 -75q152 88 151 201q0 156 -149 155q-72 0 -107 -51t-35 -127z" /> +<glyph unicode="9" d="M68 545q0 201 98 345t289 144q115 0 194.5 -52t120.5 -143t57.5 -184.5t16.5 -201.5q0 -266 -193.5 -475t-468.5 -320l-57 160q338 168 453 413q-102 -39 -160 -38q-152 0 -251 100t-99 252zM279 541q0 -78 50 -132.5t126 -54.5q88 0 166 39q10 66 10 121 q0 49 -8.5 102.5t-28 111.5t-57 95t-91.5 37q-167 0 -167 -319z" /> +<glyph unicode=":" horiz-adv-x="528" d="M121 104q0 61 47 106.5t111 45.5q59 0 99 -37t40 -96q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38t-40 97zM121 694q0 63 47 108.5t111 45.5q59 0 99 -38t40 -97q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38t-40 97z" /> +<glyph unicode=";" horiz-adv-x="528" d="M23 -229q137 205 137 327q0 27 -6 52q61 49 122 49q43 0 89.5 -37t74.5 -82q-109 -213 -290 -397zM135 692q0 63 47 108.5t111 45.5q59 0 99 -38t40 -97q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38t-40 97z" /> +<glyph unicode="<" horiz-adv-x="614" d="M33 512l403 410l146 -117l-295 -293l299 -293l-146 -117z" /> +<glyph unicode="=" horiz-adv-x="983" d="M86 270v185h811v-185h-811zM86 559v184h811v-184h-811z" /> +<glyph unicode=">" horiz-adv-x="614" d="M29 219l297 293l-293 293l145 117l404 -410l-408 -410z" /> +<glyph unicode="?" horiz-adv-x="733" d="M129 104q0 61 47 106.5t111 45.5q59 0 99 -37t40 -96q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38t-40 97zM190 381v111q0 147 18.5 200t106.5 168l109 144q63 88 63 137q0 100 -79 100q-98 0 -197 -29v172q121 33 235.5 33t184 -74.5t69.5 -189.5q0 -49 -17 -98t-37 -78 l-58 -82l-148 -203q-66 -92 -65 -198v-113h-185z" /> +<glyph unicode="@" horiz-adv-x="1622" d="M92 727q0 297 221.5 525.5t515.5 228.5q315 0 505 -171t190 -481q0 -199 -139.5 -364.5t-333.5 -165.5q-66 0 -119 41t-55 104q-57 -45 -133 -82.5t-130 -37.5q-94 0 -136 94t-42 201q0 207 101.5 363.5t298.5 156.5q76 0 172 -86l14 57h172l-141 -575q-8 -33 -9 -48 q0 -53 41 -53q94 0 166 100.5t86 204.5q6 43 6 90q0 231 -140 355.5t-376 124.5q-227 0 -383.5 -178.5t-156.5 -407.5q0 -256 173 -421t429 -165q281 0 493 217l109 -98q-250 -287 -641 -287q-315 0 -536.5 221.5t-221.5 536.5zM614 590q0 -98 62 -98q55 0 219 92l82 340 q-98 59 -158 59q-66 0 -116 -74.5t-69.5 -160.5t-19.5 -158z" /> +<glyph unicode="A" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM426 567h195l-97 398z" /> +<glyph unicode="B" horiz-adv-x="1028" d="M150 27v1263h335q170 0 288 -75.5t118 -237.5q0 -88 -48 -167t-126 -116q109 -16 176.5 -104t67.5 -201q0 -199 -134.5 -309.5t-337.5 -110.5q-122 1 -339 58zM373 774h110q84 0 135.5 47t51.5 131q0 92 -45 122t-142 30h-110v-330zM373 184q66 -18 112 -18 q117 0 184.5 55t67.5 168q0 117 -68.5 154t-193.5 37h-102v-396z" /> +<glyph unicode="C" horiz-adv-x="1017" d="M106 645q0 117 31 232.5t90.5 217t159.5 164t225 62.5q209 0 334 -92l-86 -154q-92 55 -248 55q-68 0 -124 -52t-88.5 -130t-51 -159.5t-18.5 -151.5q0 -78 12 -152.5t41 -151.5t89 -124t144 -47q139 0 246 78l103 -152q-66 -55 -165.5 -87t-191.5 -32 q-135 0 -235.5 59.5t-156.5 161t-83 215t-27 240.5z" /> +<glyph unicode="D" horiz-adv-x="1120" d="M150 0v1290h387q133 0 230 -55t150.5 -151.5t77 -205t23.5 -235.5q0 -125 -27.5 -235.5t-85 -204.5t-157 -148.5t-230.5 -54.5h-368zM371 193h147q274 0 275 446q0 459 -265 459h-157v-905z" /> +<glyph unicode="E" horiz-adv-x="929" d="M147 0v1290h697v-198h-473v-320h362v-201h-362v-368h493v-203h-717z" /> +<glyph unicode="F" horiz-adv-x="843" d="M145 0v1290h643v-196h-419v-322h331v-201h-331v-571h-224z" /> +<glyph unicode="G" horiz-adv-x="1073" d="M100 645q0 119 31 234.5t90.5 217t160.5 163t228 61.5q92 0 193.5 -28.5t167.5 -80.5l-88 -157q-117 78 -273 78q-96 0 -164.5 -92.5t-95 -200t-26.5 -203.5q0 -78 13 -153.5t43 -151.5t90 -123t144 -47q78 0 138 16v412h211v-559q-143 -61 -353 -62q-135 0 -237.5 59.5 t-159.5 159t-85 215t-28 242.5z" /> +<glyph unicode="H" horiz-adv-x="1122" d="M152 0v1290h223v-518h373v518h223v-1290h-223v571h-373v-571h-223z" /> +<glyph unicode="I" horiz-adv-x="487" d="M133 0v1290h221v-1290h-221z" /> +<glyph unicode="J" horiz-adv-x="501" d="M-6 -180q150 66 149 162v1308h219v-1319q0 -215 -317 -307z" /> +<glyph unicode="K" horiz-adv-x="993" d="M152 0v1290h223v-491l356 491h240l-471 -659l481 -541v-90h-205l-401 457v-457h-223z" /> +<glyph unicode="L" horiz-adv-x="782" d="M145 0v1290h215v-1093h404v-197h-619z" /> +<glyph unicode="M" horiz-adv-x="1519" d="M162 0v1290h262l307 -874l29 -115q8 55 28 115l308 874h262v-1290h-223v711l16 149l-299 -860h-184l-299 860l16 -149v-711h-223z" /> +<glyph unicode="N" horiz-adv-x="1134" d="M152 0v1290h262l286 -651q33 -74 60 -186v837h223v-1290h-215l-408 903q14 -84 15 -170v-733h-223z" /> +<glyph unicode="O" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM326 645q0 -84 11 -161.5t35.5 -152.5t74 -120t115 -45t114.5 45t73.5 120 t36 152.5t11.5 161.5t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162z" /> +<glyph unicode="P" horiz-adv-x="1001" d="M133 0v1290h299q504 0 504 -409q0 -195 -126 -320t-321 -125q-59 0 -133 33v-469h-223zM356 659q76 -29 119 -28q102 0 170 74.5t68 177.5q0 109 -83 162t-198 53h-76v-439z" /> +<glyph unicode="Q" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5q0 -215 -79 -400.5t-236 -248.5q51 -31 76.5 -43t63.5 -21.5t85 -9.5l94 15l-39 -220q-49 -8 -74 -8q-51 0 -97 14.5t-95 50.5t-73.5 56t-81.5 77l-66 66 q-135 20 -228.5 132t-130.5 250t-37 290zM326 645q0 -84 11 -162t35.5 -152.5t74 -119.5t115 -45t114.5 45t73.5 119.5t36 152.5t11.5 162t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162z" /> +<glyph unicode="R" horiz-adv-x="1034" d="M152 0v1290h297q254 0 372.5 -112.5t118.5 -296.5q0 -152 -87 -275t-224 -135l366 -381v-90h-200l-420 461v-461h-223zM375 655q63 -27 117 -26q104 0 162.5 73.5t58.5 180.5q0 215 -264 215h-74v-443z" /> +<glyph unicode="S" horiz-adv-x="888" d="M88 979q0 154 103.5 248t259.5 94q94 0 188 -44t147 -118l-122 -121q-90 98 -203 99q-68 0 -111 -40t-43 -108q0 -74 53.5 -130t128 -98t149.5 -91t128 -134t53 -200q0 -164 -114.5 -265.5t-280.5 -101.5q-78 0 -164 36t-145 89l102 162q119 -94 209 -94q78 0 126 49 t48 127q0 49 -38 95t-95 80t-122.5 79t-123 93t-95.5 125t-38 169z" /> +<glyph unicode="T" horiz-adv-x="829" d="M20 1085v205h789v-205h-285v-1085h-221v1085h-283z" /> +<glyph unicode="U" horiz-adv-x="1142" d="M150 506v784h223v-784q0 -340 198.5 -340t198.5 340v784h223v-784q0 -231 -102.5 -384t-319.5 -153t-319 153t-102 384z" /> +<glyph unicode="V" horiz-adv-x="1044" d="M12 1290h238l219 -772l53 -182l45 160l228 794h237l-399 -1290h-221z" /> +<glyph unicode="W" horiz-adv-x="1449" d="M18 1290h226l157 -723q16 -74 25 -188q4 119 23 190l169 721h213l170 -721q18 -72 23 -190q8 115 25 188l159 723h224l-295 -1290h-219l-175 782q-10 35 -18 115q-8 -78 -16 -115l-177 -782h-219z" /> +<glyph unicode="X" horiz-adv-x="1007" d="M14 0l359 659l-348 631h247l222 -424l217 424h237l-330 -620l377 -670h-258l-243 459l-232 -459h-248z" /> +<glyph unicode="Y" horiz-adv-x="964" d="M27 1290h239l215 -583l228 583h229l-344 -809v-481h-223v483z" /> +<glyph unicode="Z" horiz-adv-x="937" d="M72 0v174q197 408 479 913h-438v203h712v-178q-295 -508 -493 -909h547v-203h-807z" /> +<glyph unicode="[" horiz-adv-x="507" d="M31 -319v1816h370v-180h-182v-1456h182v-180h-370z" /> +<glyph unicode="\" horiz-adv-x="686" d="M-4 1536h192l523 -1825h-193z" /> +<glyph unicode="]" horiz-adv-x="509" d="M111 -139h180v1456h-180v180h368v-1816h-368v180z" /> +<glyph unicode="^" horiz-adv-x="1277" d="M113 643l448 819h152l448 -819h-215l-309 559l-309 -559h-215z" /> +<glyph unicode="_" d="M0 -123h922v-184h-922v184z" /> +<glyph unicode="`" horiz-adv-x="692" d="M55 1407l172 176q123 -168 342 -328l-98 -139q-258 137 -416 291z" /> +<glyph unicode="a" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90z" /> +<glyph unicode="b" horiz-adv-x="1030" d="M158 102v1321h215v-493q35 41 94 72.5t108 31.5q178 0 275.5 -142t97.5 -331q0 -109 -22.5 -208t-69.5 -188t-134 -142.5t-204 -53.5q-184 0 -360 133zM373 201q82 -55 159 -56q57 0 99.5 43t62 111t27.5 127t8 115q0 49 -7 95t-23.5 96t-53.5 80t-88 30q-76 0 -184 -103 v-538z" /> +<glyph unicode="c" horiz-adv-x="843" d="M86 492q0 131 42 250.5t139.5 205.5t232.5 86q82 0 165 -32.5t130 -90.5l-109 -127q-80 70 -184 70q-66 0 -115 -63.5t-67.5 -138t-18.5 -140.5q0 -51 10.5 -108.5t33 -116t65.5 -97t98 -38.5q106 0 235 102l74 -148q-63 -59 -153.5 -98t-169.5 -39q-129 0 -225.5 82 t-139.5 198t-43 243z" /> +<glyph unicode="d" horiz-adv-x="1048" d="M90 457q0 115 19.5 212t62.5 182t125 134t195 49q37 0 84 -8t75 -16l29 -9v422h215v-1212q0 -51 11.5 -69.5t56.5 -18.5l-66 -154q-12 -1 -22 0q-65 0 -97 21q-55 39 -78 133q-31 -59 -92 -106.5t-147 -47.5q-166 0 -268.5 141.5t-102.5 346.5zM311 479q0 -45 6.5 -91 t21.5 -101.5t50 -91t86 -39.5q5 0 10 -1q87 0 140 105q55 111 55 211v342q-106 33 -180 33q-189 0 -189 -367z" /> +<glyph unicode="e" horiz-adv-x="964" d="M88 526q0 203 120 355.5t318 152.5q145 0 253 -118.5t112 -266.5q0 -23 1 -67.5t1 -67.5h-586q-2 -12 -2 -37q0 -66 17.5 -136.5t64.5 -129.5t115 -59q123 0 268 110l72 -139q-74 -61 -173.5 -107.5t-179.5 -46.5q-143 0 -237 89t-129 209t-35 259zM336 692h342 q-6 78 -49 130t-117 52q-131 0 -176 -182z" /> +<glyph unicode="f" horiz-adv-x="585" d="M27 850v154h131v114q0 63 21.5 116.5t54 86.5t73.5 58.5t82 38t74 19.5t53 9h23l36 -156q-8 0 -21 -2t-49 -13t-62.5 -28.5t-48 -54.5t-21.5 -84v-104h186v-181h-186v-823h-215v827z" /> +<glyph unicode="g" horiz-adv-x="931" d="M70 -145q0 70 56 125t128 86q-47 6 -84 50t-37 95q0 55 42 98t101 66q-84 35 -128 120t-44 179q0 164 99.5 262t263.5 98q98 0 168 -30h256v-185h-117q29 -94 29 -151q0 -127 -67.5 -213t-192.5 -117q-2 0 -29 -4t-41 -8l-10 -2q-10 -4 -15.5 -5.5t-15.5 -4.5t-16 -5 t-16.5 -7t-15.5 -9t-13.5 -11.5t-11.5 -13.5t-6 -15t-3 -20q0 -16 16.5 -25t45 -12t52 -3t56.5 1t39 1h66q109 0 183.5 -60.5t74.5 -167.5q0 -170 -139.5 -271.5t-317.5 -101.5q-145 0 -250.5 64t-105.5 197zM281 -117q0 -133 153 -133q96 0 169 55.5t73 145.5q0 78 -156 78 h-119q-4 -2 -12 -7.5l-29.5 -20.5t-37 -30.5t-28.5 -40t-13 -47.5zM315 696q0 -201 144 -200q74 0 105.5 54t31.5 132q0 80 -34 138.5t-107.5 58.5t-106.5 -52.5t-33 -130.5z" /> +<glyph unicode="h" horiz-adv-x="1062" d="M154 0v1423h215v-493q53 43 132 73.5t142 30.5q158 0 214 -78.5t56 -214.5v-741h-215v741q0 61 -15 83t-65 22q-61 0 -131.5 -34t-117.5 -71v-741h-215z" /> +<glyph unicode="i" horiz-adv-x="487" d="M102 1251q0 57 44 99.5t102 42.5q55 0 96 -38t41 -93q0 -57 -44 -99.5t-101 -42.5q-55 0 -96.5 38t-41.5 93zM137 0v1004h211v-1004h-211z" /> +<glyph unicode="j" horiz-adv-x="487" d="M-6 -244q59 20 102 65.5t43 104.5v1078h211v-1090q0 -57 -21.5 -105.5t-51 -79t-76.5 -58t-81 -41t-81 -27.5zM102 1251q0 57 44 99.5t102 42.5q55 0 96 -38t41 -93q0 -57 -44 -99.5t-101 -42.5q-55 0 -96.5 38t-41.5 93z" /> +<glyph unicode="k" horiz-adv-x="964" d="M156 0v1423h215v-745l282 326h265l-369 -429l407 -493v-82h-206l-379 471v-471h-215z" /> +<glyph unicode="l" horiz-adv-x="483" d="M125 420v1003h211v-1003q0 -182 70 -420h-222q-59 152 -59 420z" /> +<glyph unicode="m" horiz-adv-x="1579" d="M156 0v1004h215v-72q35 31 115.5 66.5t136.5 35.5q143 0 235 -119q2 2 28.5 18.5t38.5 22.5l38 22q27 15 44 22.5t41.5 16.5t47 13t43.5 4q291 0 291 -284v-750h-216v750q0 55 -18 76.5t-70 21.5q-86 0 -213 -92v-756h-215v721q0 127 -88 127q-53 0 -126.5 -39 t-112.5 -72v-737h-215z" /> +<glyph unicode="n" horiz-adv-x="1062" d="M154 0v1004h215v-74q53 43 132 73.5t142 30.5q158 0 214 -78.5t56 -214.5v-741h-215v741q0 61 -15 84t-65 23q-115 0 -249 -107v-741h-215z" /> +<glyph unicode="o" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM305 502q0 -143 53.5 -245.5t141.5 -102.5t141 102t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5z" /> +<glyph unicode="p" horiz-adv-x="1056" d="M158 -358v1362h217v-74q96 104 221 104q178 0 275.5 -149.5t97.5 -337.5q0 -102 -17.5 -195.5t-57.5 -183.5t-120 -144.5t-190 -54.5q-154 0 -209 103v-430h-217zM375 489q0 -78 12 -145.5t60.5 -127.5t127.5 -60q55 0 92.5 41t52.5 106.5t21.5 117.5t6.5 105 q0 315 -173 316q-94 0 -200 -103v-250z" /> +<glyph unicode="q" horiz-adv-x="1054" d="M88 465q0 133 41 258t138.5 218t234.5 93q80 0 179 -19.5t159 -39.5l59 -19v-1314h-217v430q-33 -45 -92.5 -74t-116.5 -29q-133 0 -223 77t-126 184.5t-36 234.5zM307 492q0 -49 7.5 -99.5t23.5 -108t53 -93t90 -35.5q51 0 86 26q115 84 115 307v338q-12 4 -30.5 8.5 t-71.5 12.5q-27 4 -47.5 4t-34.5 -4q-55 -8 -94 -47t-59.5 -94.5t-29 -108.5t-8.5 -106z" /> +<glyph unicode="r" horiz-adv-x="653" d="M150 0v1004h217v-74q90 104 155 104l121 -30l-35 -203q-66 37 -114 37q-25 0 -56.5 -22.5t-52.5 -45.5l-20 -22v-748h-215z" /> +<glyph unicode="s" horiz-adv-x="858" d="M90 739q0 141 104.5 218t251.5 77q88 0 177.5 -36.5t134.5 -102.5l-113 -111q-37 37 -93 60.5t-110 23.5q-51 0 -95 -31.5t-44 -80.5q0 -57 96 -103l250 -127q68 -35 106 -102.5t38 -144.5q0 -147 -109 -228.5t-262 -81.5q-72 0 -156 36t-147 87l80 144q131 -94 229 -95 q66 0 110 29t44 92q0 70 -103 123l-278 145q-53 29 -82 86t-29 123z" /> +<glyph unicode="t" horiz-adv-x="626" d="M35 850v154h137v180h203v-180h182v-183h-178v-594q0 -72 51 -71l107 26l51 -151q-106 -51 -156 -58q-27 -4 -63 -4q-104 0 -154.5 71t-50.5 181v606z" /> +<glyph unicode="u" horiz-adv-x="1062" d="M152 258v746h215v-746q0 -53 17 -75.5t71 -22.5q104 0 241 102v742h215v-818q0 -68 41 -98l-65 -119q-47 4 -96.5 43t-61.5 80q-66 -49 -149.5 -86t-151.5 -37q-147 0 -211.5 71t-64.5 218z" /> +<glyph unicode="v" horiz-adv-x="925" d="M18 1004h232l219 -695q139 352 211 695h229q-143 -520 -368 -1004h-158z" /> +<glyph unicode="w" horiz-adv-x="1372" d="M12 1004h228l184 -682l162 682h205l176 -682q119 377 157 682h226q-66 -487 -297 -1004h-205l-103 369q-12 45 -25 113.5t-20 115.5l-8 47q-27 -178 -55 -276l-105 -369h-213z" /> +<glyph unicode="x" horiz-adv-x="868" d="M20 0l291 518l-272 486h242l151 -302l154 302h235l-272 -474l307 -530h-246l-180 340l-174 -340h-236z" /> +<glyph unicode="y" horiz-adv-x="929" d="M23 1004h227l180 -564q43 -137 53 -207q125 360 205 771h219q-33 -147 -79 -314.5t-115.5 -381.5t-158.5 -383t-175 -230q-33 -25 -86 -46.5t-90 -29.5l-37 -10l-47 162q90 29 139 71q47 41 113 158z" /> +<glyph unicode="z" horiz-adv-x="872" d="M70 0v170q221 379 432 641h-393v193h663v-175q-281 -356 -444 -634h487v-195h-745z" /> +<glyph unicode="{" horiz-adv-x="681" d="M61 518v141q66 0 113 58.5t47 148.5v357q0 168 62.5 227t160.5 59h144l35 -172h-156q-39 0 -49 -26.5t-10 -114.5v-356q0 -88 -46.5 -160t-93.5 -94q53 -33 96.5 -97.5t43.5 -152.5v-356q0 -86 12 -113t57 -27h146l-35 -172h-70h-69q-98 0 -163 58.5t-65 226.5v356 q0 90 -47 149.5t-113 59.5z" /> +<glyph unicode="|" horiz-adv-x="454" d="M135 -29v1569h184v-1569h-184z" /> +<glyph unicode="}" horiz-adv-x="681" d="M53 -160h146q45 0 57 27t12 113v356q0 88 42 152.5t96 97.5q-47 23 -92.5 94.5t-45.5 159.5v356q0 88 -10 114.5t-49 26.5h-156l35 172h143q98 0 161 -59t63 -227v-357q0 -90 47 -148.5t112 -58.5v-141q-66 0 -112.5 -59.5t-46.5 -149.5v-356q0 -168 -64.5 -226.5 t-163.5 -58.5h-71h-68z" /> +<glyph unicode="~" horiz-adv-x="1363" d="M285 459v207l22 32q14 20 64.5 53t105.5 33q84 0 220.5 -65.5t199.5 -65.5q109 0 178 119l6 12v-217l-21 -30q-13 -18 -61.5 -49t-101.5 -31q-94 0 -230.5 66.5t-191.5 66.5q-35 0 -67.5 -13.5t-54 -33t-38 -39t-24.5 -33.5z" /> +<glyph unicode="¡" horiz-adv-x="540" d="M94 852q0 61 48 107.5t109.5 46.5t101.5 -37t40 -98.5t-48 -107.5t-109.5 -46t-101.5 38t-40 97zM104 -459q41 211 41 414v639h185q27 -510 26 -668q0 -254 -30 -356z" /> +<glyph unicode="¢" horiz-adv-x="843" d="M86 492q0 231 102 368q109 154 277 172v137h92v-139q70 -8 129 -37.5t84 -54.5l25 -27l-109 -129q-53 47 -129 66v-690q41 10 88 33.5t72 41.5l26 21l74 -148q-115 -104 -260 -131v-127h-92v123q-150 12 -254 138q-125 145 -125 383zM301 512q0 -127 45 -226.5 t119 -125.5v688q-68 -23 -116 -110t-48 -226z" /> +<glyph unicode="£" horiz-adv-x="905" d="M25 745l59 148h78v252q0 141 105.5 207.5t240.5 66.5q150 0 242 -78l-56 -149q-86 51 -174 51q-143 0 -143 -127v-223h260v-184h-260v-195q0 -154 -37 -328h100l90 -30q38 -12 73 -13q96 0 167 97l18 28l43 -172q-2 -4 -7 -10l-20 -25q-15 -18 -32.5 -31.5t-46.5 -31.5 t-58.5 -25.5t-68.5 -11.5q-9 -1 -17 -1l-61 11l-106 29h-332q80 352 80 524v195z" /> +<glyph unicode="¤" horiz-adv-x="1110" d="M6 463l78 172h111v55v58h-181l82 176h123q33 217 180.5 353t378.5 136q197 0 344 -141l-75 -168q-68 70 -128.5 98.5t-150.5 28.5q-270 0 -338 -307h551l-82 -174h-491q-7 -36 -8 -70q0 -23 3 -45h445l-82 -174h-336q51 -303 318 -303q156 0 319 192v-241 q-139 -133 -330 -134q-205 0 -346 130.5t-172 357.5h-213z" /> +<glyph unicode="¥" horiz-adv-x="974" d="M31 1290h241l220 -583l221 583h231l-211 -487h197v-187h-277l-49 -114h326v-187h-334v-315h-221v315h-324v187h316l-50 114h-266v187h189z" /> +<glyph unicode="§" horiz-adv-x="860" d="M25 -158l77 160q160 -100 306 -100q72 0 111.5 33.5t39.5 91.5q0 43 -40 77.5t-150 88.5q-10 4 -56.5 26.5t-61.5 30.5t-54 30.5t-55 41.5l-38 45q-23 27 -30.5 57.5t-7.5 67.5q0 98 62 172.5t163 105.5q-217 96 -217 242q0 141 100 225t256 84q158 0 326 -94l-84 -160 q-150 74 -246 74q-66 0 -102.5 -35t-36.5 -90q0 -10 4 -20.5t12 -19.5t15.5 -16.5t21.5 -15.5t20.5 -12t22.5 -11.5t18 -9.5l63 -30l73 -37l67 -38q45 -26 68.5 -48.5t50.5 -52t39 -62.5t12 -70q0 -96 -62.5 -179t-154.5 -113q59 -33 97 -57.5t75 -72t37 -98.5 q0 -154 -100.5 -245t-266.5 -91q-167 0 -374 125zM276 514q0 -41 32 -74.5t85 -33.5q84 0 134.5 36.5t50.5 92.5q0 43 -38 73.5t-94 30.5q-76 0 -123 -35t-47 -90z" /> +<glyph unicode="¨" horiz-adv-x="786" d="M76 1276q0 57 43 103t100 46q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75zM422 1276q0 57 43 103t100 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -75.5 30t-30.5 75z" /> +<glyph unicode="©" horiz-adv-x="1800" d="M242 503.5q0 274.5 194.5 468t467 193.5t467 -193.5t194.5 -468t-194.5 -468t-467 -193.5t-467 193.5t-194.5 468zM403 504q0 -207 146.5 -353.5t353.5 -146.5t353.5 146.5t146.5 353.5t-146.5 353.5t-353.5 146.5t-353.5 -146.5t-146.5 -353.5zM600 514q0 150 85 253 t230 103q55 0 121 -29.5t111 -70.5l-82 -108q-88 63 -147 63q-72 0 -111 -67.5t-39 -143.5q0 -92 38 -162.5t122 -70.5q66 0 143 59l62 -117q-100 -88 -211 -88q-158 0 -240 107.5t-82 271.5z" /> +<glyph unicode="ª" horiz-adv-x="774" d="M70 936q0 66 47 151l336 146v78q0 76 -93 75q-98 0 -196 -94l-58 99q49 57 123 96t146 39q111 0 176 -54.5t65 -162.5v-445q0 -39 29 -39q18 0 29 9l43 -82q-55 -33 -111 -33q-53 0 -90 29.5t-47 80.5q-74 -113 -193 -112q-206 0 -206 219zM225 936q0 -35 18.5 -59.5 t51.5 -24.5q70 0 115 68.5t45 146.5v27l-219 -88q-11 -56 -11 -70z" /> +<glyph unicode="«" horiz-adv-x="1024" d="M27 496l403 409l145 -117l-296 -288l301 -297l-146 -117zM387 496l404 409l145 -117l-295 -288l299 -297l-145 -117z" /> +<glyph unicode="¬" horiz-adv-x="1095" d="M82 717v184h850v-555l-182 -35v406h-668z" /> +<glyph unicode="­" horiz-adv-x="618" d="M86 430v184h440v-184h-440z" /> +<glyph unicode="®" horiz-adv-x="1800" d="M242 503.5q0 274.5 194.5 468t467 193.5t467 -193.5t194.5 -468t-194.5 -468t-467 -193.5t-467 193.5t-194.5 468zM403 504q0 -207 146.5 -353.5t353.5 -146.5t353.5 146.5t146.5 353.5t-146.5 353.5t-353.5 146.5t-353.5 -146.5t-146.5 -353.5zM692 147v725h178 q125 0 203 -58t78 -179q0 -76 -39 -141.5t-106 -87.5l176 -183v-76h-138l-192 205v-205h-160zM852 530q16 -8 41 -8q47 0 71.5 33t24.5 80q0 94 -119 94h-18v-199z" /> +<glyph unicode="¯" horiz-adv-x="686" d="M43 1143v182h598v-182h-598z" /> +<glyph unicode="°" horiz-adv-x="720" d="M49 1038q0 125 89 214t214 89t214 -89t89 -214t-89 -214t-214 -89t-214 89t-89 214zM205 1038.5q0 -61.5 43 -104.5t104.5 -43t104.5 43t43 104.5t-43 104.5t-104.5 43t-104.5 -43t-43 -104.5z" /> +<glyph unicode="±" horiz-adv-x="915" d="M78 487v172h313v271h180v-271h316v-172h-316v-225h-180v225h-313zM98 0v170h768v-170h-768z" /> +<glyph unicode="²" d="M111 160q141 80 307 259t166 306q0 57 -39 88t-99 31q-70 0 -145.5 -32t-126.5 -81l-45 172q59 61 155.5 96t184.5 35q152 0 242 -75.5t90 -225.5q0 -154 -102.5 -300t-247.5 -240h385v-193h-686z" /> +<glyph unicode="³" d="M131 -178q111 2 213 34.5t181 114.5t79 199q0 152 -239 152q-90 0 -199 -11v164l108 17q100 55 236 163q70 57 70 115q0 35 -27 57.5t-61 22.5q-41 0 -114 -27.5t-124 -56.5l-53 -29l-54 162q74 43 121 66.5t118 45t136 21.5q125 0 199 -67.5t74 -171.5q0 -94 -80 -182.5 t-170 -123.5q115 -10 193.5 -93t78.5 -193q0 -141 -56 -250t-152.5 -171.5t-214.5 -92t-251 -29.5h-12v164z" /> +<glyph unicode="´" horiz-adv-x="692" d="M86 1255q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291z" /> +<glyph unicode="µ" horiz-adv-x="1058" d="M57 -371q92 145 93 277q0 45 -9.5 127t-9.5 141v49v781h215v-791q0 -53 84 -53q51 0 97 16.5t68.5 30.5t78.5 57v740h215v-818q0 -39 22.5 -47t55.5 15l67 -134q-78 -51 -155 -51q-59 0 -116.5 27t-78.5 76q-141 -102 -242 -103q-51 0 -88 19q12 -90 13 -152 q0 -80 -50 -164z" /> +<glyph unicode="¶" horiz-adv-x="1112" d="M61 778q-11 61 -11 122.5t11 122.5q23 122 125.5 207t255.5 85q45 0 95.5 -4t113 -11.5t94.5 -9.5v80h177v-78l106 19l-8 -168l-100 -23q-2 -340 -73 -715.5t-202 -642.5l-188 78q139 256 209.5 598t74.5 678q-27 2 -77 9.5t-91 11.5t-75 4q-116 -4 -116 -193 q0 -18 1 -39q14 -199 119 -221q9 -2 18 -1q64 0 152 71q-94 -272 -271 -273q-133 0 -225 85t-115 208z" /> +<glyph unicode="·" horiz-adv-x="528" d="M121 492q0 63 47 108t111 45q59 0 99 -38t40 -97q0 -63 -47 -108.5t-111 -45.5q-59 0 -99 38.5t-40 97.5z" /> +<glyph unicode="¸" horiz-adv-x="823" d="M375 -299q66 35 95.5 53.5t53 42t23.5 47.5q0 33 -40 37q-10 1 -20 1q-30 0 -61 -9l-39 -14l2 141h246q84 -61 84 -154q0 -78 -73 -142t-165 -103z" /> +<glyph unicode="¹" d="M201 799v155q117 0 250 41l26 9h174v-1004h-213v799h-237z" /> +<glyph unicode="º" horiz-adv-x="802" d="M94 1118.5q0 157.5 82 281.5t229.5 124t230.5 -124t83 -281.5t-83 -280.5t-230.5 -123t-229.5 123t-82 280.5zM258 1118.5q0 -108.5 41 -186.5t107 -78q68 0 108.5 78t40.5 186.5t-41 187t-108 78.5q-66 0 -107 -78.5t-41 -187z" /> +<glyph unicode="»" horiz-adv-x="1024" d="M98 788l146 117l407 -409l-403 -410l-146 117l295 289zM459 788l145 117l408 -409l-404 -410l-145 117l297 289z" /> +<glyph unicode="¼" horiz-adv-x="2537" d="M201 799v155q117 0 250 41l26 9h174v-1004h-213v799h-237zM743 -256l869 1769h198l-868 -1769h-199zM1675 166q145 373 494 838h209v-816h116v-188h-120v-283h-205v283h-483zM1896 188h273v502q-187 -281 -273 -502z" /> +<glyph unicode="½" horiz-adv-x="2537" d="M201 799v155q117 0 250 41l26 9h174v-1004h-213v799h-237zM743 -256l869 1769h198l-868 -1769h-199zM1726 160q141 80 307.5 259t166.5 306q0 57 -39 88t-99 31q-70 0 -145.5 -32t-126.5 -81l-45 172q59 61 155.5 96t184.5 35q152 0 242 -75.5t90 -225.5 q0 -154 -102.5 -300t-248.5 -240h385v-193h-686z" /> +<glyph unicode="¾" horiz-adv-x="2537" d="M131 -178q111 2 213 34.5t181 114.5t79 199q0 152 -239 152q-90 0 -199 -11v164l108 17q100 55 236 163q70 57 70 115q0 35 -27 57.5t-61 22.5q-41 0 -114 -27.5t-124 -56.5l-53 -29l-54 162q74 43 121 66.5t118 45t136 21.5q125 0 199 -67.5t74 -171.5q0 -94 -80 -182.5 t-170 -123.5q115 -10 193.5 -93t78.5 -193q0 -141 -56 -250t-152.5 -171.5t-214.5 -92t-251 -29.5h-12v164zM743 -256l869 1769h198l-868 -1769h-199zM1675 166q145 373 494 838h209v-816h116v-188h-120v-283h-205v283h-483zM1896 188h273v502q-187 -281 -273 -502z" /> +<glyph unicode="¿" horiz-adv-x="733" d="M57 -150q0 49 17.5 98.5t36 78t57.5 82.5l149 202q63 88 64 199v113h184v-111q0 -150 -17.5 -202t-105.5 -167l-108 -143q-63 -82 -64 -137q0 -100 78 -101q100 0 197 29v-172q-135 -33 -236 -33q-115 0 -183.5 73.5t-68.5 190.5zM330 881q0 61 48 107t109.5 46 t101.5 -36.5t40 -98t-48.5 -106.5t-109.5 -45t-101 37t-40 96z" /> +<glyph unicode="À" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM262 1659l172 176q121 -168 342 -328l-98 -139q-254 133 -416 291zM426 567h195l-97 398z" /> +<glyph unicode="Á" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM262 1507q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291zM426 567h195l-97 398z" /> +<glyph unicode="Â" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM174 1505q195 135 346 334q14 -20 41 -53t117 -121t188 -158l-98 -139q-109 57 -248 188q-111 -119 -248 -190zM426 567h195l-97 398z" /> +<glyph unicode="Ã" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM115 1581q90 72 149.5 102.5t118.5 30.5q63 0 146 -43t120 -43q45 0 70.5 12.5t99.5 61.5l88 -129q-90 -84 -141 -108.5t-131 -24.5q-47 0 -129 43t-129 43q-72 0 -184 -82zM426 567h195l-97 398z" /> +<glyph unicode="Ä" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM215 1528q0 59 44 104t101 45q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM426 567h195l-97 398zM561 1528q0 59 44 104t102 45q45 0 78.5 -32.5t33.5 -77.5 q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="Å" horiz-adv-x="1044" d="M23 0l407 1290h184l410 -1290h-229l-123 371h-295l-123 -371h-231zM283 1581q0 98 71.5 169t167.5 71t166 -72t70 -168q0 -98 -71 -169t-169 -71t-166.5 71t-68.5 169zM426 567h195l-97 398zM438 1581q0 -33 24.5 -58.5t55.5 -25.5q33 0 57.5 25.5t24.5 58.5t-23.5 57.5 t-52.5 24.5q-37 0 -61.5 -24.5t-24.5 -57.5z" /> +<glyph unicode="Æ" horiz-adv-x="1497" d="M18 0l408 1290h178l84 -262v262h698v-198h-473v-320h363v-201h-363v-368h494v-203h-717v393h-309l-131 -393h-232zM436 590h201l-100 375z" /> +<glyph unicode="Ç" horiz-adv-x="1017" d="M106 645q0 162 52.5 311.5t171.5 257t282 107.5q94 0 177 -23.5t120 -46.5l37 -22l-86 -154q-90 55 -248 55q-57 0 -120.5 -48t-102.5 -128q-59 -129 -59 -317q0 -78 12 -152.5t41 -151.5t89 -124t144 -47q135 0 246 78l103 -152q-90 -76 -236 -106q63 -59 64 -136 q0 -78 -73 -142t-165 -103l-106 100q66 35 95 53.5t53 42t24 47.5q0 33 -40 37q-10 1 -20 1q-30 0 -61 -9l-39 -14v131q-94 29 -165 97.5t-111 160.5t-59.5 191.5t-19.5 205.5z" /> +<glyph unicode="È" horiz-adv-x="929" d="M147 0v1290h697v-198h-473v-320h362v-201h-362v-368h493v-203h-717zM244 1659l172 176q127 -174 342 -328l-99 -139q-253 133 -415 291z" /> +<glyph unicode="É" horiz-adv-x="929" d="M147 0v1290h697v-198h-473v-320h362v-201h-362v-368h493v-203h-717zM244 1507q98 70 183 152t122 129l37 47l172 -176q-156 -156 -416 -291z" /> +<glyph unicode="Ê" horiz-adv-x="929" d="M147 0v1290h697v-198h-473v-320h362v-201h-362v-368h493v-203h-717zM152 1505q190 131 348 334q14 -20 40.5 -53t117 -121t186.5 -158l-99 -139q-111 59 -247 188q-111 -119 -246 -190z" /> +<glyph unicode="Ë" horiz-adv-x="929" d="M147 0v1290h697v-198h-473v-320h362v-201h-362v-368h493v-203h-717zM201 1528q0 59 43 104t100 45q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -75.5 30t-30.5 75zM547 1528q0 59 43 104t100 45q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5 q-45 0 -75.5 30t-30.5 75z" /> +<glyph unicode="Ì" horiz-adv-x="487" d="M-12 1659l172 176q121 -168 342 -328l-101 -139q-253 135 -413 291zM133 0v1290h221v-1290h-221z" /> +<glyph unicode="Í" horiz-adv-x="487" d="M-12 1507q96 70 182 152t123 129l37 47l172 -176q-182 -168 -416 -291zM133 0v1290h221v-1290h-221z" /> +<glyph unicode="Î" horiz-adv-x="487" d="M-106 1505q190 131 348 334q14 -20 40.5 -53t116.5 -121t187 -158l-99 -139q-111 59 -247 188q-113 -121 -246 -190zM133 0v1290h221v-1290h-221z" /> +<glyph unicode="Ï" horiz-adv-x="487" d="M-61 1528q0 59 43 104t100 45q47 0 81 -32.5t34 -77.5q0 -57 -46.5 -100.5t-103.5 -43.5q-45 0 -76.5 30t-31.5 75zM133 0v1290h221v-1290h-221zM285 1528q0 59 43 104t100 45q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75z" /> +<glyph unicode="Ñ" horiz-adv-x="1134" d="M152 0v1290h262l286 -651q33 -74 60 -186v837h223v-1290h-215l-408 903q14 -84 15 -170v-733h-223zM168 1581q92 72 150.5 102.5t119.5 30.5q63 0 145.5 -43t118.5 -43q45 0 73 12.5t97 61.5l89 -129q-90 -84 -141.5 -108.5t-129.5 -24.5q-47 0 -130 43t-128 43 q-74 0 -186 -82z" /> +<glyph unicode="Ò" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM297 1659l172 176q121 -168 342 -328l-98 -139q-254 133 -416 291zM326 645 q0 -84 11 -161.5t35.5 -152.5t74 -120t115 -45t114.5 45t73.5 120t36 152.5t11.5 161.5t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162z" /> +<glyph unicode="Ó" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM301 1507q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291z M326 645q0 -84 11 -162t35.5 -152.5t74 -119.5t115 -45t114.5 45t73.5 119.5t36 152.5t11.5 162t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162z" /> +<glyph unicode="Ô" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM219 1505q195 135 346 334q14 -20 41 -53t117 -121t188 -158l-98 -139 q-109 57 -248 188q-111 -119 -248 -190zM326 645q0 -84 11 -161.5t35.5 -152.5t74 -120t115 -45t114.5 45t73.5 120t36 152.5t11.5 161.5t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162z" /> +<glyph unicode="Õ" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM156 1581q92 72 150.5 102.5t119.5 30.5q63 0 146 -43t120 -43q45 0 72 12.5 t96 61.5l88 -129q-90 -84 -140 -108.5t-130 -24.5q-47 0 -129 43t-129 43q-74 0 -184 -82zM326 645q0 -84 11 -161.5t35.5 -152.5t74 -120t115 -45t114.5 45t73.5 120t36 152.5t11.5 161.5t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5 t-11 -162z" /> +<glyph unicode="Ö" horiz-adv-x="1122" d="M102 645q0 119 27 234.5t79 216t144 163t209 62.5q119 0 211 -61.5t144.5 -164t78 -216t25.5 -234.5t-25.5 -234.5t-78 -216t-144.5 -164t-211 -61.5t-211 61.5t-144 164t-78 216t-26 234.5zM260 1528q0 59 43 104t100 45q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5 t-103 -43.5q-45 0 -76 30t-31 75zM326 645q0 -84 11 -161.5t35.5 -152.5t74 -120t115 -45t114.5 45t73.5 120t36 152.5t11.5 161.5t-11.5 162t-36 152.5t-73.5 119.5t-114.5 45t-115 -45t-74 -119.5t-35.5 -152.5t-11 -162zM606 1528q0 59 43 104t101 45q47 0 79.5 -32.5 t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -76 30t-31 75z" /> +<glyph unicode="Ø" horiz-adv-x="1122" d="M102 655q0 111 26 221.5t77 214t143 167t213 63.5q66 0 133 -23l68 236h102l-82 -283q121 -90 179.5 -259t58.5 -345q0 -113 -24.5 -225.5t-76 -217t-144.5 -170t-214 -65.5q-72 0 -139 25l-80 -281h-102l94 332q-119 90 -175.5 261t-56.5 349zM326 635q0 -254 71 -373 l242 846q-33 16 -78 16q-235 0 -235 -489zM477 184q39 -18 84 -18q236 0 236 481q0 262 -76 385z" /> +<glyph unicode="Ù" horiz-adv-x="1142" d="M150 506v784h223v-784q0 -340 198.5 -340t198.5 340v784h223v-784q0 -231 -102.5 -384t-319.5 -153t-319 153t-102 384zM309 1659l172 176q125 -170 344 -328l-100 -139q-252 133 -416 291z" /> +<glyph unicode="Ú" horiz-adv-x="1142" d="M150 506v784h223v-784q0 -340 198.5 -340t198.5 340v784h223v-784q0 -231 -102.5 -384t-319.5 -153t-319 153t-102 384zM319 1507q98 70 183.5 152t122.5 129l37 47l172 -176q-156 -156 -414 -291z" /> +<glyph unicode="Û" horiz-adv-x="1142" d="M150 506v784h223v-784q0 -340 198.5 -340t198.5 340v784h223v-784q0 -231 -102.5 -384t-319.5 -153t-319 153t-102 384zM225 1505q195 135 346 334q14 -20 41 -53t117 -121t189 -158l-99 -139q-111 59 -248 188q-111 -119 -247 -190z" /> +<glyph unicode="Ü" horiz-adv-x="1142" d="M150 506v784h223v-784q0 -340 198.5 -340t198.5 340v784h223v-784q0 -231 -102.5 -384t-319.5 -153t-319 153t-102 384zM270 1528q0 59 44 104t102 45q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM616 1528q0 59 44.5 104t101.5 45 q45 0 78.5 -32.5t33.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="Ý" horiz-adv-x="964" d="M27 1290h239l215 -583l228 583h229l-344 -809v-481h-223v483zM385 1583q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291z" /> +<glyph unicode="ß" horiz-adv-x="1171" d="M27 846v158h129v131q0 143 113.5 226t260.5 83q180 0 299 -95.5t119 -271.5q0 -31 -5 -66.5t-11 -59.5l-18 -66q-11 -43 -15 -62q-68 31 -135 31q-113 0 -113 -98q0 -12 6.5 -24.5t14.5 -21t22.5 -18.5t22.5 -15t26.5 -14.5t20.5 -11.5l250 -135q57 -31 91 -88t34 -125 q0 -158 -103.5 -246t-265.5 -88q-172 0 -324 99l86 161q111 -76 246 -75q68 0 111 30.5t43 94.5q0 72 -92 120l-273 144q-129 68 -129 207q0 117 74 196.5t190 79.5q31 0 48 -2q8 31 8 70q0 168 -215 168q-68 0 -120 -42t-52 -110v-1110h-215v827z" /> +<glyph unicode="à" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM229 1407l172 176q123 -168 342 -328l-98 -139q-258 137 -416 291zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90z" /> +<glyph unicode="á" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM240 1255q98 70 183 152t122 129l37 47l172 -176q-156 -156 -416 -291zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90z" /> +<glyph unicode="â" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM133 1253q186 127 348 334q14 -20 41 -53t117 -121t186 -158l-98 -139q-117 61 -248 187q-115 -119 -246 -189zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90z" /> +<glyph unicode="ã" horiz-adv-x="954" d="M74 1329q92 72 150.5 102.5t119.5 30.5q63 0 145 -43t119 -43q45 0 73 12.5t97 61.5l88 -129q-90 -84 -140 -108.5t-130 -24.5q-47 0 -129 43t-129 43q-74 0 -186 -82zM78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127 t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75t-68.5 212zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90z " /> +<glyph unicode="ä" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM180 1276q0 57 44 103t102 46q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90zM526 1276q0 57 44 103t102 46q47 0 79.5 -32.5 t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="å" horiz-adv-x="954" d="M78 256q0 84 61 203l441 190v101q0 100 -119 100q-63 0 -136 -36t-124 -87l-74 129q63 76 160.5 127t191.5 51q145 0 231.5 -72.5t86.5 -213.5v-584q0 -53 39 -53q12 0 36 12l56 -109q-70 -43 -146 -43q-68 0 -117 39t-61 107q-96 -147 -254 -148q-135 0 -203.5 75 t-68.5 212zM240 1349.5q0 98.5 71.5 169t167.5 70.5t166 -70.5t70 -169t-71 -169t-169 -70.5t-166.5 70.5t-68.5 169zM281 258q0 -45 25.5 -79t68.5 -34q92 0 150.5 91.5t58.5 193.5v37l-287 -119q-16 -57 -16 -90zM393 1349.5q0 -32.5 25.5 -57t56.5 -24.5q33 0 57.5 24.5 t24.5 57t-23.5 57.5t-52.5 25q-37 0 -62.5 -25t-25.5 -57.5z" /> +<glyph unicode="æ" horiz-adv-x="1511" d="M102 264q0 66 50 189l462 190v98q-16 41 -46.5 73t-67.5 32q-61 0 -137 -49t-132 -109l-88 146q68 84 169.5 142t201.5 58q182 0 260 -108q115 109 273 108q152 0 255 -118.5t109 -272.5l4 -135h-588q0 -53 10.5 -109.5t32 -114t64.5 -94t100 -36.5q137 0 256 71l74 -155 q-156 -100 -340 -101q-102 0 -188 50.5t-142 138.5l-129 -111q-90 -78 -186 -78q-129 0 -203 82t-74 213zM315 258q0 -43 21.5 -71.5t64.5 -28.5q20 0 45 9t54.5 29.5t47 34t49.5 41t38 31.5q-18 72 -23 164l-272 -117q-25 -74 -25 -92zM856 686h332q-10 68 -50 118 t-103.5 50t-109.5 -50t-69 -118z" /> +<glyph unicode="ç" horiz-adv-x="843" d="M86 492q0 133 40 251.5t137 204.5t237 86q76 0 127 -15t73 -32l95 -72l-109 -131q-90 70 -184 70q-76 0 -138.5 -95t-62.5 -247q0 -147 58.5 -254.5t148.5 -107.5q49 0 117.5 33.5t115.5 72.5l76 -152q-88 -74 -199 -112q74 -59 74 -146q0 -78 -72.5 -142t-164.5 -103 l-107 100q66 35 95.5 53.5t53 42t23.5 47.5q0 33 -40 37q-10 1 -20 1q-30 0 -59 -9l-41 -14v137q-92 37 -156.5 124t-91 181.5t-26.5 190.5z" /> +<glyph unicode="è" horiz-adv-x="960" d="M88 526q0 203 120 355.5t318 152.5q145 0 253 -118.5t112 -266.5q0 -23 1 -67.5t1 -67.5h-586q-2 -12 -2 -37q0 -66 17.5 -136.5t64.5 -129.5t115 -59q123 0 268 110l72 -139q-74 -61 -173.5 -107.5t-179.5 -46.5q-143 0 -237 89t-129 209t-35 259zM252 1407l172 176 q127 -174 342 -328l-98 -139q-254 133 -416 291zM336 692h342q-6 78 -49 130t-117 52q-131 0 -176 -182z" /> +<glyph unicode="é" horiz-adv-x="960" d="M88 526q0 203 120 355.5t318 152.5q145 0 253 -118.5t112 -266.5q0 -23 1 -67.5t1 -67.5h-586q-2 -12 -2 -37q0 -66 17.5 -136.5t64.5 -129.5t115 -59q123 0 268 110l72 -139q-74 -61 -173.5 -107.5t-179.5 -46.5q-143 0 -237 89t-129 209t-35 259zM276 1255 q96 70 182.5 152t123.5 129l36 47l173 -176q-184 -170 -416 -291zM336 692h342q-6 78 -49 130t-117 52q-131 0 -176 -182z" /> +<glyph unicode="ê" horiz-adv-x="960" d="M88 526q0 203 120 355.5t318 152.5q145 0 253 -118.5t112 -266.5q0 -23 1 -67.5t1 -67.5h-586q-2 -12 -2 -37q0 -66 17.5 -136.5t64.5 -129.5t115 -59q123 0 268 110l72 -139q-74 -61 -173.5 -107.5t-179.5 -46.5q-143 0 -237 89t-129 209t-35 259zM164 1253 q193 131 346 334q14 -20 41 -53t117 -121t188 -158l-98 -139q-117 61 -248 187q-115 -119 -248 -189zM336 692h342q-6 78 -49 130t-117 52q-131 0 -176 -182z" /> +<glyph unicode="ë" horiz-adv-x="960" d="M88 526q0 203 120 355.5t318 152.5q145 0 253 -118.5t112 -266.5q0 -23 1 -67.5t1 -67.5h-586q-2 -12 -2 -37q0 -66 17.5 -136.5t64.5 -129.5t115 -59q123 0 268 110l72 -139q-74 -61 -173.5 -107.5t-179.5 -46.5q-143 0 -237 89t-129 209t-35 259zM217 1276q0 57 44 103 t99 46q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM336 692h342q-6 78 -49 130t-117 52q-131 0 -176 -182zM563 1276q0 57 44 103t100 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="ì" horiz-adv-x="487" d="M-16 1407l172 176q127 -174 342 -328l-99 -139q-259 135 -415 291zM137 0v1004h213v-1004h-213z" /> +<glyph unicode="í" horiz-adv-x="487" d="M-10 1255q98 70 183 152t122 129l37 47l172 -176q-156 -156 -416 -291zM137 0v1004h213v-1004h-213z" /> +<glyph unicode="î" horiz-adv-x="487" d="M-98 1253q186 127 348 334q14 -20 40.5 -53t117 -121t186.5 -158l-98 -139q-117 61 -248 187q-115 -119 -248 -189zM137 0v1004h213v-1004h-213z" /> +<glyph unicode="ï" horiz-adv-x="487" d="M-55 1276q0 57 44 103t101 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75zM137 0v1004h213v-1004h-213zM291 1276q0 57 44 103t101 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75z" /> +<glyph unicode="ñ" horiz-adv-x="1062" d="M141 1329q90 72 149.5 102.5t119.5 30.5q63 0 146 -43t120 -43q45 0 72.5 12.5t97.5 61.5l88 -129q-90 -84 -141.5 -108.5t-130.5 -24.5q-45 0 -128 43t-131 43q-72 0 -184 -82zM154 0v1004h215v-74q53 43 132 73.5t142 30.5q158 0 214 -78.5t56 -214.5v-741h-215v741 q0 61 -15 84t-65 23q-115 0 -249 -107v-741h-215z" /> +<glyph unicode="ò" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM250 1407l172 176q125 -170 344 -328l-100 -139q-258 137 -416 291zM305 502q0 -143 53.5 -245.5t141.5 -102.5t141 102 t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5z" /> +<glyph unicode="ó" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM250 1255q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291zM305 502q0 -143 53.5 -245.5t141.5 -102.5 t141 102t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5z" /> +<glyph unicode="ô" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM162 1253q186 127 348 334q14 -20 41 -53t117 -121t186 -158l-98 -139q-117 61 -248 187q-115 -119 -248 -189zM305 502 q0 -143 53.5 -245.5t141.5 -102.5t141 102t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5z" /> +<glyph unicode="õ" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM113 1329q90 72 149.5 102.5t118.5 30.5q63 0 146 -43t120 -43q45 0 70.5 12.5t99.5 61.5l88 -129q-90 -84 -141 -108.5 t-131 -24.5q-47 0 -129 43t-129 43q-72 0 -185 -82zM305 502q0 -143 53.5 -245.5t141.5 -102.5t141 102t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5z" /> +<glyph unicode="ö" horiz-adv-x="999" d="M90 502q0 131 43 248.5t139.5 200.5t227.5 83t227 -83t139 -200.5t43 -248.5q0 -133 -43 -251t-139 -200t-227 -82t-227.5 82t-139.5 200t-43 251zM199 1276q0 57 44 103t101 46q45 0 79 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75zM305 502 q0 -143 53.5 -245.5t141.5 -102.5t141 102t53 246q0 141 -53 244.5t-141 103.5t-141.5 -103.5t-53.5 -244.5zM545 1276q0 57 44 103t99 46q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75z" /> +<glyph unicode="÷" horiz-adv-x="1001" d="M96 420v184h807v-184h-807zM342 213q0 63 47 108.5t111 45.5q59 0 99 -38t40 -98q0 -61 -47 -106t-111 -45q-59 0 -99 37t-40 96zM342 799q0 63 47 108t111 45q59 0 99 -37.5t40 -97.5q0 -63 -47 -108t-111 -45q-59 0 -99 38t-40 97z" /> +<glyph unicode="ø" horiz-adv-x="999" d="M90 500q0 129 42 246.5t138.5 202.5t229.5 85q49 0 96 -12l49 172h103l-60 -213q111 -68 166 -202t55 -279q0 -205 -107.5 -368t-301.5 -163q-49 0 -99 13l-57 -205h-102l69 246q-111 68 -166 201t-55 276zM305 500q0 -182 64 -275l176 619q-20 6 -45 6q-59 0 -100.5 -36 t-59.5 -94t-26.5 -111.5t-8.5 -108.5zM453 160q18 -8 47 -8q59 0 100 35.5t59.5 94t26.5 110.5t8 108q0 184 -65 278z" /> +<glyph unicode="ù" horiz-adv-x="1058" d="M152 258v746h215v-746q0 -53 17 -75.5t71 -22.5q104 0 241 102v742h215v-818q0 -68 41 -98l-65 -119q-47 4 -96.5 43t-61.5 80q-66 -49 -149.5 -86t-151.5 -37q-147 0 -211.5 71t-64.5 218zM276 1407l173 176q121 -168 342 -328l-99 -139q-260 135 -416 291z" /> +<glyph unicode="ú" horiz-adv-x="1058" d="M152 258v746h215v-746q0 -53 17 -75.5t71 -22.5q104 0 241 102v742h215v-818q0 -68 41 -98l-65 -119q-47 4 -96.5 43t-61.5 80q-66 -49 -149.5 -86t-151.5 -37q-147 0 -211.5 71t-64.5 218zM268 1255q96 70 182.5 152t122.5 129l37 47l172 -176q-156 -156 -415 -291z" /> +<glyph unicode="û" horiz-adv-x="1058" d="M152 258v746h215v-746q0 -53 17 -75.5t71 -22.5q104 0 241 102v742h215v-818q0 -68 41 -98l-65 -119q-47 4 -96.5 43t-61.5 80q-66 -49 -149.5 -86t-151.5 -37q-147 0 -211.5 71t-64.5 218zM176 1253q184 127 346 334q14 -20 41 -53t117 -121t188 -158l-100 -139 q-119 63 -246 187q-125 -125 -248 -189z" /> +<glyph unicode="ü" horiz-adv-x="1058" d="M152 258v746h215v-746q0 -53 17 -75.5t71 -22.5q104 0 241 102v742h215v-818q0 -68 41 -98l-65 -119q-47 4 -96.5 43t-61.5 80q-66 -49 -149.5 -86t-151.5 -37q-147 0 -211.5 71t-64.5 218zM223 1276q0 57 43 103t101 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5 t-103 -43.5q-45 0 -77 30t-32 75zM569 1276q0 57 43 103t101 46q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -76 30t-31 75z" /> +<glyph unicode="ý" horiz-adv-x="929" d="M23 1004h227l180 -564q43 -137 53 -207q125 360 205 771h219q-33 -147 -79 -314.5t-115.5 -381.5t-158.5 -383t-175 -230q-33 -25 -86 -46.5t-90 -29.5l-37 -10l-47 162q90 29 139 71q47 41 113 158zM367 1296q96 70 182 152t123 129l37 47l172 -176q-184 -170 -416 -291 z" /> +<glyph unicode="ÿ" horiz-adv-x="929" d="M23 1004h227l180 -564q43 -137 53 -207q125 360 205 771h219q-33 -147 -79 -314.5t-115.5 -381.5t-158.5 -383t-175 -230q-33 -25 -86 -46.5t-90 -29.5l-37 -10l-47 162q90 29 139 71q47 41 113 158zM182 1276q0 57 44 103t100 46q47 0 80.5 -32.5t33.5 -77.5 q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM528 1276q0 57 43 103t101 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="Œ" horiz-adv-x="1290" d="M102 653q0 106 25 212t74 204t137 159.5t203 61.5h663v-198h-473v-320h363v-201h-363v-368h494v-203h-684q-117 0 -206 63.5t-137 165t-72 208.5t-24 216zM319 633q0 -408 189 -440v903q-189 -35 -189 -463z" /> +<glyph unicode="œ" horiz-adv-x="1558" d="M102 494q0 129 43 248.5t139.5 205.5t229.5 86q178 0 285 -139q123 139 303 139q152 0 255 -118.5t109 -272.5l7 -135h-590q0 -72 17.5 -147.5t67.5 -141t122 -65.5q139 0 256 71l73 -155q-162 -100 -354 -101q-156 0 -272 133q-113 -133 -279 -133q-129 0 -226.5 83 t-141.5 200t-44 242zM317 493.5q0 -55.5 8.5 -107.5t28 -108.5t60.5 -91t100 -34.5q121 0 174 165q-23 96 -22 185q0 100 22 178q-49 170 -174 170q-57 0 -99 -37t-61.5 -96.5t-28 -113.5t-8.5 -109.5zM913 686h332q-10 68 -51 118t-104.5 50t-107.5 -49t-69 -119z" /> +<glyph unicode="Ÿ" horiz-adv-x="964" d="M27 1290h239l215 -583l228 583h229l-344 -809v-481h-223v483zM186 1528q0 59 43 104t101 45q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -76 30t-31 75zM532 1528q0 59 43 104t101 45q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5 q-45 0 -76 30t-31 75z" /> +<glyph unicode="ˆ" horiz-adv-x="823" d="M92 1253q193 131 346 334q14 -20 41 -53t117 -121t188 -158l-98 -139q-117 61 -248 187q-115 -119 -248 -189z" /> +<glyph unicode="˜" horiz-adv-x="778" d="M-14 1329q92 72 150.5 102.5t117.5 30.5q63 0 146 -43t120 -43q45 0 73 12.5t97 61.5l88 -129q-90 -84 -141 -108.5t-129 -24.5q-47 0 -130 43t-128 43q-74 0 -187 -82z" /> +<glyph unicode="–" horiz-adv-x="733" d="M0 430v184h733v-184h-733z" /> +<glyph unicode="—" horiz-adv-x="1245" d="M0 432v184h1245v-184h-1245z" /> +<glyph unicode="‘" horiz-adv-x="546" d="M70 1006q109 213 290 397l127 -88q-137 -195 -137 -328q0 -18 4 -51q-61 -49 -121 -49q-43 0 -87.5 37t-75.5 82z" /> +<glyph unicode="’" horiz-adv-x="552" d="M63 891q137 199 138 330q0 25 -6 49q68 51 122 51q43 0 88.5 -37t75.5 -82q-109 -209 -291 -399z" /> +<glyph unicode="‚" horiz-adv-x="528" d="M27 -229q137 199 137 327q0 35 -4 52q61 49 123 49q43 0 89 -37t74 -82q-109 -213 -290 -397z" /> +<glyph unicode="“" horiz-adv-x="913" d="M66 1006q109 213 290 397l127 -88q-137 -195 -137 -328q0 -18 4 -51q-61 -49 -121 -49q-43 0 -87.5 37t-75.5 82zM440 1006q117 221 293 397l127 -88q-137 -195 -137 -328q0 -18 4 -51q-61 -49 -123 -49q-80 0 -164 119z" /> +<glyph unicode="”" horiz-adv-x="913" d="M59 891q137 199 138 330q0 25 -7 49q68 51 123 51q43 0 88 -37t76 -82q-109 -209 -291 -399zM434 891q139 203 139 330q0 25 -6 49q63 51 123 51q41 0 88 -37t76 -82q-109 -209 -291 -399z" /> +<glyph unicode="„" horiz-adv-x="888" d="M27 -223q137 195 137 327q0 35 -4 52q61 49 123 49q41 0 88 -37t75 -82q-109 -213 -290 -397zM371 -223q137 195 137 327q0 18 -4 52q61 49 121 49q43 0 88 -37t75 -82q-109 -213 -290 -397z" /> +<glyph unicode="•" horiz-adv-x="868" d="M63 645.5q0 151.5 109 260t260.5 108.5t261 -108.5t109.5 -260t-109.5 -260.5t-261 -109t-260.5 109t-109 260.5z" /> +<glyph unicode="…" horiz-adv-x="1236" d="M100 104.5q0 61.5 47.5 106.5t110.5 45q59 0 99 -37t40 -96q0 -61 -47 -107.5t-108.5 -46.5t-101.5 37t-40 98.5zM451 104.5q0 61.5 47 106.5t110 45q59 0 99.5 -37t40.5 -96q0 -61 -47.5 -107.5t-109 -46.5t-101 37t-39.5 98.5zM797 104.5q0 61.5 47 106.5t110 45 q59 0 99.5 -37t40.5 -96q0 -61 -47.5 -107.5t-108.5 -46.5t-101 37t-40 98.5z" /> +<glyph unicode="‹" horiz-adv-x="614" d="M33 512l403 410l146 -117l-295 -289l299 -297l-146 -117z" /> +<glyph unicode="›" horiz-adv-x="614" d="M29 219l297 293l-293 293l145 117l404 -410l-408 -410z" /> +<glyph unicode="™" horiz-adv-x="1642" d="M145 1278v129h500v-129h-182v-684h-137v684h-181zM713 596v813h166l192 -551l19 -72q6 35 18 72l192 551h166v-813h-139v448l8 95l-186 -543h-117l-188 543l10 -95v-448h-141z" /> +<glyph unicode="" horiz-adv-x="1004" d="M0 1005h1005v-1005h-1005v1005z" /> +<glyph unicode="fi" horiz-adv-x="1069" d="M37 846v158h127v92q0 96 38 166.5t101.5 107.5t133 54.5t147.5 17.5q51 0 107.5 -8.5t119.5 -25.5t104 -55t41 -91q0 -61 -44 -100.5t-105 -39.5q-59 0 -86 29t-38 64.5t-24 44.5q-45 27 -112 26q-66 0 -116 -60.5t-50 -178.5v-43h188v-181h-188v-823h-217v827zM713 0 v1004h211v-1004h-211z" /> +<glyph unicode="fl" horiz-adv-x="1069" d="M37 846v158h129v73q0 174 100.5 264.5t253.5 90.5q119 0 191 -91l4 -4v84h211v-1001q0 -182 69 -420h-221q-59 152 -59 420v774q-51 74 -156 74q-100 0 -139 -57.5t-39 -163.5v-43h188v-181h-188v-823h-215v827z" /> +<glyph unicode="ffi" horiz-adv-x="1658" d="M27 850v154h131v114q0 63 21.5 116.5t54 86.5t73.5 58.5t82 38t74 19.5t53 9h23l36 -156q-8 0 -21 -2t-49 -13t-62.5 -28.5t-48 -54.5t-21.5 -84v-104h186v-181h-186v-823h-215v827zM612 850v154h131v114q0 63 21.5 116.5t54.5 86.5t74 58.5t82 38t73.5 19.5t53.5 9h22 l37 -156q-8 0 -21.5 -2t-49 -13t-62.5 -28.5t-48.5 -54.5t-21.5 -84v-104h187v-181h-187v-823h-215v827zM1274 1251q0 57 44 99.5t101 42.5q55 0 96 -38t41 -93q0 -57 -44 -99.5t-101 -42.5q-55 0 -96 38t-41 93zM1309 0v1004h211v-1004h-211z" /> +<glyph unicode="ffl" horiz-adv-x="1654" d="M27 850v154h131v114q0 63 21.5 116.5t54 86.5t73.5 58.5t82 38t74 19.5t53 9h23l36 -156q-8 0 -21 -2t-49 -13t-62.5 -28.5t-48 -54.5t-21.5 -84v-104h186v-181h-186v-823h-215v827zM612 850v154h131v114q0 63 21.5 116.5t54.5 86.5t74 58.5t82 38t73.5 19.5t53.5 9h22 l37 -156q-8 0 -21.5 -2t-49 -13t-62.5 -28.5t-48.5 -54.5t-21.5 -84v-104h187v-181h-187v-823h-215v827zM1296 420v1003h211v-1003q0 -182 70 -420h-221q-60 152 -60 420z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-bold-webfont.ttf b/app/assets/fonts/delicious-bold-webfont.ttf Binary files differnew file mode 100755 index 000000000..2ec5d3e49 --- /dev/null +++ b/app/assets/fonts/delicious-bold-webfont.ttf diff --git a/app/assets/fonts/delicious-bold-webfont.woff b/app/assets/fonts/delicious-bold-webfont.woff Binary files differnew file mode 100755 index 000000000..b47277ef1 --- /dev/null +++ b/app/assets/fonts/delicious-bold-webfont.woff diff --git a/app/assets/fonts/delicious-bolditalic-webfont.eot b/app/assets/fonts/delicious-bolditalic-webfont.eot Binary files differnew file mode 100755 index 000000000..8e8957864 --- /dev/null +++ b/app/assets/fonts/delicious-bolditalic-webfont.eot diff --git a/app/assets/fonts/delicious-bolditalic-webfont.svg b/app/assets/fonts/delicious-bolditalic-webfont.svg new file mode 100755 index 000000000..7368c90f2 --- /dev/null +++ b/app/assets/fonts/delicious-bolditalic-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : 40 I1995 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousBoldItalic" horiz-adv-x="921" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="	" horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="!" horiz-adv-x="514" d="M74 104.5q0 61.5 50 105.5t114 44q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5zM168 381l14 309q6 160 10.5 223.5t21.5 176t50 217.5l31 102l237 27q-100 -254 -135 -602q-20 -219 -45 -453h-184z" /> +<glyph unicode=""" horiz-adv-x="583" d="M166 999l4 314l51 137h141l11 -147l-90 -304h-117zM453 999l4 314l51 137h141l10 -147l-90 -304h-116z" /> +<glyph unicode="#" horiz-adv-x="1138" d="M106 412l29 206h164l25 216h-140l37 206h129l39 379h189l-39 -379h137l41 344h188l-41 -344h160l-18 -206h-164l-27 -216h129l-18 -206h-138l-51 -412h-188l51 412h-139l-51 -359h-189l51 359h-166zM487 618h140l26 216h-139z" /> +<glyph unicode="$" horiz-adv-x="878" d="M37 92l121 164q78 -68 170 -82l61 393l-131 99q-124 91 -124 240q0 25 3 52q16 147 114.5 246t250.5 113l16 106h123l-16 -110q59 -12 116.5 -48t85.5 -65l27 -29l-137 -114q-45 45 -123 59l-55 -364l75 -54q164 -119 164 -292q0 -164 -100 -283t-256 -148l-21 -131h-122 l18 127q-68 8 -133.5 38t-95.5 57zM367 954q0 -63 61 -120l43 272q-104 -39 -104 -152zM455 197q102 53 102 159q0 68 -59 127z" /> +<glyph unicode="%" horiz-adv-x="1431" d="M90 1032q0 119 84 204t205 85q104 0 182 -66l311 64l144 -39l-451 -1280h-200l401 1106h-109q10 -41 11 -74q0 -119 -84 -204t-205 -85t-205 85t-84 204zM266 1032q0 -45 33 -78.5t80 -33.5t80 33.5t33 78.5t-33 79t-80 34t-80 -34t-33 -79zM756 256q0 121 85 205 t203.5 84t203.5 -84t85 -205t-85 -205t-203.5 -84t-203.5 84t-85 205zM932 256q0 -47 33.5 -80t79 -33t79 33t33.5 80t-33.5 80t-79 33t-79 -33t-33.5 -80z" /> +<glyph unicode="&" horiz-adv-x="1800" d="M117 590q0 262 184 457.5t451 246.5l28 -182q-201 -53 -325 -217q-100 -132 -101 -284q0 -38 6 -76q309 252 551 469q105 95 105 170q0 51 -49 97q-47 44 -97 62l54 115q12 -2 32.5 -8t72.5 -31t92 -53.5t73 -79.5t33 -111q0 -141 -166 -280l-631 -533q135 -199 401 -198 q236 0 420.5 164.5t184.5 398.5q0 78 -21 133h-137l-25 115q86 41 148 41q156 0 209 -80t53 -221q0 -209 -128 -381t-317.5 -262.5t-392.5 -92.5h-10q-182 0 -338 71q-160 73 -260 217.5t-100 332.5z" /> +<glyph unicode="'" horiz-adv-x="301" d="M164 999l6 314l51 137h141l11 -147l-90 -304h-119z" /> +<glyph unicode="(" horiz-adv-x="716" d="M140 522q11 213 86 410.5t226.5 379t356.5 306.5l-2 -205q-199 -125 -331 -348t-157 -461q-5 -52 -5 -103q0 -183 69 -354q39 -84 69.5 -123.5t137.5 -146.5l-98 -139q-211 174 -289 383q-64 163 -64 345q0 28 1 56z" /> +<glyph unicode=")" horiz-adv-x="696" d="M-33 -117q129 102 172 144.5t109 128.5q154 205 199 455q19 103 19 200q0 140 -39 268q-67 217 -226 340l47 195q229 -170 327 -427q69 -181 70 -376q0 -81 -12 -164q-41 -283 -209 -538q-129 -197 -389 -388z" /> +<glyph unicode="*" horiz-adv-x="808" d="M74 758l229 264l-227 76l69 176l205 -100l-31 208h322l-102 -208l217 100l49 -176l-250 -82l145 -307h-202l-82 192l-164 -199z" /> +<glyph unicode="+" horiz-adv-x="964" d="M92 418v188h299v322h180v-322h301v-188h-301v-322h-180v322h-299z" /> +<glyph unicode="," horiz-adv-x="528" d="M-55 -201q106 129 141 211q18 41 25 133l25 11q15 6 49 15t51 9q37 -2 70.5 -31.5t47.5 -56.5l15 -29l-70 -110q-45 -72 -262 -246z" /> +<glyph unicode="-" horiz-adv-x="618" d="M106 436v172h406v-172h-406z" /> +<glyph unicode="." horiz-adv-x="528" d="M74 104.5q0 61.5 50 105.5t114 44q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5z" /> +<glyph unicode="/" horiz-adv-x="755" d="M-154 -303l793 1853h201l-805 -1853h-189z" /> +<glyph unicode="0" d="M49 383q0 104 32 215t90 210t155.5 162.5t214.5 63.5q168 0 249.5 -116.5t81.5 -284.5q0 -152 -51 -299.5t-165.5 -256t-268.5 -108.5q-168 0 -253 122t-85 292zM264 399q0 -98 32 -168.5t103 -70.5q90 0 151.5 78.5t84 178t22.5 204.5q0 217 -141 217q-80 0 -139.5 -75 t-86 -172.5t-26.5 -191.5z" /> +<glyph unicode="1" d="M231 791l25 167q133 0 260 43h164l-143 -1001h-199l115 791h-222z" /> +<glyph unicode="2" d="M102 178q180 92 357 275q182 197 182 303q0 35 -24.5 58.5t-71.5 23.5q-94 0 -260 -97l-82 148q84 59 120.5 81.5t104.5 43t147 20.5q233 0 277 -182q8 -35 8 -71q0 -56 -19 -114q-32 -97 -99.5 -185.5t-135 -155t-143.5 -125.5h344l-31 -201h-661z" /> +<glyph unicode="3" d="M53 -182q135 12 250 51q274 102 275 338q0 47 -33 72.5t-66 29.5t-86 4q-98 0 -184 -10l-14 178q33 4 78.5 8.5t58.5 6.5l223 161q88 66 88 129q0 25 -17.5 40.5t-45.5 15.5q-41 0 -166 -54l-119 -51l-64 152l115 53q201 92 281 92q111 0 172 -57t61 -170 q0 -68 -49 -134.5t-98 -103.5t-127 -86q211 -31 211 -237q0 -172 -96.5 -307.5t-254.5 -204.5q-168 -72 -358 -72z" /> +<glyph unicode="4" d="M37 0l16 170l58 86q229 356 473 662l69 86h215l-127 -824h121l-33 -180h-124q-55 -242 -127 -352l-162 69q31 66 80 283h-459zM293 180h237l74 475q-61 -86 -311 -475z" /> +<glyph unicode="5" d="M63 -180q143 0 275 59q113 49 183.5 134t70.5 196q0 106 -179 106q-65 0 -155 -14l-78 -14l164 717h533l-29 -193h-334l-76 -313q43 6 92 6q119 0 201 -67.5t82 -188.5q0 -178 -103.5 -311.5t-267.5 -204.5q-162 -74 -348 -74z" /> +<glyph unicode="6" d="M96 381q0 426 346 719q152 127 359 223l65 -145q-166 -100 -227 -150q-162 -123 -256 -311q-35 -70 -58.5 -168t-23.5 -176q0 -88 32 -150.5t93 -62.5q72 0 123 63.5t69.5 140t18.5 150.5q0 86 -62 110q-57 22 -166 21h-20l174 168q104 0 194.5 -77t90.5 -218 q0 -215 -125 -383t-326 -168q-86 0 -147.5 38t-93 101.5t-46 131t-14.5 143.5z" /> +<glyph unicode="7" d="M84 -250q193 219 350.5 505t204.5 548h-473l31 201h682l-25 -168q-43 -285 -229.5 -636.5t-395.5 -582.5z" /> +<glyph unicode="8" d="M66 287q0 119 72.5 222t183.5 165q-129 94 -129 231q0 162 114.5 273.5t278.5 111.5q137 0 219 -69q84 -70 84 -191q0 -203 -244 -352q88 -39 141.5 -111.5t53.5 -167.5q0 -49 -19 -114q-43 -145 -162.5 -231.5t-273.5 -86.5q-139 0 -229 90.5t-90 229.5zM283 315 q0 -68 36.5 -114.5t100.5 -46.5q80 0 139 76.5t59 158.5q0 39 -16 69.5t-29.5 43t-56.5 40t-53 33.5q-180 -98 -180 -260zM399 907q0 -72 93 -129q18 8 55 29q53 27 96 85t43 112q0 104 -121 104q-72 0 -119 -63.5t-47 -137.5z" /> +<glyph unicode="9" d="M74 -176q166 100 227 149q162 123 256 312q35 70 59.5 169t24.5 175q0 213 -127 213q-68 0 -118 -63.5t-71.5 -141.5t-21.5 -150q0 -86 62 -109q57 -22 166 -22h20l-174 -168q-104 0 -194.5 77t-90.5 218q0 215 124 383t327 168q115 0 186.5 -68.5t94 -154.5t22.5 -190 q0 -410 -348 -719q-137 -121 -359 -224z" /> +<glyph unicode=":" horiz-adv-x="528" d="M74 104.5q0 61.5 50 105.5t114 44q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5zM158 682q0 61 50 105t114 44q59 0 99 -36.5t40 -96.5q0 -61 -50.5 -106t-113.5 -45q-59 0 -99 38t-40 97z" /> +<glyph unicode=";" horiz-adv-x="528" d="M-55 -201q106 129 141 211q18 41 25 133l25 11q15 6 49 15t51 9q37 -2 70.5 -31.5t47.5 -56.5l15 -29l-70 -110q-45 -72 -262 -246zM158 682q0 61 50 105t114 44q59 0 99 -36.5t40 -96.5q0 -61 -50.5 -106t-113.5 -45q-59 0 -99 38t-40 97z" /> +<glyph unicode="<" horiz-adv-x="614" d="M53 512l371 397l137 -116l-266 -277l268 -285l-135 -116z" /> +<glyph unicode="=" horiz-adv-x="983" d="M90 293v172h782v-172h-782zM90 549v172h782v-172h-782z" /> +<glyph unicode=">" horiz-adv-x="614" d="M51 231l268 285l-266 277l137 116l371 -397l-375 -397z" /> +<glyph unicode="?" horiz-adv-x="813" d="M92 104.5q0 61.5 50.5 105.5t113.5 44q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5zM193 1362l122 131q51 -70 105 -70h160q82 0 136 -31.5t71.5 -76.5t25.5 -95q3 -20 3 -38q0 -26 -7 -47q-8 -43 -40 -99.5t-55.5 -86t-83 -97.5t-65.5 -74 q-94 -111 -119.5 -158t-37.5 -126l-17 -113h-184l12 113q18 172 88 256l185 231l33 41l39.5 48t26.5 46q14 29 13 62q0 59 -51 59h-152q-113 0 -202 117z" /> +<glyph unicode="@" horiz-adv-x="1622" d="M92 727q0 297 221.5 525.5t515.5 228.5q326 0 510.5 -175.5t184.5 -476.5q0 -55 -13 -110q-33 -168 -165 -294t-295 -126h-6q-64 0 -114 41q-52 43 -54 104l-53 -35q-32 -22 -105 -56q-62 -29 -106 -28q-8 0 -15 1q-113 14 -149 171q-16 67 -16 132q0 87 28 170 q49 166 131 253t244 87q35 0 77.5 -21.5t69.5 -44.5l25 -20l14 57h172l-141 -575q-9 -22 -9 -44q0 -12 3 -24q8 -33 38 -33q100 0 170 105.5t82 199.5q6 43 6 90q0 227 -139 353.5t-377 126.5q-225 0 -382.5 -178.5t-157.5 -407.5q0 -242 159.5 -407.5t403.5 -178.5 q18 -1 35 -1q283 0 497 218l109 -98q-252 -287 -636 -287h-5q-319 4 -538.5 222.5t-219.5 535.5zM641 758q-26 -104 -26 -168q0 -97 62 -97q11 0 23 3q27 6 76 28.5t84 40.5l35 19l82 340q-98 59 -158 59q-123 0 -178 -225z" /> +<glyph unicode="A" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM442 567h209l-47 398z" /> +<glyph unicode="B" horiz-adv-x="1017" d="M84 29l192 1261h322q160 0 258 -66.5t98 -215.5q0 -109 -63.5 -193t-145.5 -121q188 -47 189 -252q0 -29 -6 -59q-31 -193 -174.5 -303.5t-341.5 -110.5q-90 1 -328 60zM330 184q68 -18 98 -18q123 0 202 76.5t79 199.5q0 45 -18.5 74t-55.5 41t-66.5 15t-76.5 3h-99z M424 774h90h6q88 0 150 59q63 60 63 150q0 72 -32.5 97.5t-120.5 25.5h-107z" /> +<glyph unicode="C" horiz-adv-x="1007" d="M94 481q0 70 15 170q18 115 68 230.5t125 215t181.5 162t223.5 62.5q209 0 319 -90l6 -4l-108 -156q-86 55 -236 55q-68 0 -134.5 -56t-109.5 -124q-57 -88 -91 -219t-34 -244q0 -127 49.5 -223t161.5 -96q117 0 254 82l86 -156q-70 -55 -172 -88t-194 -33 q-215 0 -312.5 140.5t-97.5 371.5z" /> +<glyph unicode="D" horiz-adv-x="1126" d="M82 0l201 1290h374q231 0 328 -174q65 -117 65 -293q0 -86 -16 -186q-49 -309 -210.5 -473t-372.5 -164h-369zM338 201h131q100 0 174 65.5t111 169t53 201.5t16 190q0 262 -192 263h-154z" /> +<glyph unicode="E" horiz-adv-x="911" d="M82 0l199 1290h675l-28 -198h-451l-49 -318h332l-31 -205h-334l-57 -366h469l-31 -203h-694z" /> +<glyph unicode="F" horiz-adv-x="845" d="M82 0l197 1290h620l-27 -196h-397l-51 -322h317l-28 -203h-318l-90 -569h-223z" /> +<glyph unicode="G" horiz-adv-x="1060" d="M94 481q0 139 43 285.5t119 270.5t194.5 204t256.5 80q98 0 180 -23.5t114 -48.5l33 -22l-110 -156q-88 55 -236 55q-63 0 -133 -57t-111 -123q-57 -88 -91 -219t-34 -244q0 -78 16.5 -144.5t68 -119.5t128.5 -53l136 16l65 408h215l-88 -563q-186 -57 -352 -58 q-215 0 -314.5 140.5t-99.5 371.5z" /> +<glyph unicode="H" horiz-adv-x="1120" d="M84 0l201 1290h225l-80 -518h350l84 518h228l-201 -1290h-229l92 571h-353l-90 -571h-227z" /> +<glyph unicode="I" horiz-adv-x="485" d="M61 0l189 1290h225l-190 -1290h-224z" /> +<glyph unicode="J" horiz-adv-x="487" d="M-119 -170l51 20q115 43 129 136l189 1304h225l-194 -1327q-16 -117 -93 -182q-100 -88 -278 -117z" /> +<glyph unicode="K" horiz-adv-x="931" d="M78 0l198 1290h228l-70 -475l170 170q103 103 103 227q0 26 -5 54l213 41l7 -24q4 -15 7 -59q1 -15 1 -29q0 -29 -4 -57q-6 -42 -32 -96.5t-69 -97.5l-315 -313l401 -537v-94h-215l-321 449l-68 -449h-229z" /> +<glyph unicode="L" horiz-adv-x="761" d="M86 0l205 1290h221l-174 -1093h389l-27 -197h-614z" /> +<glyph unicode="M" horiz-adv-x="1517" d="M82 0l205 1290h270l178 -831q12 -53 13 -148q18 88 51 154l418 825h274l-205 -1290h-233l100 635l41 225q-74 -213 -98 -264l-295 -596h-191l-139 641l-22 219l-33 -215l-101 -645h-233z" /> +<glyph unicode="N" horiz-adv-x="1132" d="M82 0l201 1290h262l196 -702l29 -156q0 164 8 219l99 639h229l-201 -1290h-217l-250 899q-2 -86 -14 -166l-115 -733h-227z" /> +<glyph unicode="O" horiz-adv-x="1120" d="M119 483q0 98 20.5 208t66.5 222.5t109.5 202.5t158.5 147.5t206 57.5q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -98 -20 -207.5t-66.5 -223.5t-111 -204t-159.5 -147.5t-206 -57.5q-100 0 -174 47t-112.5 125t-57 163t-18.5 179zM338 430q0 -256 164 -256 q88 0 155.5 78t101.5 194.5t49 220t15 185.5q0 264 -164 264q-88 0 -154.5 -80t-100 -197.5t-50 -223t-16.5 -185.5z" /> +<glyph unicode="P" horiz-adv-x="991" d="M82 0l199 1290h288q238 0 341 -121q82 -96 83 -227q0 -34 -6 -70q-29 -193 -162 -315.5t-321 -122.5q-29 0 -58.5 7.5t-46.5 13.5l-16 8l-76 -463h-225zM410 655q51 -23 98 -22q98 0 166.5 70.5t89.5 183.5q5 29 5 53q0 61 -32 101q-45 56 -186 57h-72z" /> +<glyph unicode="Q" horiz-adv-x="1071" d="M94 483q0 125 33 263.5t96.5 270.5t176 218t253.5 86q102 0 178 -49t114 -130t55.5 -165t17.5 -172q0 -330 -143.5 -569.5t-419.5 -368.5l63 -35q54 -28 137 -28q122 0 306 61l-48 -248l-30 -6q-20 -4 -81 -9q-38 -3 -75 -4q-22 0 -44 2q-58 3 -135 24q-76 20 -142 57 l-205 116l-25 115l215 98q-147 51 -222 179t-75 294zM315 475q0 -272 238 -315q242 207 242 665q0 289 -160 289q-88 0 -154.5 -69.5t-100.5 -176t-49.5 -207t-15.5 -186.5z" /> +<glyph unicode="R" horiz-adv-x="1017" d="M82 0l199 1290h288q182 0 303 -80t121 -264q0 -166 -95 -303t-253 -166l279 -383l-15 -94h-198l-324 449l-8 4l-72 -453h-225zM410 659q55 -25 90 -24q113 0 189.5 86t76.5 205q0 164 -217 164h-72z" /> +<glyph unicode="S" horiz-adv-x="878" d="M37 92l121 164q96 -84 202 -84q78 0 137.5 54.5t59.5 129.5q0 84 -80 144l-219 166q-124 91 -124 240q0 25 3 52q18 160 134 261.5t288 101.5q152 0 283 -137l12 -13l-137 -114q-63 63 -172 63q-74 0 -126 -46t-52 -120q0 -76 79 -135l168 -121q164 -119 164 -292 q0 -186 -126 -311.5t-312 -125.5q-80 0 -155.5 31t-110.5 61z" /> +<glyph unicode="T" horiz-adv-x="800" d="M92 1075l33 211h784l-32 -211h-279l-164 -1075h-223l164 1075h-283z" /> +<glyph unicode="U" horiz-adv-x="1114" d="M126 317q-4 35 -4 70q0 64 13 129l119 774h227l-123 -788q-10 -104 -10 -163q0 -67 33 -115q35 -50 111 -50q193 0 245 342l121 774h227l-122 -794q-23 -147 -79.5 -261t-163 -190t-249.5 -76q-113 0 -190.5 48.5t-110.5 125t-44 174.5z" /> +<glyph unicode="V" horiz-adv-x="991" d="M115 1290h239l135 -954l224 440q57 113 57 275v239h231v-237q0 -201 -81 -357l-371 -696h-225z" /> +<glyph unicode="W" horiz-adv-x="1517" d="M158 1290h241l35 -721l-2 -190q33 147 53 201l281 710h217l45 -743l4 -168q37 96 68 162l114 231q86 176 86 301q-2 119 -2 217h232v-237q0 -135 -105 -353l-348 -700h-215l-47 768q-1 13 -1 31q0 55 9 159q-16 -88 -41 -157l-50 -134l-98 -258l-110 -292 q-43 -117 -41 -117h-227z" /> +<glyph unicode="X" horiz-adv-x="1013" d="M10 0v94l416 572l-268 624h248l182 -420l293 420h184v-92l-385 -534l289 -664h-258l-191 453l-319 -453h-191z" /> +<glyph unicode="Y" horiz-adv-x="880" d="M55 1165l154 154q129 -82 209 -246q49 -104 61 -262q35 154 138.5 304.5t232.5 217.5l143 -151q-61 -39 -113.5 -87t-94.5 -114t-73.5 -120t-60.5 -141l-46 -138q-17 -51 -38 -154l-28 -143q-8 -41 -26 -150l-21 -135h-224l74 471q-14 127 -20 164q-20 164 -86 314 t-181 216z" /> +<glyph unicode="Z" horiz-adv-x="909" d="M49 180l574 901h-390l19 209h670l-21 -184l-577 -895h546l-30 -211h-789z" /> +<glyph unicode="[" horiz-adv-x="589" d="M12 -319l264 1818h355l-29 -192h-168l-209 -1434h168l-28 -192h-353z" /> +<glyph unicode="\" horiz-adv-x="686" d="M12 1550h189l493 -1853h-188z" /> +<glyph unicode="]" horiz-adv-x="595" d="M-14 -319l28 192h168l209 1434h-168l27 192h354l-266 -1818h-352z" /> +<glyph unicode="^" horiz-adv-x="1277" d="M119 655l442 795h152q190 -365 444 -795h-221l-299 531l-299 -531h-219z" /> +<glyph unicode="_" d="M0 -154h922v-122h-922v122z" /> +<glyph unicode="`" horiz-adv-x="518" d="M88 1403l180 156q90 -154 264 -295l-108 -125q-195 112 -336 264z" /> +<glyph unicode="a" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46z" /> +<glyph unicode="b" horiz-adv-x="1038" d="M102 109l201 1314h217l-76 -493q10 12 29 29.5t73 46t105 28.5q152 0 224.5 -101.5t72.5 -273.5q0 -119 -33.5 -238.5t-97 -221t-166 -166t-227.5 -64.5q-74 0 -154 35t-123 70zM317 197q45 -41 129 -41q74 0 134.5 77.5t88 160.5t40.5 157q10 72 10 125q0 166 -109 166 q-98 0 -207 -125z" /> +<glyph unicode="c" horiz-adv-x="849" d="M92 381q0 133 45 265t119 218q72 82 162 126t160 44q33 0 61.5 -4t50 -9t43 -14.5t33.5 -16.5t26.5 -18t19.5 -17l15 -15l11 -10l-129 -137q-41 57 -148 57q-72 0 -145.5 -94t-98.5 -250q-8 -70 -8 -98q0 -111 40 -180.5t110 -69.5q43 0 99 31.5t93 64.5l35 33l100 -123 q-6 -8 -17 -21.5t-48 -46.5t-76 -58.5t-95 -47t-110 -21.5q-180 0 -276 140q-72 102 -72 272z" /> +<glyph unicode="d" horiz-adv-x="1024" d="M92 395q0 246 141.5 442.5t335.5 196.5q96 0 179 -73q25 143 24 219q0 86 -45 186l170 80q90 -133 90 -293q0 -35 -6 -70l-109 -684q-29 -184 -133 -307t-280 -123q-180 0 -273.5 121t-93.5 305zM313 383q0 -229 152 -229q37 0 67.5 15t52 49t35 59.5t24.5 79l14.5 70.5 t11.5 69l45 276q-84 70 -144 70q-115 0 -186.5 -147.5t-71.5 -311.5z" /> +<glyph unicode="e" horiz-adv-x="894" d="M92 373q0 156 54.5 303t174 252.5t279.5 105.5q262 0 262 -246q0 -86 -56 -156.5t-148.5 -115.5t-175.5 -71.5t-169 -43.5v-28q0 -74 34 -148.5t97 -74.5q59 0 120 25.5t91 49.5l31 25l92 -123q-16 -18 -47 -44t-126 -70t-193 -44q-160 0 -240 119t-80 285zM338 586 q307 72 307 215q0 55 -65 55q-86 0 -148.5 -85t-93.5 -185z" /> +<glyph unicode="f" horiz-adv-x="550" d="M16 -299q111 205 138 524l45 602l-121 21l24 156h115q0 72 10.5 137t34 132.5t73.5 108.5t120 41q33 0 64.5 -5t45.5 -11l17 -6l-25 -168h-63q-14 0 -25.5 -9.5t-18 -27.5t-11.5 -35.5t-7 -44t-3 -43t-1 -40.5v-29h137v-181h-151l-62 -714q-10 -129 -63 -239t-148 -249z " /> +<glyph unicode="g" horiz-adv-x="907" d="M-33 -188q0 90 79 186t159 147l78 52l88 -123l-52 -46q-31 -28 -82 -89.5t-51 -94.5q0 -61 109 -61q102 0 198.5 61.5t96.5 141.5q0 59 -34 96t-103 37l-11 76q49 37 65.5 60.5t37.5 84.5q-113 -63 -178 -64q-133 0 -199 86.5t-66 221.5q0 201 106.5 325.5t305.5 124.5 l315 -35q-12 -205 -52 -432t-109 -342q63 -20 100 -85.5t37 -137.5q0 -98 -52.5 -177t-132 -125t-165.5 -70.5t-164 -24.5q-324 0 -324 207zM317 594q0 -117 93 -117q70 0 117.5 63.5t64.5 143.5q14 66 22 162q-61 12 -127 12q-84 0 -127 -76.5t-43 -187.5z" /> +<glyph unicode="h" horiz-adv-x="1042" d="M84 0l217 1423h219l-92 -581q4 8 13.5 20l38 46t60 59.5t80 46t97.5 20.5q119 0 175 -42t57 -109v-13q0 -63 -9 -139l-66 -422q-18 -121 -52 -184.5t-124 -155.5l-131 109q49 63 64.5 109t32.5 147l65 409q3 20 3 36q0 29 -10 40q-15 18 -60 19q-57 0 -143.5 -82 t-127.5 -160l-90 -596h-217z" /> +<glyph unicode="i" horiz-adv-x="477" d="M66 0l151 1004h217l-153 -1004h-215zM213 1251q0 78 74 121q33 20 73 21q55 0 95.5 -38t40.5 -93q0 -25 -11 -50q-23 -57 -77 -79q-27 -12 -58 -13q-78 0 -119 66q-18 32 -18 65z" /> +<glyph unicode="j" horiz-adv-x="477" d="M-119 -240l56 25q41 18 74.5 58t39.5 85l166 1076h215l-160 -1057q-20 -143 -90.5 -213t-181.5 -107l-96 -33zM213 1251q0 78 74 121q33 20 73 21q55 0 95.5 -38t40.5 -93q0 -25 -11 -50q-23 -57 -77 -79q-27 -12 -58 -13q-78 0 -119 66q-18 32 -18 65z" /> +<glyph unicode="k" horiz-adv-x="980" d="M84 0l217 1423h215l-121 -788q6 2 24.5 3t38 4t36.5 9q92 27 161.5 71t69.5 99q0 33 -19.5 44.5t-76.5 11.5l-17 141q76 16 148 16q96 0 143 -55t47 -137q0 -141 -111.5 -231.5t-248.5 -90.5l362 -520h-278l-305 465l-72 -465h-213z" /> +<glyph unicode="l" horiz-adv-x="493" d="M123 426l143 997h217l-145 -1009q-11 -86 -11 -155q0 -24 2 -46q5 -84 17 -131l12 -47l-206 -58q-41 124 -41 276q0 82 12 173z" /> +<glyph unicode="m" horiz-adv-x="1576" d="M86 0l158 1004h223l-31 -162q117 193 303 192q211 0 211 -178q123 178 291 178q143 0 187 -76q29 -50 30 -130q0 -41 -8 -91l-113 -737h-219l119 750q2 15 2 27q0 65 -63 65q-94 0 -240 -191l-12 -18l-101 -633h-217l121 750q6 22 6 39q0 16 -5 27q-11 24 -62.5 24 t-136.5 -82t-128 -150l-96 -608h-219z" /> +<glyph unicode="n" horiz-adv-x="1060" d="M84 0l158 1004h217l-29 -162q49 80 126 136t161 56q113 0 170 -43t60 -109q1 -18 1 -37q0 -51 -6 -108l-115 -737h-219l119 750q3 21 3 36q0 27 -9 38q-14 17 -57 18q-111 0 -271 -244l-92 -598h-217z" /> +<glyph unicode="o" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM305 365q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209q-68 0 -120 -53.5 t-79.5 -133t-41 -155.5t-13.5 -137z" /> +<glyph unicode="p" horiz-adv-x="1095" d="M70 -358l147 942l-137 -78l31 192l137 86l33 220h219l-11 -74q143 104 220 104q96 0 164.5 -46t96 -118.5t39.5 -159.5q5 -35 4 -70q0 -50 -9 -99q-14 -102 -45 -195.5t-83.5 -182.5t-137.5 -141.5t-191 -52.5q-133 0 -189 109l-69 -436h-219zM413 350q1 -43 10 -75.5 t25.5 -60.5t44 -42t66.5 -14q158 0 221 374q11 75 11 132q0 178 -107 178q-61 0 -225 -119q-2 -14 -10.5 -54t-13.5 -72l-12 -79q-7 -47 -9 -86l-2 -54q1 -14 1 -28z" /> +<glyph unicode="q" horiz-adv-x="1058" d="M94 367q0 143 57.5 293.5t175 262t261.5 111.5q170 0 383 -65l-203 -1327h-219l69 436q-33 -45 -94 -77t-116 -32q-168 0 -241 111t-73 287zM317 362q0 -199 119 -198q78 0 136.5 72.5t86 165t41.5 206.5l31 217q-70 16 -106 17q-88 0 -151.5 -46t-95.5 -122t-46.5 -153 t-14.5 -159z" /> +<glyph unicode="r" horiz-adv-x="710" d="M88 0l154 1004h215l-23 -152l21 35l29 44l29 36q22 27 37 37t38.5 20t48.5 10q86 0 135 -116l-143 -179q-2 4 -6.5 11.5t-18.5 17.5t-31 8q-66 -4 -153 -145l-29 -45l-90 -586h-213z" /> +<glyph unicode="s" horiz-adv-x="843" d="M45 92l113 154q113 -88 200 -88q145 0 162 110q2 8 2 16q0 54 -76 93l-204 115q-61 35 -95 103q-24 50 -23 112q0 23 3 49q23 135 131.5 206.5t251.5 71.5q53 0 103.5 -15t85 -35.5t61 -42t39.5 -36.5l12 -16l-123 -115q-74 76 -201 76q-66 0 -106.5 -35t-40.5 -76 q0 -20 14.5 -38.5t28.5 -27.5t45 -26l180 -96q127 -68 128 -218q0 -43 -11 -93q-27 -115 -128 -193t-265 -78q-66 0 -137.5 31t-110.5 61z" /> +<glyph unicode="t" horiz-adv-x="688" d="M127 840l25 164h124l23 178h197l-25 -178h174l-31 -181h-165l-91 -602q0 -2 -1 -7t-1 -9q0 -49 35 -49q68 0 154 71l63 -147q-125 -111 -258 -111q-201 0 -200 199q0 18 4 59l88 596z" /> +<glyph unicode="u" horiz-adv-x="1038" d="M108 156q-1 11 -1 21q0 43 10 89l114 738h218l-117 -750q-3 -21 -3 -36q0 -27 9 -39q14 -17 57 -17q104 0 271 244l92 598h217l-131 -836q-4 -31 8 -50.5t29 -23.5l14 -4l-78 -121q-10 -1 -22 -1h-14q-22 1 -67.5 15.5t-64.5 47.5q-15 25 -15 67q0 29 7 66 q-53 -78 -135 -136.5t-164 -58.5q-86 0 -140.5 25.5t-72.5 67t-21 94.5z" /> +<glyph unicode="v" horiz-adv-x="866" d="M68 1004h227l115 -719l206 358q28 45 28 119q0 77 -13 135q-12 53 -27 84l207 43l11 -29q7 -18 18.5 -74.5t13.5 -111.5v-13q0 -51 -14 -115q-16 -73 -54 -136l-325 -545h-193z" /> +<glyph unicode="w" horiz-adv-x="1355" d="M76 1004h229l72 -711l262 711h213l68 -711l145 282q37 72 54 124q16 49 17 117q0 78 -32 163l207 45q4 -10 9 -28.5t12 -75t7 -111.5t-17 -130t-54 -138l-307 -541h-203l-58 682q-25 -121 -51 -184l-211 -498h-213z" /> +<glyph unicode="x" horiz-adv-x="864" d="M-14 0v86l338 432l-222 486h238l133 -336l94 96q20 23 31.5 38t24 42.5t10.5 61.5t-19 77l203 43q2 -8 7 -22.5t12 -56.5q4 -28 4 -54q0 -13 -1 -27q-3 -39 -21.5 -87t-55.5 -85l-205 -202l238 -492h-244l-141 334l-248 -334h-176z" /> +<glyph unicode="y" horiz-adv-x="880" d="M-18 -225q106 45 173.5 100t120.5 150l-200 979h229l123 -728l61 109q168 287 168 358q0 113 -43 236l211 45q41 -113 41 -246q0 -121 -67 -237l-336 -584q-78 -137 -177.5 -215t-252.5 -125z" /> +<glyph unicode="z" horiz-adv-x="843" d="M18 0l13 184q248 348 499 627h-430l25 193h709l-21 -173q-281 -301 -522 -632h483l-22 -199h-734z" /> +<glyph unicode="{" horiz-adv-x="720" d="M68 504l24 170q59 0 103.5 44t67 120.5t33.5 141t18.5 146.5t9.5 95q23 168 90 228t159 60h144l6 -184h-143q-39 0 -52.5 -26.5t-25.5 -116.5l-51 -346q-23 -154 -148 -254q77 -80 77 -194q0 -29 -5 -60l-49 -346q-10 -53 -10 -82q0 -12 1 -21q6 -27 45 -26h148l-61 -185 h-130q-188 0 -187 208q0 42 7 91l49 344q3 24 4 46q0 60 -26 97q-35 50 -98 50z" /> +<glyph unicode="|" horiz-adv-x="454" d="M135 -29v1569h184v-1569h-184z" /> +<glyph unicode="}" horiz-adv-x="630" d="M-47 -147h133q29 0 41 5t24.5 35.5t22.5 100.5l51 346q20 145 146 250q-76 84 -76 200q0 28 4 58l51 346q10 66 10 96q0 19 -4 25q-8 10 -30 10h-160l61 184h133q184 0 184 -207q0 -42 -7 -94l-50 -344q-4 -26 -4 -49q0 -57 26 -92q36 -49 99 -49l-24 -170 q-61 0 -114.5 -56.5t-66.5 -148.5l-51 -344q-25 -170 -93.5 -228.5t-160.5 -58.5h-139z" /> +<glyph unicode="~" horiz-adv-x="1140" d="M254 387l47 256q10 10 34.5 37t35 35t32 21.5t48 17.5t65.5 4q78 0 193.5 -53.5t169.5 -53.5q47 0 93 40t144 175l-57 -284q-12 -10 -32.5 -30t-29 -27t-25 -18t-31.5 -14.5t-38.5 -6.5t-56.5 -3q-76 0 -192.5 53.5t-170.5 53.5q-47 0 -90 -37t-139 -166z" /> +<glyph unicode="¡" horiz-adv-x="514" d="M8 -428q100 252 135 600q18 219 45 453h185l-15 -310q-16 -326 -45 -462q-14 -76 -39 -154l-30 -102zM162 886.5q0 61.5 50 105.5t114 44q61 0 101 -36.5t40 -96.5q0 -61 -50 -106t-114 -45q-61 0 -101 36.5t-40 98z" /> +<glyph unicode="¢" horiz-adv-x="849" d="M92 381q0 133 45 265t119 218q117 135 274 166l21 139h123l-21 -141q25 -4 47.5 -10t40 -14.5t30.5 -15.5t24.5 -17t17.5 -17l14 -14l11 -10l-127 -135q-27 33 -86 47l-101 -678q106 25 164 102l100 -123q-14 -16 -39.5 -39.5t-102.5 -70.5t-150 -60l-19 -123h-123 l19 125q-133 20 -209 134q-72 102 -72 272zM309 408q0 -180 92 -236l99 659q-61 -35 -112.5 -118.5t-70.5 -206.5q-8 -69 -8 -98z" /> +<glyph unicode="£" horiz-adv-x="905" d="M25 745l59 148h78v252q0 141 105.5 207.5t240.5 66.5q150 0 242 -78l-56 -149q-86 51 -174 51q-143 0 -143 -127v-223h260v-184h-260v-195q0 -154 -37 -328h100l90 -30q38 -12 73 -13q96 0 167 97l18 28l43 -172q-2 -4 -7 -10l-20 -25q-15 -18 -32.5 -31.5t-46.5 -31.5 t-58.5 -25.5t-68.5 -11.5q-9 -1 -17 -1l-61 11l-106 29h-332q80 352 80 524v195z" /> +<glyph unicode="¤" horiz-adv-x="1110" d="M-31 463l107 172h110q0 14 9 55q0 6 3 28.5t5 29.5h-180l110 176h121q68 217 238 353t401 136q195 0 321 -141l-102 -168q-55 70 -111.5 98.5t-148.5 28.5q-268 0 -385 -307h532l-106 -174h-477q-18 -55 -23 -115h428l-108 -174h-320q2 -156 74 -229.5t197 -73.5 q156 0 348 192l-37 -241q-164 -133 -352 -134q-203 0 -325 130q-116 124 -116 336v22h-213z" /> +<glyph unicode="¥" horiz-adv-x="880" d="M35 293l31 190h274l-16 131h-261l31 191h193q-72 268 -232 360l154 154q129 -82 209 -246q47 -100 61 -280q27 154 133.5 313.5t237.5 226.5l143 -151q-195 -123 -307 -377h211l-31 -191h-252q-25 -86 -34 -131h288l-30 -190h-297q-10 -53 -25.5 -151.5t-23.5 -141.5 h-224l47 293h-280z" /> +<glyph unicode="§" horiz-adv-x="860" d="M25 -158l77 160q160 -100 306 -100q72 0 111.5 33.5t39.5 91.5q0 43 -40 77.5t-150 88.5q-10 4 -56.5 26.5t-61.5 30.5t-54 30.5t-55 41.5l-38 45q-23 27 -30.5 57.5t-7.5 67.5q0 98 62 172.5t163 105.5q-217 96 -217 242q0 141 100 225t256 84q158 0 326 -94l-84 -160 q-150 74 -246 74q-66 0 -102.5 -35t-36.5 -90q0 -10 4 -20.5t12 -19.5t15.5 -16.5t21.5 -15.5t20.5 -12t22.5 -11.5t18 -9.5l63 -30l73 -37l67 -38q45 -26 68.5 -48.5t50.5 -52t39 -62.5t12 -70q0 -96 -62.5 -179t-154.5 -113q59 -33 97 -57.5t75 -72t37 -98.5 q0 -154 -100.5 -245t-266.5 -91q-167 0 -374 125zM276 514q0 -41 32 -74.5t85 -33.5q84 0 134.5 36.5t50.5 92.5q0 43 -38 73.5t-94 30.5q-76 0 -123 -35t-47 -90z" /> +<glyph unicode="¨" horiz-adv-x="638" d="M76 1276q0 57 43 103t100 46q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75zM422 1276q0 57 43 103t100 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -75.5 30t-30.5 75z" /> +<glyph unicode="©" horiz-adv-x="1800" d="M246 502q0 174 88 327.5t241 241.5q150 86 328 86q176 0 330 -86q154 -90 240 -242q88 -147 88 -327q0 -186 -91 -334q-88 -152 -243 -240q-154 -86 -324 -86q-174 0 -323 86q-154 88 -242 238q-92 152 -92 336zM410 502q0 -137 67 -254q66 -113 179.5 -177.5 t246.5 -66.5q205 0 346 141q150 145 150 357q0 131 -66 246q-88 154 -260 219q-92 31 -170 30q-131 0 -248 -67q-115 -68 -180 -182q-65 -109 -65 -246zM600 514q0 154 86 255t229 101q113 -2 232 -100l-82 -108q-86 63 -147 63q-51 0 -100.5 -53.5t-49.5 -157.5 q0 -94 40 -163.5t120 -69.5q63 0 143 59l62 -117q-100 -88 -211 -88q-139 0 -230.5 96.5t-91.5 282.5z" /> +<glyph unicode="ª" horiz-adv-x="743" d="M158 969q0 82 24.5 179t70.5 189.5t126 153.5t176 61q70 0 227 -28l-77 -561q-7 -36 -8 -59q0 -28 16 -27l37 8l34 -107q-70 -35 -129 -35q-49 0 -78 37q-25 31 -25 66q0 7 1 14l6 45l-11 -17q-7 -11 -31.5 -39t-51.5 -49.5t-69 -39t-85 -17.5q-153 1 -153 226zM328 958 q0 -23 1 -33t10 -18t30 -8q49 0 118.5 90t81.5 158l43 252q-37 4 -55 4q-53 0 -99 -49t-73 -122t-42 -143.5t-15 -121.5v-9z" /> +<glyph unicode="«" horiz-adv-x="970" d="M37 500l457 393l131 -121l-330 -276l244 -285l-162 -113zM385 500l457 393l133 -121l-332 -276l246 -285l-162 -113z" /> +<glyph unicode="¬" horiz-adv-x="1095" d="M82 717v184h850v-555l-182 -35v406h-668z" /> +<glyph unicode="­" horiz-adv-x="618" d="M106 436v172h406v-172h-406z" /> +<glyph unicode="®" horiz-adv-x="1800" d="M242 500q0 174 88 327.5t241 241.5q150 86 328 86t328 -86q154 -88 241.5 -241.5t87.5 -327.5q0 -184 -90 -334q-92 -156 -243 -240q-150 -86 -324 -86t-324 86q-156 88 -241 240q-92 156 -92 334zM406 500q0 -135 65 -254q70 -117 182 -180q111 -61 246 -64h6 q127 0 242 66.5t180.5 177t65.5 254.5q0 131 -66 245q-41 76 -110 132q-59 55 -148 88q-92 31 -170 30q-131 0 -248 -67q-115 -68 -180 -182.5t-65 -245.5zM692 147v725h178q141 0 211 -65.5t70 -171.5q0 -76 -40 -142.5t-105 -86.5l176 -183v-76h-138l-182 193l-10 12v-205 h-160zM852 530q20 -8 41 -8q45 0 70.5 32t25.5 81q0 94 -119 94h-18v-199z" /> +<glyph unicode="¯" horiz-adv-x="630" d="M45 1174l29 120h565l-29 -120h-565z" /> +<glyph unicode="°" horiz-adv-x="638" d="M68 1044q18 115 113 197t210 82q119 0 192 -86q58 -69 58 -158q0 -23 -4 -47q-18 -115 -113.5 -196.5t-210.5 -81.5q-119 0 -191 86q-58 69 -58 158q0 22 4 46zM219 1032q-2 -11 -2 -22q0 -39 25 -68q31 -37 84 -37q57 0 103 41t56 98q2 11 2 22q0 39 -24 69 q-31 37 -84 36q-57 0 -103.5 -41t-56.5 -98z" /> +<glyph unicode="±" horiz-adv-x="915" d="M78 487v172h313v271h180v-271h316v-172h-316v-225h-180v225h-313zM98 0v170h768v-170h-768z" /> +<glyph unicode="²" d="M102 178q180 92 357 275q182 197 182 303q0 35 -24.5 58.5t-71.5 23.5q-94 0 -260 -97l-82 148q84 59 120.5 81.5t104.5 43t147 20.5q233 0 277 -182q8 -35 8 -71q0 -56 -19 -114q-32 -97 -99.5 -185.5t-135 -155t-143.5 -125.5h344l-31 -201h-661z" /> +<glyph unicode="³" d="M53 -182q135 12 250 51q274 102 275 338q0 47 -33 72.5t-66 29.5t-86 4q-98 0 -184 -10l-14 178q33 4 78.5 8.5t58.5 6.5l223 161q88 66 88 129q0 25 -17.5 40.5t-45.5 15.5q-41 0 -166 -54l-119 -51l-64 152l115 53q201 92 281 92q111 0 172 -57t61 -170 q0 -68 -49 -134.5t-98 -103.5t-127 -86q211 -31 211 -237q0 -172 -96.5 -307.5t-254.5 -204.5q-168 -72 -358 -72z" /> +<glyph unicode="´" horiz-adv-x="573" d="M92 1270q98 61 187.5 134t130.5 114l39 41l139 -164q-156 -131 -420 -256z" /> +<glyph unicode="µ" horiz-adv-x="1058" d="M57 -371q92 145 93 277q0 45 -9.5 127t-9.5 141v49v781h215v-791q0 -53 84 -53q51 0 97 16.5t68.5 30.5t78.5 57v740h215v-818q0 -39 22.5 -47t55.5 15l67 -134q-78 -51 -155 -51q-59 0 -116.5 27t-78.5 76q-141 -102 -242 -103q-51 0 -88 19q12 -90 13 -152 q0 -80 -50 -164z" /> +<glyph unicode="¶" horiz-adv-x="1132" d="M109 901q23 193 161 316q121 107 280 106q24 0 48 -2l264 -25q0 6 4 32l8 50h181q-4 -39 -13 -75l103 20l-39 -180l-107 -25q-90 -235 -90 -419q0 -92 23 -173q19 -88 19 -179q0 -76 -13 -154q-29 -174 -84 -267l-205 82q109 274 110 433q0 40 -7 73q-20 94 -21 192 q0 197 82 414l-205 19h-8q-73 0 -126 -103q-42 -81 -42 -164q0 -29 5 -58q19 -114 126 -118q10 -23 -92 -204q-33 -5 -63 -5q-130 0 -218 85q-87 85 -87 246q1 39 6 83z" /> +<glyph unicode="·" horiz-adv-x="559" d="M139 514q0 61 50.5 105.5t113.5 44.5q59 0 99 -37t40 -97q0 -61 -49 -106t-112 -45q-61 0 -101.5 37t-40.5 98z" /> +<glyph unicode="¸" horiz-adv-x="823" d="M346 -262q176 61 177 118q0 21 -16 25q-13 4 -31.5 -1t-36.5 -13l-32 -15q-15 -7 -18 -8l23 154h209q49 -39 63 -96q3 -13 3 -26q0 -60 -63 -122q-77 -76 -173 -116z" /> +<glyph unicode="¹" d="M231 791l25 167q133 0 260 43h164l-143 -1001h-199l115 791h-222z" /> +<glyph unicode="º" horiz-adv-x="788" d="M170 1065q0 172 103.5 328.5t265.5 156.5q127 0 189.5 -97t62.5 -230q0 -106 -41 -214t-129.5 -188t-198.5 -80q-125 0 -188.5 95.5t-63.5 228.5zM332 1040q0 -158 104 -157q51 0 91 40t60.5 101t31 118.5t10.5 104.5q0 158 -103 158q-68 0 -114.5 -70.5t-63 -148.5 t-16.5 -146z" /> +<glyph unicode="»" horiz-adv-x="1046" d="M63 219l332 277l-245 284l161 113l342 -401l-456 -394zM414 219l329 277l-243 284l162 113l339 -401l-456 -394z" /> +<glyph unicode="¼" horiz-adv-x="2844" d="M231 791l25 167q133 0 260 43h164l-143 -1001h-199l115 791h-222zM598 -268l1145 1794h213l-1145 -1794h-213zM1960 0l16 170l58 86q229 356 473 662l69 86h215l-127 -824h121l-32 -180h-125q-55 -242 -127 -352l-162 69q31 66 80 283h-459zM2216 180h238l73 475 q-61 -86 -311 -475z" /> +<glyph unicode="½" horiz-adv-x="2844" d="M231 791l25 167q133 0 260 43h164l-143 -1001h-199l115 791h-222zM598 -268l1145 1794h213l-1145 -1794h-213zM2025 178q180 92 357 275q182 197 182 303q0 35 -24.5 58.5t-71.5 23.5q-94 0 -260 -97l-82 148q84 59 121 81.5t104.5 43t147.5 20.5q233 0 276 -182 q8 -35 8 -71q0 -56 -19 -114q-32 -97 -99.5 -185.5t-135 -155t-143.5 -125.5h344l-31 -201h-661z" /> +<glyph unicode="¾" horiz-adv-x="2844" d="M53 -182q135 12 250 51q274 102 275 338q0 47 -33 72.5t-66 29.5t-86 4q-98 0 -184 -10l-14 178q33 4 78.5 8.5t58.5 6.5l223 161q88 66 88 129q0 25 -17.5 40.5t-45.5 15.5q-41 0 -166 -54l-119 -51l-64 152l115 53q201 92 281 92q111 0 172 -57t61 -170 q0 -68 -49 -134.5t-98 -103.5t-127 -86q211 -31 211 -237q0 -172 -96.5 -307.5t-254.5 -204.5q-168 -72 -358 -72zM598 -268l1145 1794h213l-1145 -1794h-213zM1960 0l16 170l58 86q229 356 473 662l69 86h215l-127 -824h121l-32 -180h-125q-55 -242 -127 -352l-162 69 q31 66 80 283h-459zM2216 180h238l73 475q-61 -86 -311 -475z" /> +<glyph unicode="¿" horiz-adv-x="829" d="M38 -195q3 37 5 66q8 43 40 99.5t55.5 86t83 97t65.5 73.5q94 111 119.5 158t37.5 127l17 113h184l-12 -113q-18 -168 -88 -256l-185 -231q-4 -4 -32.5 -39t-40 -50.5t-25.5 -45t-16 -54.5q0 -68 53 -67h152q113 0 202 -117l6 -8l-122 -131q-51 70 -107 69h-158 q-66 0 -114.5 21.5t-71.5 54.5t-37 72q-12 32 -12 62q0 6 1 13zM455 885q0 61 50 106t113 45q61 0 101.5 -36.5t40.5 -98.5q0 -59 -50.5 -104t-113.5 -45q-61 0 -101 37t-40 96z" /> +<glyph unicode="À" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM442 567h209l-47 398zM455 1669l180 156q90 -154 264 -295l-108 -125q-195 112 -336 264z" /> +<glyph unicode="Á" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM442 567h209l-47 398zM479 1536q98 61 187.5 134t128.5 114l41 41l139 -164q-184 -145 -420 -256z" /> +<glyph unicode="Â" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM352 1546q268 141 414 258q115 -145 264 -274l-135 -121q-117 109 -164 162q-156 -92 -282 -156zM442 567h209l-47 398z" /> +<glyph unicode="Ã" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM295 1587q123 100 229 109q11 1 22 1q69 0 148 -34q86 -31 120 -31q9 0 13 2q25 9 25 44q0 20 -8 48l178 -34q1 -11 1 -22q0 -64 -32 -114q-47 -76 -153 -81q-12 -1 -24 -1l-120 25q-8 4 -26.5 12.5 t-25.5 12.5t-21.5 9t-25.5 8t-26.5 5t-33.5 2q-61 0 -177 -86zM442 567h209l-47 398z" /> +<glyph unicode="Ä" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM397 1542q0 57 44 103.5t100 46.5q47 0 80.5 -33t33.5 -78q0 -57 -46 -100t-103 -43q-45 0 -77 29.5t-32 74.5zM442 567h209l-47 398zM743 1542q0 57 43 103.5t101 46.5q47 0 80.5 -33t33.5 -78 q0 -57 -46 -100t-103 -43q-45 0 -77 29.5t-32 74.5z" /> +<glyph unicode="Å" horiz-adv-x="1052" d="M-41 0l602 1290h195l200 -1290h-231l-53 371h-305l-166 -371h-242zM442 567h209l-47 398zM487 1616q14 88 86 149.5t160 61.5t144 -67q44 -53 43 -119q0 -17 -2 -35q-14 -88 -85 -150.5t-161 -62.5t-145 66q-43 53 -43 120q0 18 3 37zM629 1595q0 -23 17.5 -40t37.5 -17 q41 0 66.5 28.5t25.5 63.5q0 23 -17.5 39.5t-37.5 16.5q-41 0 -66.5 -27t-25.5 -64z" /> +<glyph unicode="Æ" horiz-adv-x="1476" d="M-41 0l619 1290h202l23 -260l41 260h672l-33 -207h-436l-52 -307h328l-33 -211h-327l-58 -356h459l-35 -209h-692l63 391h-301l-192 -391h-248zM492 596h200l-51 346z" /> +<glyph unicode="Ç" horiz-adv-x="1007" d="M94 481q0 70 15 170q18 115 68 230.5t125 215t181.5 162t223.5 62.5q209 0 319 -90l6 -4l-108 -156q-86 55 -236 55q-68 0 -134.5 -56t-109.5 -124q-57 -88 -91 -219t-34 -244q0 -127 49.5 -223t161.5 -96q117 0 254 82l86 -156q-111 -88 -274 -113q31 -33 41 -75 q3 -13 3 -26q0 -60 -63 -122q-77 -76 -174 -116l-102 100q174 61 174 118q0 21 -15 25q-13 4 -31.5 -1t-36.5 -13l-33 -15q-15 -7 -17 -8l23 146q-271 83 -271 491z" /> +<glyph unicode="È" horiz-adv-x="911" d="M82 0l199 1290h675l-28 -198h-451l-49 -318h332l-31 -205h-334l-57 -366h469l-31 -203h-694zM449 1669l180 156q88 -154 264 -295l-109 -125q-216 125 -335 264z" /> +<glyph unicode="É" horiz-adv-x="911" d="M82 0l199 1290h675l-28 -198h-451l-49 -318h332l-31 -205h-334l-57 -366h469l-31 -203h-694zM408 1536q98 61 187 134t130 114l39 41l139 -164q-184 -145 -420 -256z" /> +<glyph unicode="Ê" horiz-adv-x="911" d="M82 0l199 1290h675l-28 -198h-451l-49 -318h332l-31 -205h-334l-57 -366h469l-31 -203h-694zM311 1546q268 141 414 258q115 -145 262 -274l-133 -121q-117 109 -164 162q-156 -92 -282 -156z" /> +<glyph unicode="Ë" horiz-adv-x="911" d="M82 0l199 1290h675l-28 -198h-451l-49 -318h332l-31 -205h-334l-57 -366h469l-31 -203h-694zM375 1542q0 57 44 103.5t99 46.5q47 0 81 -33t34 -78q0 -57 -46 -100t-104 -43q-45 0 -76.5 29.5t-31.5 74.5zM721 1542q0 57 43 103.5t100 46.5q47 0 81 -33t34 -78 q0 -57 -46 -100t-104 -43q-47 0 -77.5 29.5t-30.5 74.5z" /> +<glyph unicode="Ì" horiz-adv-x="485" d="M63 0l189 1290h225l-190 -1290h-224zM129 1669l180 156q88 -154 264 -295l-108 -125q-217 125 -336 264z" /> +<glyph unicode="Í" horiz-adv-x="485" d="M61 0l177 1290h198l-178 -1290h-197zM176 1483q80 57 152.5 125.5t105.5 107.5l33 39l113 -153q-127 -127 -342 -242z" /> +<glyph unicode="Î" horiz-adv-x="485" d="M61 1546q266 141 414 258q113 -145 262 -274l-135 -121q-117 109 -164 162q-152 -90 -280 -156zM70 0l190 1290h223l-188 -1290h-225z" /> +<glyph unicode="Ï" horiz-adv-x="485" d="M63 0l189 1290h225l-190 -1290h-224zM96 1542q0 57 43 103.5t101 46.5q47 0 80.5 -33t33.5 -78q0 -57 -46 -100t-103 -43q-45 0 -77 29.5t-32 74.5zM442 1542q0 57 43 103.5t101 46.5q47 0 79.5 -33t32.5 -78q0 -57 -46 -100t-103 -43q-45 0 -76 29.5t-31 74.5z" /> +<glyph unicode="Ñ" horiz-adv-x="1132" d="M82 0l201 1290h262l196 -702l29 -156q0 164 8 219l99 639h229l-201 -1290h-217l-250 899q-2 -86 -14 -166l-115 -733h-227zM369 1587q123 100 229 109q11 1 22 1q69 0 148 -34q86 -31 119 -31q9 0 14 2q25 9 25 44q0 20 -8 48l178 -34q1 -12 1 -23q0 -67 -32 -113 q-47 -76 -154 -81q-12 -1 -23 -1q-58 0 -118 25q-10 4 -32 14l-30 15q-9 4 -26.5 9t-35 8t-38.5 3q-61 0 -176 -86z" /> +<glyph unicode="Ò" horiz-adv-x="1120" d="M119 483q0 125 34.5 265.5t100 271.5t177.5 216t249 85q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -125 -34.5 -265.5t-100 -271.5t-177 -217t-249.5 -86q-100 0 -173.5 47t-113.5 125t-58.5 163t-18.5 179zM338 430q0 -256 164 -256q88 0 155.5 78t101.5 194.5t49 220 t15 185.5q0 264 -164 264q-86 0 -153.5 -80t-101 -197.5t-50 -223t-16.5 -185.5zM483 1669l181 156q90 -154 264 -295l-109 -125q-195 112 -336 264z" /> +<glyph unicode="Ó" horiz-adv-x="1120" d="M119 483q0 125 34.5 265.5t100 271.5t177.5 216t249 85q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -125 -34.5 -265.5t-100 -271.5t-177 -217t-249.5 -86q-100 0 -173.5 47t-113.5 125t-58.5 163t-18.5 179zM338 430q0 -256 164 -256q88 0 155.5 78t101.5 194.5t49 220 t15 185.5q0 264 -164 264q-86 0 -153.5 -80t-101 -197.5t-50 -223t-16.5 -185.5zM504 1536q98 61 187 134t128 114l41 41l139 -164q-156 -131 -419 -256z" /> +<glyph unicode="Ô" horiz-adv-x="1120" d="M119 483q0 125 34.5 265.5t100 271.5t177.5 216t249 85q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -125 -34.5 -265.5t-100 -271.5t-177 -217t-249.5 -86q-100 0 -173.5 47t-113.5 125t-58.5 163t-18.5 179zM338 430q0 -256 164 -256q88 0 155.5 78t101.5 194.5t49 220 t15 185.5q0 264 -164 264q-86 0 -153.5 -80t-101 -197.5t-50 -223t-16.5 -185.5zM373 1546q266 141 413 258q115 -145 263 -274l-134 -121q-96 86 -165 162q-119 -74 -281 -156z" /> +<glyph unicode="Õ" horiz-adv-x="1120" d="M119 483q0 125 34.5 265.5t100 271.5t177.5 216t249 85q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -125 -34.5 -265.5t-100 -271.5t-177 -217t-249.5 -86q-100 0 -173.5 47t-113.5 125t-58.5 163t-18.5 179zM338 430q0 -256 164 -256q88 0 155.5 78t101.5 194.5t49 220 t15 185.5q0 264 -164 264q-86 0 -153.5 -80t-101 -197.5t-50 -223t-16.5 -185.5zM350 1587q123 100 230 109q10 1 21 1q68 0 151 -34q83 -31 116 -31q9 0 15 2q25 9 24 44q0 20 -8 48l178 -34q1 -12 1 -23q0 -67 -31 -113q-47 -76 -154 -81q-12 -1 -23 -1q-58 0 -118 25 q-10 4 -32 14l-31 15q-9 4 -26.5 9t-35 8t-37.5 3q-61 0 -176 -86z" /> +<glyph unicode="Ö" horiz-adv-x="1120" d="M119 483q0 125 34.5 265.5t100 271.5t177.5 216t249 85q131 0 217 -83t116.5 -191.5t30.5 -237.5q0 -125 -34.5 -265.5t-100 -271.5t-177 -217t-249.5 -86q-100 0 -173.5 47t-113.5 125t-58.5 163t-18.5 179zM338 430q0 -256 164 -256q88 0 155.5 78t101.5 194.5t49 220 t15 185.5q0 264 -164 264q-86 0 -153.5 -80t-101 -197.5t-50 -223t-16.5 -185.5zM438 1542q0 57 43 103.5t101 46.5q47 0 79.5 -33t32.5 -78q0 -57 -46 -100t-103 -43q-45 0 -76 29.5t-31 74.5zM784 1542q0 57 43 103.5t101 46.5q47 0 79.5 -33t32.5 -78q0 -57 -46 -100 t-103 -43q-45 0 -76 29.5t-31 74.5z" /> +<glyph unicode="Ø" horiz-adv-x="1120" d="M117 -299l153 344q-152 135 -151 438q0 125 34.5 265.5t100 271.5t177.5 216t249 85q82 0 141 -31l115 258h102l-137 -311q143 -137 143 -428q0 -125 -34.5 -265.5t-100 -271.5t-178.5 -217t-250 -86q-72 0 -131 27l-131 -295h-102zM338 430q0 -106 27 -168l370 836 q-35 18 -76 18q-86 0 -153.5 -79t-101 -196.5t-50 -224t-16.5 -186.5zM434 188q31 -14 68 -14q88 0 155.5 78t101.5 193.5t49 220t15 186.5q0 96 -22 160z" /> +<glyph unicode="Ù" horiz-adv-x="1114" d="M126 317q-4 35 -4 70q0 64 13 129l119 774h227l-123 -788q-10 -104 -10 -163q0 -67 33 -115q35 -50 111 -50q193 0 245 342l121 774h227l-122 -794q-23 -147 -79.5 -261t-163 -190t-249.5 -76q-113 0 -190.5 48.5t-110.5 125t-44 174.5zM492 1669l180 156 q88 -154 262 -295l-109 -125q-212 123 -333 264z" /> +<glyph unicode="Ú" horiz-adv-x="1114" d="M126 317q-4 35 -4 70q0 64 13 129l119 774h227l-123 -788q-10 -104 -10 -163q0 -67 33 -115q35 -50 111 -50q193 0 245 342l121 774h227l-122 -794q-23 -147 -79.5 -261t-163 -190t-249.5 -76q-113 0 -190.5 48.5t-110.5 125t-44 174.5zM469 1536q98 61 187 134t130 114 l39 41l140 -164q-184 -145 -420 -256z" /> +<glyph unicode="Û" horiz-adv-x="1114" d="M126 317q-4 35 -4 70q0 64 13 129l119 774h227l-123 -788q-10 -104 -10 -163q0 -67 33 -115q35 -50 111 -50q193 0 245 342l121 774h227l-122 -794q-23 -147 -79.5 -261t-163 -190t-249.5 -76q-113 0 -190.5 48.5t-110.5 125t-44 174.5zM371 1546q266 141 413 258 q113 -145 263 -274l-136 -121q-102 94 -163 162q-152 -90 -281 -156z" /> +<glyph unicode="Ü" horiz-adv-x="1114" d="M126 317q-4 35 -4 70q0 64 13 129l119 774h227l-123 -788q-10 -104 -10 -163q0 -67 33 -115q35 -50 111 -50q193 0 245 342l121 774h227l-122 -794q-23 -147 -79.5 -261t-163 -190t-249.5 -76q-113 0 -190.5 48.5t-110.5 125t-44 174.5zM412 1542q0 57 44 103.5t101 46.5 q47 0 80 -33t33 -78q0 -57 -46 -100t-104 -43q-45 0 -76.5 29.5t-31.5 74.5zM758 1542q0 57 44 103.5t101 46.5q45 0 79 -33t34 -78q0 -57 -46 -100t-104 -43q-45 0 -76.5 29.5t-31.5 74.5z" /> +<glyph unicode="Ý" horiz-adv-x="880" d="M55 1165l154 154q129 -82 209 -246q49 -104 61 -262q35 154 138.5 304.5t232.5 217.5l143 -151q-61 -39 -113.5 -87t-94.5 -114t-73.5 -120t-60.5 -141l-46 -138q-17 -51 -38 -154l-28 -143q-8 -41 -26 -150l-21 -135h-224l74 471q-14 127 -20 164q-20 164 -86 314 t-181 216zM799 1587q98 61 187 134t130 114l39 41l139 -164q-156 -131 -420 -256z" /> +<glyph unicode="ß" horiz-adv-x="1122" d="M2 -307q12 18 32.5 55t59.5 161t51 257q45 477 58 649l-135 33l26 156h125l19 135q20 162 172 244q119 65 244 66q34 0 69 -5q117 -18 191.5 -95t86.5 -206q2 -16 2 -33q0 -118 -75 -270q-86 18 -123 18q-57 0 -88 -33q-33 -35 -33 -78q0 -54 96 -110l183 -111 q111 -68 110 -217q-2 -160 -122 -250t-283 -90q-66 0 -129.5 25.5t-94.5 50.5l-30 25l106 161q70 -66 150 -65q68 0 114.5 27.5t55.5 68.5q1 7 1 14q0 56 -77 103l-182 108q-63 37 -98 117q-24 56 -23 112q0 24 4 48q23 133 121 201.5t207 68.5q13 45 12 81q0 47 -21 80 q-37 58 -137 58q-72 0 -130 -44t-64 -107l-25 -240l-78 -809q-14 -160 -203 -436l-8 -12z" /> +<glyph unicode="à" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46zM444 1403l181 156q90 -154 264 -295l-109 -125q-195 112 -336 264z" /> +<glyph unicode="á" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46zM473 1270q98 61 187.5 134t130.5 114l38 41l140 -164q-156 -131 -420 -256z" /> +<glyph unicode="â" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM317 1280q266 141 414 258q113 -145 262 -274l-135 -121q-117 109 -164 162q-152 -90 -280 -156zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46z" /> +<glyph unicode="ã" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM276 1321q123 100 230 109q10 1 21 0q68 0 151 -33q83 -31 116 -31q9 0 15 2q25 9 25 44q0 20 -9 48l179 -35q1 -14 1 -27q0 -62 -32 -108q-47 -76 -154 -82q-12 -1 -23 -1q-58 0 -118 26l-30 14q-26 12 -32 14.5t-25.5 9.5t-36.5 9t-38 2q-61 0 -176 -86z M322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46z" /> +<glyph unicode="ä" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46zM393 1276q0 57 43 103t101 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-47 0 -78 30t-31 75z M739 1276q0 57 43 103t101 46q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -76 30t-31 75z" /> +<glyph unicode="å" horiz-adv-x="1003" d="M98 266q0 111 32 238t92.5 248.5t165 201.5t233.5 80q104 0 299 -37l-103 -739q-9 -48 -9 -77q0 -35 19 -36l50 11l45 -140q-92 -47 -168 -47q-66 0 -106 49q-32 39 -32 84q0 11 2 23l9 57q-4 -8 -14.5 -22.5t-43 -51t-67.5 -65.5t-90.5 -51.5t-112.5 -22.5 q-201 0 -201 297zM322 218q0 -19 12 -31.5t41 -12.5q66 0 156 119t108 209l55 332q-47 4 -69 4q-72 0 -132.5 -64.5t-96.5 -160t-55 -187.5t-19 -162v-46zM494 1356q14 88 86.5 149.5t158.5 61.5q88 0 144 -67q44 -53 44 -120q0 -18 -3 -37q-14 -86 -85 -148t-161 -62 t-144 66q-43 53 -43 120q0 18 3 37zM635 1335q0 -23 17.5 -41t37.5 -18q41 0 66.5 29.5t25.5 64.5q0 23 -17 39t-38 16q-41 0 -66.5 -26.5t-25.5 -63.5z" /> +<glyph unicode="æ" horiz-adv-x="1361" d="M98 266q0 115 33 244t94.5 249t166 197.5t229.5 77.5q76 0 266 -41q88 41 184 41q262 0 262 -246q0 -86 -56 -157.5t-148.5 -115.5t-174.5 -70.5t-170 -43.5v-28q0 -74 34 -148.5t97 -74.5q61 0 129 28.5t113 71.5l92 -123q-66 -70 -168 -114t-198 -44q-193 0 -277 185 q-135 -184 -307 -185q-201 0 -201 297zM319 238q0 -33 11.5 -48.5t44.5 -15.5q45 0 98 54.5t90 111.5v33q0 268 146 467q-14 0 -43 -1t-41 -1q-76 0 -138.5 -70t-96.5 -170t-52.5 -195t-18.5 -165zM809 586q307 70 307 215q0 55 -65 55q-84 0 -146.5 -84t-95.5 -186z" /> +<glyph unicode="ç" horiz-adv-x="849" d="M92 381q0 133 45 265t119 218q72 82 162 126t160 44q33 0 61.5 -4t50 -9t43 -14.5t33.5 -16.5t26.5 -18t19.5 -17l15 -15l11 -10l-129 -137q-41 57 -148 57q-72 0 -145.5 -94t-98.5 -250q-8 -70 -8 -98q0 -111 40 -180.5t110 -69.5q43 0 99 31.5t93 64.5l35 33l100 -123 q-12 -16 -36.5 -42t-95 -77t-140.5 -68q35 -37 43 -75q3 -13 3 -26q0 -60 -63 -122q-77 -76 -173 -116l-103 100q174 61 175 118q0 21 -16 25q-13 4 -31.5 -1t-36.5 -13l-32 -15q-15 -7 -18 -8l23 154h10q-82 37 -131 111q-72 102 -72 272z" /> +<glyph unicode="è" horiz-adv-x="894" d="M92 373q0 156 54.5 303t174 252.5t279.5 105.5q262 0 262 -246q0 -86 -56 -156.5t-148.5 -115.5t-175.5 -71.5t-169 -43.5v-28q0 -74 34 -148.5t97 -74.5q59 0 120 25.5t91 49.5l31 25l92 -123q-16 -18 -47 -44t-126 -70t-193 -44q-160 0 -240 119t-80 285zM338 586 q307 72 307 215q0 55 -65 55q-86 0 -148.5 -85t-93.5 -185zM393 1403l180 156q90 -154 265 -295l-109 -125q-195 112 -336 264z" /> +<glyph unicode="é" horiz-adv-x="894" d="M92 373q0 156 54.5 303t174 252.5t279.5 105.5q262 0 262 -246q0 -86 -56 -156.5t-148.5 -115.5t-175.5 -71.5t-169 -43.5v-28q0 -74 34 -148.5t97 -74.5q59 0 120 25.5t91 49.5l31 25l92 -123q-16 -18 -47 -44t-126 -70t-193 -44q-160 0 -240 119t-80 285zM338 586 q307 72 307 215q0 55 -65 55q-86 0 -148.5 -85t-93.5 -185zM389 1270q98 61 187.5 134t128.5 114l40 41l140 -164q-156 -131 -420 -256z" /> +<glyph unicode="ê" horiz-adv-x="894" d="M92 373q0 156 54.5 303t174 252.5t279.5 105.5q262 0 262 -246q0 -86 -56 -156.5t-148.5 -115.5t-175.5 -71.5t-169 -43.5v-28q0 -74 34 -148.5t97 -74.5q59 0 120 25.5t91 49.5l31 25l92 -123q-16 -18 -47 -44t-126 -70t-193 -44q-160 0 -240 119t-80 285zM219 1280 q268 141 414 258q115 -145 262 -274l-133 -121q-117 109 -164 162q-156 -92 -283 -156zM338 586q307 72 307 215q0 55 -65 55q-86 0 -148.5 -85t-93.5 -185z" /> +<glyph unicode="ë" horiz-adv-x="894" d="M92 373q0 156 54.5 303t174 252.5t279.5 105.5q262 0 262 -246q0 -86 -56 -156.5t-148.5 -115.5t-175.5 -71.5t-169 -43.5v-28q0 -74 34 -148.5t97 -74.5q59 0 120 25.5t91 49.5l31 25l92 -123q-16 -18 -47 -44t-126 -70t-193 -44q-160 0 -240 119t-80 285zM313 1276 q0 57 44 103t102 46q47 0 79.5 -32.5t32.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM338 586q307 72 307 215q0 55 -65 55q-86 0 -148.5 -85t-93.5 -185zM659 1276q0 57 44.5 103t101.5 46q47 0 80 -32.5t33 -77.5q0 -57 -46.5 -100.5t-103.5 -43.5 q-45 0 -77 30t-32 75z" /> +<glyph unicode="ì" horiz-adv-x="477" d="M66 0l135 1004h186l-135 -1004h-186zM76 1403l180 156q90 -154 264 -295l-108 -125q-195 112 -336 264z" /> +<glyph unicode="í" horiz-adv-x="477" d="M66 0l135 1004h186l-135 -1004h-186zM98 1270q215 137 355 289l139 -164q-158 -133 -420 -256z" /> +<glyph unicode="î" horiz-adv-x="477" d="M66 1280q268 141 413 258q115 -145 262 -274l-133 -121q-117 109 -166 162q-133 -82 -280 -156zM145 0l138 1004h184l-135 -1004h-187z" /> +<glyph unicode="ï" horiz-adv-x="477" d="M66 1276q0 57 44 103t99 46q47 0 81 -32.5t34 -77.5q0 -57 -46.5 -100.5t-103.5 -43.5q-45 0 -76.5 30t-31.5 75zM106 0l138 1004h186l-137 -1004h-187zM412 1276q0 57 44 103t99 46q47 0 81 -32.5t34 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -76.5 30t-31.5 75z" /> +<glyph unicode="ñ" horiz-adv-x="1060" d="M84 0l158 1004h217l-29 -162q49 80 126 136t161 56q113 0 170 -43t60 -109q1 -18 1 -37q0 -51 -6 -108l-115 -737h-219l119 750q3 21 3 36q0 27 -9 38q-14 17 -57 18q-111 0 -271 -244l-92 -598h-217zM285 1321q123 100 229 109q10 1 21 0q68 0 151 -33q83 -31 117 -31 q9 0 14 2q25 9 25 44q0 20 -8 48l178 -35q1 -14 1 -27q0 -62 -32 -108q-47 -76 -154 -82q-12 -1 -23 -1q-58 0 -118 26l-30 14q-26 12 -32 14.5t-25 9.5t-36.5 9t-38.5 2q-61 0 -176 -86z" /> +<glyph unicode="ò" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM305 365q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209q-68 0 -120 -53.5 t-79.5 -133t-41 -155.5t-13.5 -137zM395 1403l180 156q90 -154 265 -295l-109 -125q-195 112 -336 264z" /> +<glyph unicode="ó" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM305 365q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209q-68 0 -120 -53.5 t-79.5 -133t-41 -155.5t-13.5 -137zM406 1270q98 61 187 134t128 114l41 41l139 -164q-184 -145 -420 -256z" /> +<glyph unicode="ô" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM272 1280q268 141 414 258q115 -145 262 -274l-133 -121q-117 109 -164 162q-156 -92 -282 -156zM305 365 q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209q-68 0 -120 -53.5t-79.5 -133t-41 -155.5t-13.5 -137z" /> +<glyph unicode="õ" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM260 1321q123 100 229 109q11 1 22 0q70 0 151 -33q6 -2 25.5 -9.5t28.5 -10.5t28.5 -8t25.5 -4t23 3 q25 9 24 44q0 20 -8 48l180 -35q1 -11 1 -21q0 -64 -32 -114q-51 -76 -155 -82q-12 -1 -23 -1q-58 0 -118 26q-8 4 -27 12t-26 12.5t-21.5 9.5t-25.5 8t-26.5 5t-33.5 2q-61 0 -176 -86zM305 365q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209 q-68 0 -120 -53.5t-79.5 -133t-41 -155.5t-13.5 -137z" /> +<glyph unicode="ö" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q166 0 248.5 -127t82.5 -303q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-166 0 -249 126t-83 300zM305 365q0 -207 137 -207q68 0 120 53t80 132t41 154.5t13 137.5q0 209 -137 209q-68 0 -120 -53.5 t-79.5 -133t-41 -155.5t-13.5 -137zM340 1276q0 57 43 103t100 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-104 -43.5q-45 0 -75.5 30t-30.5 75zM684 1276q0 57 44 103t101 46q47 0 80 -32.5t33 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75z" /> +<glyph unicode="÷" horiz-adv-x="1001" d="M96 420v184h807v-184h-807zM342 213q0 63 47 108.5t111 45.5q59 0 99 -38t40 -98q0 -61 -47 -106t-111 -45q-59 0 -99 37t-40 96zM342 799q0 63 47 108t111 45q59 0 99 -37.5t40 -97.5q0 -63 -47 -108t-111 -45q-59 0 -99 38t-40 97z" /> +<glyph unicode="ø" horiz-adv-x="999" d="M92 395q0 141 55.5 284.5t170 249t260.5 105.5q59 0 102 -16l80 178h102l-100 -221q147 -113 147 -371q0 -102 -32.5 -211.5t-92 -205t-154.5 -157t-206 -61.5q-61 0 -105 17l-94 -209h-102l113 254q-144 112 -144 364zM305 365q0 -80 23 -134l272 609q-29 4 -41 4 q-68 0 -120 -53.5t-79.5 -132t-41 -154.5t-13.5 -139zM399 164q18 -6 43 -6q68 0 120 52t80 132t41 154.5t13 138.5q0 88 -24 137z" /> +<glyph unicode="ù" horiz-adv-x="1038" d="M105 156q-1 11 0 21q0 43 10 89l114 738h217l-116 -750q-3 -21 -3 -36q0 -27 9 -39q14 -17 57 -17q104 0 271 244l92 598h217l-131 -836q-4 -31 8 -50.5t27 -23.5l16 -4l-78 -121q-10 -1 -22 -1h-14q-22 1 -67.5 15.5t-64.5 47.5q-15 25 -15 67q0 29 7 66 q-53 -78 -135 -136.5t-164 -58.5q-86 0 -140.5 25.5t-73 67t-21.5 94.5zM436 1403l180 156q90 -154 265 -295l-109 -125q-195 112 -336 264z" /> +<glyph unicode="ú" horiz-adv-x="1038" d="M105 156q-1 11 0 21q0 43 10 89l114 738h217l-116 -750q-3 -21 -3 -36q0 -27 9 -39q14 -17 57 -17q104 0 271 244l92 598h217l-131 -836q-4 -31 8 -50.5t27 -23.5l16 -4l-78 -121q-10 -1 -22 -1h-14q-22 1 -67.5 15.5t-64.5 47.5q-15 25 -15 67q0 29 7 66 q-53 -78 -135 -136.5t-164 -58.5q-86 0 -140.5 25.5t-73 67t-21.5 94.5zM446 1270q215 137 355 289l139 -164q-158 -133 -420 -256z" /> +<glyph unicode="û" horiz-adv-x="1038" d="M105 156q-1 11 0 21q0 43 10 89l114 738h217l-116 -750q-3 -21 -3 -36q0 -27 9 -39q14 -17 57 -17q104 0 271 244l92 598h217l-131 -836q-4 -31 8 -50.5t27 -23.5l16 -4l-78 -121q-10 -1 -22 -1h-14q-22 1 -67.5 15.5t-64.5 47.5q-15 25 -15 67q0 29 7 66 q-53 -78 -135 -136.5t-164 -58.5q-86 0 -140.5 25.5t-73 67t-21.5 94.5zM307 1280q268 141 414 258q115 -145 264 -274l-135 -121q-117 109 -164 162q-156 -92 -283 -156z" /> +<glyph unicode="ü" horiz-adv-x="1038" d="M105 156q-1 11 0 21q0 43 10 89l114 738h217l-116 -750q-3 -21 -3 -36q0 -27 9 -39q14 -17 57 -17q104 0 271 244l92 598h217l-131 -836q-4 -31 8 -50.5t27 -23.5l16 -4l-78 -121q-10 -1 -22 -1h-14q-22 1 -67.5 15.5t-64.5 47.5q-15 25 -15 67q0 29 7 66 q-53 -78 -135 -136.5t-164 -58.5q-86 0 -140.5 25.5t-73 67t-21.5 94.5zM348 1276q0 57 44 103t100 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5t-103 -43.5q-45 0 -77 30t-32 75zM694 1276q0 57 43 103t101 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5 t-103 -43.5q-47 0 -78 30t-31 75z" /> +<glyph unicode="ý" horiz-adv-x="880" d="M-18 -225q106 45 173.5 100t120.5 150l-200 979h229l123 -728l61 109q168 287 168 358q0 113 -43 236l211 45q41 -113 41 -246q0 -121 -67 -237l-336 -584q-78 -137 -177.5 -215t-252.5 -125zM776 1288q98 61 187.5 134t130.5 114l39 41l139 -164q-156 -131 -420 -256z " /> +<glyph unicode="ÿ" horiz-adv-x="880" d="M-18 -225q106 45 173.5 100t120.5 150l-200 979h229l123 -728l61 109q170 291 170 358q0 109 -45 236l211 45q41 -113 41 -246q0 -121 -67 -237l-336 -584q-78 -137 -177.5 -215t-252.5 -125zM188 1276q0 57 44 103t100 46q47 0 80.5 -32.5t33.5 -77.5q0 -57 -46 -100.5 t-103 -43.5q-45 0 -77 30t-32 75zM535 1276q0 57 43 103t100 46q47 0 80 -32.5t33 -77.5q0 -57 -45.5 -100.5t-102.5 -43.5q-47 0 -77.5 30t-30.5 75z" /> +<glyph unicode="Œ" horiz-adv-x="1286" d="M150 750q76 297 256 444q119 96 258 96h667l-33 -207h-467l-49 -307h359l-33 -211h-358l-56 -356h486l-33 -209h-674q-225 0 -313 233q-44 118 -44 257q1 122 34 260zM352 627q-16 -104 -16 -184q0 -219 125 -242l139 882q-96 -27 -157.5 -155.5t-90.5 -300.5z" /> +<glyph unicode="œ" horiz-adv-x="1486" d="M100 506q35 207 169 367.5t327 160.5q166 0 262 -133q139 133 322 133q121 0 197.5 -62.5t76.5 -183.5q0 -86 -57.5 -156.5t-152.5 -117.5t-182 -75.5t-181 -49.5v-8q-2 -82 35.5 -152.5t101.5 -70.5q119 0 270 161l101 -143q-199 -209 -404 -209q-164 0 -254 125 q-135 -125 -295 -125q-135 0 -220 85t-111 205q-14 67 -14 136q0 56 9 113zM324 494q-11 -74 -12 -132q0 -206 143 -206q125 0 198 163q-2 18 -2 54q0 166 62 313q-18 158 -135 158q-98 0 -163.5 -101.5t-90.5 -248.5zM907 586q311 70 312 215q0 47 -70 47q-78 0 -143.5 -79 t-98.5 -183z" /> +<glyph unicode="Ÿ" horiz-adv-x="880" d="M55 1165l154 154q129 -82 209 -246q49 -104 61 -262q35 154 138.5 304.5t232.5 217.5l143 -151q-61 -39 -113.5 -87t-94.5 -114t-73.5 -120t-60.5 -141l-46 -138q-17 -51 -38 -154l-28 -143q-8 -41 -26 -150l-21 -135h-224l74 471q-14 127 -20 164q-20 164 -86 314 t-181 216zM240 1542q0 57 44 103.5t101 46.5q47 0 80 -33t33 -78q0 -57 -46 -100t-104 -43q-45 0 -76.5 29.5t-31.5 74.5zM586 1542q0 57 44 103.5t101 46.5q47 0 80 -33t33 -78q0 -57 -46 -100t-104 -43q-45 0 -76.5 29.5t-31.5 74.5z" /> +<glyph unicode="ˆ" horiz-adv-x="737" d="M70 1280q266 141 413 258q113 -145 262 -274l-135 -121q-117 109 -164 162q-152 -90 -280 -156z" /> +<glyph unicode="˜" horiz-adv-x="763" d="M20 1321q123 100 230 109q10 1 21 0q68 0 151 -33q83 -31 116 -31q9 0 15 2q25 9 25 44q0 20 -9 48l179 -35q1 -14 1 -27q0 -62 -32 -108q-47 -76 -154 -82q-12 -1 -23 -1q-58 0 -118 26l-30 14q-26 12 -32 14.5t-25.5 9.5t-36.5 9t-38 2q-61 0 -176 -86z" /> +<glyph unicode="–" horiz-adv-x="733" d="M0 461v123h733v-123h-733z" /> +<glyph unicode="—" horiz-adv-x="1245" d="M0 463v123h1245v-123h-1245z" /> +<glyph unicode="‘" horiz-adv-x="559" d="M174 999l70 111q45 72 262 246l92 -94q-100 -119 -141 -211q-18 -41 -25 -133l-25 -11q-15 -6 -49 -15t-51 -9q-39 2 -71.5 31.5t-47.5 56.5z" /> +<glyph unicode="’" horiz-adv-x="610" d="M172 944q106 129 141 211q18 41 25 133l26 10q15 6 48.5 15.5t50.5 9.5q37 -2 70.5 -31.5t48.5 -58.5l14 -27l-70 -110q-45 -72 -262 -246z" /> +<glyph unicode="‚" horiz-adv-x="528" d="M16 -209q57 68 85 104.5t59 106.5q27 61 27 117q0 7 -1 14q82 45 121 45q29 0 64 -28.5t55 -57.5l18 -29q-98 -164 -307 -366z" /> +<glyph unicode="“" horiz-adv-x="944" d="M178 999l70 111q45 72 262 246l92 -94q-100 -119 -141 -211q-18 -41 -25 -134l-25 -10q-15 -6 -49 -15t-51 -9q-39 2 -71.5 31.5t-46.5 56.5zM541 999l69 111q45 72 262 246l93 -94q-100 -119 -142 -211q-18 -41 -24 -133l-26 -11q-15 -6 -49 -15t-50 -9q-39 2 -72 31.5 t-47 56.5z" /> +<glyph unicode="”" horiz-adv-x="942" d="M168 944q106 129 141 211q18 41 25 133l25 10q15 6 49 15.5t51 9.5q37 -2 70.5 -31.5t48.5 -58.5l14 -27l-70 -110q-45 -72 -262 -246zM530 944q106 129 142 211q18 41 24 133l26 10q15 6 49 15.5t50 9.5q37 -2 71 -31.5t48 -58.5l14 -27l-69 -110q-45 -72 -262 -246z " /> +<glyph unicode="„" horiz-adv-x="933" d="M-55 -195q106 123 141 211q18 41 25 134l25 10q15 6 49 15t51 9q39 -2 71.5 -31.5t46.5 -58.5l15 -26l-70 -111q-45 -72 -262 -246zM307 -195q106 123 142 211q18 41 24 134l26 10q15 6 49 15t50 9q39 -2 71.5 -31.5t47.5 -58.5l14 -26l-69 -111q-45 -72 -263 -246z" /> +<glyph unicode="•" horiz-adv-x="868" d="M80 645.5q0 145.5 103.5 248.5t249 103t249.5 -103t104 -248.5t-104 -249t-249.5 -103.5t-249 103.5t-103.5 249z" /> +<glyph unicode="…" horiz-adv-x="1204" d="M70 104.5q0 61.5 50 105.5t113 44q61 0 101.5 -37t40.5 -96q0 -61 -50.5 -106.5t-113.5 -45.5q-61 0 -101 37t-40 98.5zM422 104.5q0 61.5 50 105.5t114 44q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5zM770 104.5q0 61.5 50 105.5t114 44 q61 0 101 -37t40 -96q0 -61 -50 -106.5t-114 -45.5q-61 0 -101 37t-40 98.5z" /> +<glyph unicode="‹" horiz-adv-x="589" d="M37 500l430 393l127 -121l-313 -276l229 -285l-152 -113z" /> +<glyph unicode="›" horiz-adv-x="665" d="M63 219l314 277l-230 284l152 113l322 -401l-431 -394z" /> +<glyph unicode="™" horiz-adv-x="1583" d="M219 1282l21 135h495l-20 -135h-176l-103 -676h-143l104 676h-178zM717 608l129 813h170l112 -524l9 -92q10 55 32 96l263 520h174l-131 -813h-146l64 400l24 143q-45 -135 -61 -168l-187 -375h-118l-88 404l-15 139l-20 -137l-64 -406h-147z" /> +<glyph unicode="" horiz-adv-x="1025" d="M0 1025h1025v-1025h-1025v1025z" /> +<glyph unicode="fi" horiz-adv-x="1056" d="M16 -299q111 205 138 524l45 602l-121 21l24 156h115l4 92q8 176 129 267t307 91q51 0 117 -13.5t132.5 -39t100.5 -75.5q19 -28 18 -60q0 -25 -11 -54q-23 -57 -80 -79q-25 -12 -55 -13q-78 0 -119 66q-18 29 -18 67q0 10 1 21q-57 8 -110 8q-16 0 -32 -1 q-69 -3 -119 -51t-52 -136l-2 -90h137v-181h-151l-62 -714q-10 -129 -63 -239t-148 -249zM604 0l141 1004h216l-140 -1004h-217z" /> +<glyph unicode="fl" horiz-adv-x="1056" d="M18 -299q111 205 138 524l45 602l-121 21l24 156h115l6 102q10 199 118 268.5t310 69.5q82 0 173.5 -19.5t140.5 -38.5l51 -18l-144 -954q-11 -82 -11 -155q0 -126 34 -224l-209 -58q-40 122 -40 269q0 85 14 180l118 823q-61 31 -135 31q-86 0 -147.5 -57.5 t-63.5 -126.5l-4 -92h137v-181h-151l-62 -714q-10 -129 -63 -239t-148 -249z" /> +<glyph unicode="ffi" horiz-adv-x="1579" d="M16 -299q111 205 138 524l45 602l-121 21l24 156h115q0 72 10.5 137t34 132.5t73.5 108.5t120 41q33 0 64.5 -5t45.5 -11l17 -6l-25 -168h-63q-14 0 -25.5 -9.5t-18 -27.5t-11.5 -35.5t-7 -44t-3 -43t-1 -40.5v-29h137v-181h-151l-62 -714q-10 -129 -63 -239t-148 -249z M567 -299q111 205 138 524l45 602l-121 21l24 156h115q0 72 10 137t34 132.5t74 108.5t120 41q33 0 64.5 -5t45.5 -11l17 -6l-25 -168h-64q-14 0 -25 -9.5t-17.5 -27.5t-11.5 -35.5t-7 -44t-3 -43t-1 -40.5v-29h137v-181h-151l-62 -714q-10 -129 -63.5 -239t-147.5 -249z M1167 0l152 1004h217l-154 -1004h-215zM1315 1251q0 78 74 121q33 20 73 21q55 0 95 -38t40 -93q0 -25 -10 -50q-23 -57 -78 -79q-27 -12 -57 -13q-78 0 -119 66q-18 32 -18 65z" /> +<glyph unicode="ffl" horiz-adv-x="1595" d="M16 -299q111 205 138 524l45 602l-121 21l24 156h115q0 72 10.5 137t34 132.5t73.5 108.5t120 41q33 0 64.5 -5t45.5 -11l17 -6l-25 -168h-63q-14 0 -25.5 -9.5t-18 -27.5t-11.5 -35.5t-7 -44t-3 -43t-1 -40.5v-29h137v-181h-151l-62 -714q-10 -129 -63 -239t-148 -249z M567 -299q111 205 138 524l45 602l-121 21l24 156h115q0 72 10 137t34 132.5t74 108.5t120 41q33 0 64.5 -5t45.5 -11l17 -6l-25 -168h-64q-14 0 -25 -9.5t-17.5 -27.5t-11.5 -35.5t-7 -44t-3 -43t-1 -40.5v-29h137v-181h-151l-62 -714q-10 -129 -63.5 -239t-147.5 -249z M1225 426l143 997h217l-145 -1009q-11 -86 -11 -155q0 -24 2 -46q5 -84 17 -131l12 -47l-207 -58q-41 124 -41 276q1 82 13 173z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-bolditalic-webfont.ttf b/app/assets/fonts/delicious-bolditalic-webfont.ttf Binary files differnew file mode 100755 index 000000000..ef25c0adc --- /dev/null +++ b/app/assets/fonts/delicious-bolditalic-webfont.ttf diff --git a/app/assets/fonts/delicious-bolditalic-webfont.woff b/app/assets/fonts/delicious-bolditalic-webfont.woff Binary files differnew file mode 100755 index 000000000..068f460de --- /dev/null +++ b/app/assets/fonts/delicious-bolditalic-webfont.woff diff --git a/app/assets/fonts/delicious-heavy-webfont.eot b/app/assets/fonts/delicious-heavy-webfont.eot Binary files differnew file mode 100755 index 000000000..782a01422 --- /dev/null +++ b/app/assets/fonts/delicious-heavy-webfont.eot diff --git a/app/assets/fonts/delicious-heavy-webfont.svg b/app/assets/fonts/delicious-heavy-webfont.svg new file mode 100755 index 000000000..64e0eb347 --- /dev/null +++ b/app/assets/fonts/delicious-heavy-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : copyright 19941996 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousHeavyRegular" horiz-adv-x="921" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="	" horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="!" horiz-adv-x="540" d="M115 123q0 68 56 114t130 46q72 0 125 -43t53 -108.5t-57 -113t-131 -47.5q-70 0 -123 43t-53 109zM139 1022q0 262 39 381l305 37q-55 -254 -55 -430v-600h-258l-13 192q-9 131 -13.5 226.5t-4.5 193.5z" /> +<glyph unicode=""" horiz-adv-x="708" d="M68 1305l38 168h160l47 -99v-401h-196zM385 971v403l51 99h154l45 -168l-55 -332z" /> +<glyph unicode="#" horiz-adv-x="1138" d="M68 356l36 262h115l29 216h-96l36 262h95l43 321h264l-43 -321h143l37 278h264l-37 -278h107l-25 -262h-118l-29 -216h88l-27 -262h-98l-47 -350h-258l45 350h-146l-43 -307h-258l39 307h-116zM481 618h146l28 216h-145z" /> +<glyph unicode="$" horiz-adv-x="888" d="M47 954q0 170 117 270q114 97 284 97h7l8 113l92 -9l-8 -114q82 -18 164 -68.5t129 -116.5l-170 -151q-53 61 -142 86l-18 -270l117 -74q113 -74 173 -157t60 -206q0 -168 -118.5 -271t-288.5 -114l-9 -121l-92 9l8 118q-158 27 -288 140l135 213q92 -70 172 -92l20 280 l-106 70q-117 78 -181.5 161.5t-64.5 206.5zM346 963q0 -68 76 -119l14 223q-37 -6 -63.5 -35.5t-26.5 -68.5zM471 233q88 25 88 123q0 47 -72 105z" /> +<glyph unicode="%" horiz-adv-x="1431" d="M49 1003.5q0 131.5 93.5 224.5t224.5 93q80 0 153 -39q4 -4 16 -11l17 -11q43 -20 145 -21q180 0 377 80l197 -49l-885 -1272h-272l757 1081q-68 -45 -190 -45q2 -10 2 -30q0 -131 -93 -224.5t-224 -93.5t-224.5 93t-93.5 224.5zM289 999.5q0 -32.5 22.5 -55t55 -22.5 t55 22.5t22.5 55t-22.5 55t-55 22.5t-55 -22.5t-22.5 -55zM735 287q0 131 93.5 224t224.5 93t224 -93t93 -224t-93 -224.5t-224 -93.5t-224.5 93.5t-93.5 224.5zM975 282.5q0 -32.5 22.5 -55t55 -22.5t55 22.5t22.5 55t-22.5 55t-55 22.5t-55 -22.5t-22.5 -55z" /> +<glyph unicode="&" horiz-adv-x="1290" d="M43 500q0 158 82 281.5t231 148.5q-53 18 -85.5 70.5t-32.5 111.5q0 143 122.5 224t272.5 81q166 0 290 -86t124 -246v-81h192v-244h-188v-463q0 -31 1 -42t12 -24.5t38 -13.5q41 0 74 19l69 -201q-137 -66 -246 -66q-121 0 -183 77t-62 200v514h-195q-59 0 -100 -21.5 t-61.5 -47t-31 -80t-11.5 -86t-1 -97.5q0 -217 60 -217q10 0 71.5 45t122.5 45q35 0 70 -12v-191q-47 0 -80 -20.5t-49.5 -44t-51 -44t-83.5 -20.5q-94 0 -169 51.5t-117 132t-63.5 170t-21.5 177.5zM530 1098q0 -94 131 -94h109v36q0 78 -23.5 115t-95.5 37 q-121 0 -121 -94z" /> +<glyph unicode="'" horiz-adv-x="425" d="M59 1305l41 168h215l43 -168l-71 -330h-158z" /> +<glyph unicode="(" horiz-adv-x="688" d="M35 676q0 293 159.5 526.5t432.5 396.5l37 -239q-172 -119 -268.5 -300t-96.5 -388q0 -444 354 -733l-45 -238q-145 106 -242.5 199.5t-177 209t-116.5 255t-37 311.5z" /> +<glyph unicode=")" horiz-adv-x="694" d="M29 1360q172 -119 268 -300t96 -388q0 -444 -354 -733l45 -238q197 143 313.5 268t188 302.5t71.5 404.5q0 293 -159.5 526.5t-431.5 396.5z" /> +<glyph unicode="*" horiz-adv-x="868" d="M18 1053l41 268l240 -141l-82 229h412l-82 -229l244 141l38 -268l-211 -82l220 -293h-316l-98 180l-92 -180h-299l194 293z" /> +<glyph unicode="+" horiz-adv-x="964" d="M45 391v242h305v299h262v-299h308v-242h-308v-301h-262v301h-305z" /> +<glyph unicode="," horiz-adv-x="528" d="M27 -229q115 152 114 247q0 18 -16 156q90 68 162 68q45 0 101 -46t93 -96q-154 -289 -309 -428z" /> +<glyph unicode="-" horiz-adv-x="618" d="M45 408v227h520v-227h-520z" /> +<glyph unicode="." horiz-adv-x="528" d="M80 131q0 72 56.5 122t129.5 50q70 0 123 -46t53 -116q0 -72 -56 -122t-130 -50q-70 0 -123 46t-53 116z" /> +<glyph unicode="/" horiz-adv-x="729" d="M8 -289l443 1825h276l-442 -1825h-277z" /> +<glyph unicode="0" d="M45 501.5q0 227.5 104.5 380t311.5 152.5t311.5 -152.5t104.5 -380t-104.5 -380t-311.5 -152.5t-311.5 152.5t-104.5 380zM317 502q0 -283 144 -283q68 0 105.5 73t37.5 210q0 281 -143.5 281t-143.5 -281z" /> +<glyph unicode="1" d="M158 737v219q111 0 276 48h219v-1004h-293v737h-202z" /> +<glyph unicode="2" d="M74 193q143 92 306 249.5t163 268.5q0 37 -29 54t-68 17q-72 0 -153.5 -32.5t-140.5 -79.5l-68 219q68 68 176.5 106.5t208.5 38.5q164 0 268.5 -78.5t104.5 -238.5q0 -127 -99.5 -248t-234.5 -215h360v-254h-751z" /> +<glyph unicode="3" d="M90 -133q72 2 148.5 19.5t151.5 50t123 94t48 139.5q0 39 -17.5 64.5t-51 36t-61.5 13.5t-66 3q-119 0 -238 -13v228l129 10q188 102 254 166q33 33 33 70q0 45 -49 45q-72 0 -308 -107l-71 203q92 55 208.5 99t198.5 44q131 0 222.5 -69.5t91.5 -194.5q0 -88 -59.5 -165 t-143.5 -116q96 -23 160.5 -102.5t64.5 -175.5q0 -147 -61.5 -258t-166 -172.5t-227 -91t-260.5 -29.5h-53v209z" /> +<glyph unicode="4" d="M43 201q188 414 489 803h271v-750h78v-254h-82v-283h-283v283h-461zM319 254h193v348q-20 -25 -98.5 -164t-94.5 -184z" /> +<glyph unicode="5" d="M82 -133q106 0 208.5 27.5t185.5 102.5t83 185q0 51 -41 79t-96 28q-145 0 -307 -45l90 760h596v-252h-355l-20 -240q47 10 88 10q145 0 243.5 -84t98.5 -227q0 -176 -106.5 -305t-260.5 -188q-148 -56 -314 -56h-11h-82v205z" /> +<glyph unicode="6" d="M59 537q0 147 52.5 274t148.5 223t205.5 167t249.5 134l108 -182q-295 -176 -405 -350q90 37 135 37q150 0 241 -111t91 -262q0 -215 -98.5 -356.5t-303.5 -141.5q-147 0 -246.5 89t-138.5 212.5t-39 266.5zM350 500q0 -90 31 -185.5t102 -95.5q121 0 121 242 q0 152 -133 151q-39 0 -113 -28q-8 -53 -8 -84z" /> +<glyph unicode="7" d="M78 745v259h762v-148q0 -276 -136.5 -631.5t-324.5 -593.5l-234 140q160 217 282 482t140 492h-489z" /> +<glyph unicode="8" d="M31 352q0 178 219 320q-184 129 -184 266q0 166 119.5 259t289.5 93q164 0 269.5 -89t105.5 -251q0 -133 -188 -260q113 -59 176 -132t63 -181q0 -188 -130 -298t-322 -110q-168 0 -293 108.5t-125 274.5zM326 373q0 -63 40 -112.5t101 -49.5q63 0 100 46t37 112 q0 82 -158 180q-120 -90 -120 -176zM354 932q0 -72 101 -135q111 72 110 153q0 109 -110 109q-101 0 -101 -127z" /> +<glyph unicode="9" d="M45 537q0 215 98.5 356t302.5 141q147 0 247 -89t138.5 -212t38.5 -266q0 -500 -673 -807l-95 193q299 158 410 348q-84 -37 -135 -37q-150 0 -241 110.5t-91 262.5zM330 543q0 -74 28.5 -113t100.5 -39q39 0 112 29q8 53 9 84q0 90 -31 184t-103 94q-41 0 -66.5 -23.5 t-35.5 -67.5t-12 -72.5t-2 -75.5z" /> +<glyph unicode=":" horiz-adv-x="528" d="M80 131q0 72 56.5 122t129.5 50q70 0 123 -46t53 -116q0 -72 -56 -122t-130 -50q-70 0 -123 46t-53 116zM80 721q0 74 56.5 124t129.5 50q70 0 123 -46t53 -116q0 -74 -56 -124t-130 -50q-70 0 -123 46t-53 116z" /> +<glyph unicode=";" horiz-adv-x="528" d="M20 -229q115 145 115 247q0 18 -16 156q90 68 162 68q47 0 102 -45t92 -97q-154 -289 -309 -428zM94 719q0 74 56.5 124t130.5 50q70 0 123 -46t53 -116q0 -74 -56.5 -124t-130.5 -50q-70 0 -123 46t-53 116z" /> +<glyph unicode="<" horiz-adv-x="614" d="M33 512l352 432l207 -168l-199 -262l201 -270l-207 -164z" /> +<glyph unicode="=" horiz-adv-x="983" d="M35 248v219h893v-219h-893zM35 545v219h893v-219h-893z" /> +<glyph unicode=">" horiz-adv-x="614" d="M31 244l207 -164l354 432l-352 432l-207 -168l198 -262z" /> +<glyph unicode="?" horiz-adv-x="733" d="M80 122.5q0 65.5 56.5 113t129.5 47.5q70 0 123 -43t53 -108.5t-57 -113t-129 -47.5q-70 0 -123 43t-53 108.5zM137 410v110q0 121 17.5 193.5t83.5 140.5l106 111q86 90 86 151q0 29 -15.5 43.5t-31.5 16.5t-49 2q-90 0 -187 -27v227q188 43 277 43q137 0 216 -82 t79 -219q0 -119 -113 -241l-127 -138q-92 -98 -92 -221v-110h-250z" /> +<glyph unicode="@" horiz-adv-x="1622" d="M51 721q0 311 236.5 535.5t550.5 224.5q195 0 357.5 -71t266 -215t103.5 -339q0 -180 -152.5 -348t-322.5 -168q-82 0 -127 35t-74 104q-147 -121 -242 -121q-109 0 -159 79t-50 196q0 182 85 316t251 134q94 0 174 -79l8 51h222l-66 -424q-2 -10 -5 -27.5t-5 -32 t-2 -26.5q0 -45 43 -45q68 0 114 118.5t46 206.5q0 207 -132.5 310.5t-343.5 103.5q-205 0 -352 -154.5t-147 -361.5q0 -240 169 -379t414 -139q154 0 259.5 44t236.5 150l113 -141q-160 -147 -314.5 -218t-355.5 -71q-324 0 -561.5 216.5t-237.5 535.5zM672 657 q0 -78 39 -77q29 0 54.5 6t34.5 10t43 21.5t40 21.5l28 186q-86 43 -114 43q-125 0 -125 -211z" /> +<glyph unicode="A" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM428 506h193l-97 393z" /> +<glyph unicode="B" horiz-adv-x="1028" d="M109 31v1259h413q182 0 296 -84t114 -260q0 -74 -49 -151.5t-119 -96.5q102 -16 169.5 -104t67.5 -193q0 -207 -149.5 -319.5t-362.5 -112.5q-110 1 -380 62zM414 227q66 -14 71 -14q96 0 153.5 47t57.5 141q0 39 -13 68t-32.5 43t-51.5 21.5t-56.5 9.5t-61.5 2h-67v-318 zM414 776h67q150 0 150 142q0 117 -109 116h-108v-258z" /> +<glyph unicode="C" horiz-adv-x="1017" d="M66 645q0 168 59 318.5t187 254t300 103.5q90 0 190.5 -24.5t162.5 -73.5l-113 -205q-94 47 -240 47q-82 0 -140 -81t-79.5 -174t-21.5 -173q0 -70 9 -133.5t33.5 -130t77 -106.5t125.5 -40q129 0 242 74l133 -203q-72 -59 -178 -94t-205 -35q-139 0 -246.5 57.5 t-170 155t-94 214t-31.5 249.5z" /> +<glyph unicode="D" horiz-adv-x="1120" d="M109 0v1290h428q272 0 397 -177t125 -462q0 -170 -57.5 -314t-183.5 -240.5t-300 -96.5h-409zM412 258h106q70 0 118 37t71.5 98.5t34 120.5t10.5 125q0 76 -6.5 131t-26 121.5t-68.5 103.5t-123 37h-116v-774z" /> +<glyph unicode="E" horiz-adv-x="929" d="M106 0v1290h748v-262h-442v-225h340v-266h-340v-271h465v-266h-771z" /> +<glyph unicode="F" horiz-adv-x="843" d="M104 0v1290h695v-262h-389v-225h307v-266h-307v-537h-306z" /> +<glyph unicode="G" horiz-adv-x="1073" d="M59 645q0 170 59.5 320.5t188.5 252t303 101.5q102 0 216 -33t188 -92l-117 -201q-137 72 -287 72q-82 0 -141 -81t-81.5 -174t-22.5 -173q0 -70 10 -134.5t35.5 -130t78 -105.5t125.5 -40q72 0 97 9v395h293v-588q-162 -74 -394 -74q-139 0 -247.5 56.5t-173 152.5 t-97.5 215t-33 252z" /> +<glyph unicode="H" horiz-adv-x="1122" d="M111 0v1290h305v-487h291v487h305v-1290h-305v539h-291v-539h-305z" /> +<glyph unicode="I" horiz-adv-x="487" d="M92 0v1290h303v-1290h-303z" /> +<glyph unicode="J" horiz-adv-x="501" d="M-29 -125q49 16 90 52t41 79v1284h301v-1294q0 -74 -29.5 -131.5t-87 -95t-111.5 -60t-130 -45.5z" /> +<glyph unicode="K" horiz-adv-x="993" d="M111 0v1290h305v-438l235 438h342l-389 -649l379 -504v-137h-264l-303 440v-440h-305z" /> +<glyph unicode="L" horiz-adv-x="782" d="M104 0v1290h297v-1028h365v-262h-662z" /> +<glyph unicode="M" horiz-adv-x="1519" d="M111 0v1290h331l287 -784q20 -55 31 -135q10 80 31 135l284 784h334v-1290h-305v565l16 156l-243 -721h-236l-244 721l19 -156v-565h-305z" /> +<glyph unicode="N" horiz-adv-x="1134" d="M111 0v1290h329l224 -508q37 -80 55 -176v684h305v-1290h-281l-344 764q16 -82 17 -166v-598h-305z" /> +<glyph unicode="O" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM367 645.5q0 -65.5 2 -113t13 -109t31.5 -99t57.5 -65.5t90 -28t90 28 t57.5 67t32 100t13.5 108.5t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5t-2 -112.5z" /> +<glyph unicode="P" horiz-adv-x="1001" d="M106 0v1290h340q238 0 384.5 -98t146.5 -326q0 -186 -133 -319t-320 -133q-66 0 -112 28v-442h-306zM412 688q61 -25 82 -24q178 0 178 204q0 96 -56.5 131t-158.5 35h-45v-346z" /> +<glyph unicode="Q" horiz-adv-x="1122" d="M61 645q0 121 29 235.5t85 216t156.5 163t229.5 61.5t229.5 -61.5t157 -163t85 -216t28.5 -235.5q0 -199 -76 -379t-231 -256q63 -72 139 -71q72 0 168 30l-45 -288q-20 -4 -60 -5q-156 0 -261 75t-206 222q-113 16 -198.5 85t-133.5 166.5t-72 203.5t-24 217zM367 645 q0 -49 1 -79.5t6 -87t17 -93.5t32.5 -76t55.5 -58.5t82 -19.5q55 0 94 32t58.5 73t30 107.5t11.5 104.5t1 97q0 49 -1 80t-6.5 87t-17.5 93t-32.5 76t-55.5 58.5t-82 19.5t-82 -19.5t-55.5 -58.5t-32.5 -76t-17 -93t-6 -87t-1 -80z" /> +<glyph unicode="R" horiz-adv-x="1034" d="M111 0v1290h338q152 0 265 -36.5t190 -135t77 -252.5q0 -125 -67.5 -230.5t-182.5 -148.5l270 -350v-137h-256l-329 451v-451h-305zM416 682q47 -14 76 -14q84 0 132 57t48 143q0 88 -43 127t-133 39h-80v-352z" /> +<glyph unicode="S" horiz-adv-x="888" d="M47 954q0 168 116 267.5t288 99.5q106 0 214.5 -54.5t174.5 -140.5l-170 -151q-37 41 -95.5 68.5t-113.5 27.5q-45 0 -80 -31.5t-35 -76.5q0 -53 53.5 -98.5t128 -85.5t150.5 -90t129 -136t53 -199q0 -176 -128 -280.5t-308 -104.5q-84 0 -182.5 42t-169.5 104l135 213 q137 -100 219 -101q59 0 96 35t37 94q0 37 -38 74t-95 68.5t-122.5 77t-123 93.5t-95.5 122.5t-38 162.5z" /> +<glyph unicode="T" horiz-adv-x="829" d="M23 1022v268h786v-268h-244v-1022h-303l2 1022h-241z" /> +<glyph unicode="U" horiz-adv-x="1142" d="M109 514v776h305v-776q0 -147 37.5 -215t119.5 -68t120 68t38 215v776h305v-776q0 -104 -23.5 -195.5t-73.5 -172t-144.5 -129t-221.5 -48.5t-221 48.5t-144 129t-73.5 172t-23.5 195.5z" /> +<glyph unicode="V" horiz-adv-x="1044" d="M10 1290h328l158 -696q14 -59 30 -182q18 129 31 182l158 696h325l-364 -1290h-301z" /> +<glyph unicode="W" horiz-adv-x="1449" d="M12 1290h312l82 -585l22 -209q10 139 27 209l133 585h274l133 -585l31 -205q8 133 18 205l82 585h312l-250 -1290h-305l-138 690l-20 137q-10 -90 -20 -137l-138 -690h-305z" /> +<glyph unicode="X" horiz-adv-x="1007" d="M10 0l299 664l-289 626h342l130 -364l131 364h331l-274 -626l317 -664h-354l-149 399l-142 -399h-342z" /> +<glyph unicode="Y" horiz-adv-x="964" d="M4 1290h326l151 -502l154 502h326l-326 -807v-483h-305l2 483z" /> +<glyph unicode="Z" horiz-adv-x="937" d="M31 0v211q193 375 450 813h-409v266h794v-215q-262 -428 -467 -807h506v-268h-874z" /> +<glyph unicode="[" horiz-adv-x="507" d="M57 -319v1816h394v-250h-123v-1317h123v-249h-394z" /> +<glyph unicode="\" horiz-adv-x="686" d="M10 1536h277l391 -1825h-277z" /> +<glyph unicode="]" horiz-adv-x="509" d="M57 -70v-249h394v1816h-394v-250h123v-1317h-123z" /> +<glyph unicode="^" horiz-adv-x="1277" d="M84 643l453 819h198l453 -819h-309l-242 469l-246 -469h-307z" /> +<glyph unicode="_" d="M0 -92h922v-246h-922v246z" /> +<glyph unicode="`" horiz-adv-x="692" d="M39 1393l205 202q164 -193 331 -319l-114 -174q-221 121 -422 291z" /> +<glyph unicode="a" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102z" /> +<glyph unicode="b" horiz-adv-x="1030" d="M117 115v1308h297v-493q100 104 190 104q182 0 283.5 -142t101.5 -331q0 -117 -26.5 -220t-81 -188t-148.5 -134.5t-217 -49.5q-192 1 -399 146zM414 250q53 -37 110 -37q164 0 164 328q0 248 -131 247q-74 0 -143 -104v-434z" /> +<glyph unicode="c" horiz-adv-x="843" d="M45 494q0 217 122 378.5t333 161.5q80 0 166 -34.5t145 -90.5l-133 -174q-70 55 -176 56q-55 0 -93 -53.5t-52.5 -114t-14.5 -113.5q0 -55 14.5 -119.5t54.5 -122t97 -57.5q109 0 217 88l106 -190q-68 -59 -160.5 -99.5t-176.5 -40.5q-205 0 -327 157t-122 368z" /> +<glyph unicode="d" horiz-adv-x="1048" d="M49 471q0 117 22.5 213t72 177t137.5 127t211 46q63 0 147 -30v419h297v-1185q0 -27 2 -44.5t15.5 -33t37.5 -15.5l-61 -176q-12 0 -32.5 -1t-33.5 -1q-61 0 -109 36t-67 106q-25 -59 -92.5 -100.5t-134.5 -41.5q-193 0 -302.5 152.5t-109.5 351.5zM352 489 q0 -274 133 -274q57 0 94 50t48.5 105.5t11.5 112.5v279q-63 29 -139 29q-37 0 -63.5 -13.5t-42 -42t-25 -52.5t-12.5 -69t-4 -63.5t-1 -61.5z" /> +<glyph unicode="e" horiz-adv-x="964" d="M47 524q0 211 135 360.5t344 149.5q162 0 279 -116.5t121 -278.5q4 -111 4 -164h-584q-2 -10 -2 -33q0 -86 38 -157.5t118 -71.5q162 0 282 88l86 -184q-76 -63 -182 -106.5t-197 -43.5q-150 0 -253 84t-146 206t-43 267zM367 664h284q-2 68 -39 121t-100 53 q-57 0 -98 -56.5t-47 -117.5z" /> +<glyph unicode="f" horiz-adv-x="585" d="M16 791v213h101v94q0 96 43 167.5t104.5 104.5t123 52.5t104.5 21.5l43 2l45 -211q-6 -2 -17.5 -4t-40 -13.5t-50 -26.5t-40 -43t-18.5 -63v-81h153v-240h-153v-764h-297v774z" /> +<glyph unicode="g" horiz-adv-x="931" d="M37 -121q0 61 56.5 118.5t119.5 80.5q-45 12 -80 51t-35 86q0 82 138 160q-74 35 -123.5 119t-49.5 159q0 172 112 276.5t292 104.5q98 0 176 -30h252v-216l-96 25q41 -72 41 -164q0 -127 -82 -214t-207 -111q-4 0 -31.5 -4.5t-44 -8.5t-39 -12t-34 -21.5t-11.5 -29.5 q0 -8 7.5 -15.5t19.5 -10.5l25 -6q13 -3 33 -3h32h30h21h66q125 0 204.5 -54.5t79.5 -172.5q0 -182 -148.5 -287t-334.5 -105q-68 0 -132.5 14.5t-124.5 45.5t-96 89t-36 136zM322 -92q0 -88 112 -88q82 0 140.5 43t58.5 108q0 8 -4 14.5t-12.5 10.5t-15.5 6t-20.5 3 t-20.5 1h-21h-19h-108q-90 -55 -90 -98zM356 674q0 -147 103 -148q94 0 94 138q0 145 -98.5 145t-98.5 -135z" /> +<glyph unicode="h" horiz-adv-x="1062" d="M113 0v1423h297v-493q147 104 233 104q94 0 158.5 -26.5t96.5 -73.5t44 -96.5t12 -112.5v-725h-297v725q0 63 -51 63q-33 0 -64.5 -9t-47 -17t-47.5 -30l-37 -25v-707h-297z" /> +<glyph unicode="i" horiz-adv-x="487" d="M61 1268q0 72 57.5 123t129 51t125 -47t53.5 -117q0 -74 -56.5 -123t-129.5 -49q-72 0 -125.5 46t-53.5 116zM96 0v1004h293v-1004h-293z" /> +<glyph unicode="j" horiz-adv-x="487" d="M-35 -193q82 37 107.5 67t25.5 79v1051h293v-1065q0 -80 -31.5 -139.5t-93 -98.5t-112 -59.5t-125.5 -42.5zM61 1268q0 72 57.5 123t129 51t125 -47t53.5 -117q0 -74 -56.5 -123t-129.5 -49q-72 0 -125.5 46t-53.5 116z" /> +<glyph unicode="k" horiz-adv-x="964" d="M115 0v1423h297v-673l198 254h334l-352 -429l362 -446v-129h-262l-280 379v-379h-297z" /> +<glyph unicode="l" horiz-adv-x="483" d="M84 436v987h293v-987q0 -117 16.5 -225.5t32.5 -159.5l14 -51h-286q-70 157 -70 436z" /> +<glyph unicode="m" horiz-adv-x="1579" d="M115 0v1006h297v-76q135 104 235 104q160 0 230 -96l6 -8q156 104 256 104q332 0 331 -303v-731h-296v711q0 80 -52 80q-45 0 -90 -16.5t-67 -33.5l-23 -18v-723h-297v705q0 49 -11 66t-38 17q-35 0 -67.5 -10t-46 -18t-41 -29.5t-29.5 -23.5v-707h-297z" /> +<glyph unicode="n" horiz-adv-x="1062" d="M113 0v1004h297v-74q147 104 233 104q94 0 158.5 -26.5t96.5 -73.5t44 -96.5t12 -112.5v-725h-297v725q0 63 -51 63q-33 0 -64.5 -9t-47 -17t-47.5 -30l-37 -25v-707h-297z" /> +<glyph unicode="o" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM346 502q0 -133 41 -210t112.5 -77t112.5 77t41 210t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5z" /> +<glyph unicode="p" horiz-adv-x="1056" d="M113 -358v1362h299l-2 -74q12 12 31.5 29.5t75.5 46t106 28.5q125 0 216 -82t129 -191.5t38 -223.5q0 -98 -18.5 -187.5t-61.5 -180.5t-132 -145.5t-214 -54.5q-94 0 -168 72v-399h-299zM412 485q0 -276 159 -276q33 0 56.5 15.5t38 47t22.5 58t11 73t3 62.5v55 q0 268 -131 268q-29 0 -56.5 -10t-36.5 -15t-36.5 -30.5t-29.5 -27.5v-220z" /> +<glyph unicode="q" horiz-adv-x="1054" d="M47 459q0 94 25.5 190t77 184.5t143.5 144.5t209 56q78 0 187.5 -21.5t178.5 -41.5l72 -23v-1306h-299v405q-84 -78 -168 -78q-150 0 -250 77t-138 182.5t-38 230.5zM348 481q0 -272 133 -272q61 0 99 52t49.5 108.5t11.5 111.5v293q-74 18 -116 18q-12 0 -21 -1 q-47 -8 -80 -42t-48.5 -83.5t-21.5 -93.5t-6 -91z" /> +<glyph unicode="r" horiz-adv-x="653" d="M109 0v1004h299v-74q88 104 139 104q27 0 53.5 -7t38.5 -15l14 -8l-39 -291q-72 31 -129 30q-23 0 -43 -7t-28 -15l-8 -8v-713h-297z" /> +<glyph unicode="s" horiz-adv-x="858" d="M49 733q4 123 107.5 212t289.5 89q106 0 197.5 -43t130.5 -86l37 -43l-156 -143q-111 84 -213 84q-98 0 -98 -58q0 -12 5 -22t18.5 -19.5t20.5 -13.5t30 -14l96 -43q74 -33 103.5 -47.5t84 -50t77 -68.5t39.5 -84q14 -41 14 -90q0 -11 -1 -23q-14 -178 -186 -256 q-94 -45 -223 -45q-74 0 -160 36t-135 71l-51 35l108 198q162 -104 244 -104q113 0 113 74q0 43 -80 79q-29 14 -110 49t-118.5 56.5t-90 61.5t-73.5 91q-20 47 -20 106v11z" /> +<glyph unicode="t" horiz-adv-x="626" d="M35 791v213h96v180h285v-180h168v-236h-164v-475q0 -61 33 -72q16 -6 32 -6q32 0 68 21l63 -203q-88 -37 -120 -47q-49 -16 -127 -17q-127 0 -186.5 80t-59.5 195v524z" /> +<glyph unicode="u" horiz-adv-x="1062" d="M111 274v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119q-317 0 -317 305z" /> +<glyph unicode="v" horiz-adv-x="925" d="M16 1004h322l135 -586q96 285 127 586h315q-141 -582 -348 -1004h-213z" /> +<glyph unicode="w" horiz-adv-x="1372" d="M16 1004h314q92 -457 92 -586l135 586h270l138 -586q84 344 84 586h307q-66 -555 -277 -1004h-262q-100 324 -127 504q-20 -143 -125 -504h-276z" /> +<glyph unicode="x" horiz-adv-x="868" d="M4 0l275 518l-269 486h330l92 -216l96 216h326l-270 -476l280 -528h-334l-98 262l-104 -262h-324z" /> +<glyph unicode="y" horiz-adv-x="929" d="M18 1004h316q137 -459 149 -658q78 283 125 658h305q-29 -154 -70.5 -323t-106 -380t-152.5 -376t-181 -226q-37 -25 -96 -46.5t-102 -29.5l-41 -10l-68 217q72 25 113 50.5t57.5 46t46.5 73.5l13 20z" /> +<glyph unicode="z" horiz-adv-x="872" d="M51 0v197q154 283 357 553h-324v254h709v-209q-217 -270 -371 -541h405v-254h-776z" /> +<glyph unicode="{" horiz-adv-x="681" d="M31 481v215q74 0 116.5 40t42.5 120v340q0 313 265 313h141l51 -227h-119q-43 0 -56 -20.5t-13 -75.5v-357q0 -131 -127 -243q127 -113 127 -240v-356q0 -55 13 -74.5t56 -19.5h121l-51 -228h-70h-69q-268 0 -269 312v339q0 80 -42.5 121t-116.5 41z" /> +<glyph unicode="|" horiz-adv-x="454" d="M94 -29v1569h266v-1569h-266z" /> +<glyph unicode="}" horiz-adv-x="681" d="M31 -104l51 -228h70h69q270 0 271 312v339q0 80 41.5 121t115.5 41v215q-74 0 -115.5 40t-41.5 120v340q0 313 -267 313h-141l-51 -227h119q43 0 57 -20.5t14 -75.5v-357q0 -133 125 -243q-125 -111 -125 -240v-356q0 -53 -14 -73.5t-57 -20.5h-121z" /> +<glyph unicode="~" horiz-adv-x="1363" d="M274 463v209q10 16 27.5 39.5t71 62.5t104.5 39q92 0 229.5 -66.5t190.5 -66.5q41 0 90 28.5t78 57.5l27 27v-224q-8 -16 -24.5 -38.5t-67 -61.5t-103.5 -39q-102 0 -239.5 66.5t-182.5 66.5q-74 0 -107 -16t-94 -84z" /> +<glyph unicode="¡" horiz-adv-x="540" d="M70 -465q53 262 53 430v600h260l11 -194l12.5 -222.5t5.5 -195.5q0 -262 -39 -381zM74 844q0 68 56 114t130 46q72 0 125 -43t53 -107q0 -68 -57 -115t-131 -47q-70 0 -123 43t-53 109z" /> +<glyph unicode="¢" horiz-adv-x="843" d="M45 494q0 221 109 364q66 82 153.5 127t175.5 49v135h92v-141q74 -10 132.5 -42t80.5 -58l23 -27l-145 -158q-31 25 -91 39v-561q82 23 150 78l106 -190q-113 -98 -256 -132v-129h-92v121q-176 2 -303 142q-135 150 -135 383zM342 510q0 -115 41 -197t100 -98v573 q-53 -14 -97 -82.5t-44 -195.5z" /> +<glyph unicode="£" horiz-adv-x="905" d="M25 727l96 195v198q0 109 67.5 180.5t149.5 95t170 23.5q166 0 291 -100l-82 -211q-119 66 -197 66q-53 0 -77.5 -17.5t-24.5 -62.5v-172h260v-252h-260v-148q0 -96 -29 -268h55q70 -16 99 -31q38 -12 72 -11q114 0 202 122l60 -211q-2 -4 -9 -10l-25 -27 q-19 -20 -40 -35.5t-54.5 -36t-67.5 -29t-80 -15.5q-15 -2 -31 -2q-30 0 -60 9l-102 23h-375q88 422 88 532v154z" /> +<glyph unicode="¤" horiz-adv-x="1110" d="M-12 436l98 217h68v37l2 41h-156l104 219h80q43 205 200 334t394 129q125 0 213 -40t180 -124l-112 -235q-90 90 -148.5 118.5t-142.5 28.5q-217 0 -285 -211h547l-102 -219h-479q-4 -16 -5 -78h447l-105 -219h-305q55 -207 267 -207q53 0 83.5 8.5t91 55.5t144.5 145 v-321q-90 -80 -152.5 -110t-177.5 -30q-211 0 -362.5 123t-192.5 338h-194z" /> +<glyph unicode="¥" horiz-adv-x="974" d="M4 1290h330l153 -502l152 502h322l-179 -440h158v-232h-252l-47 -116h299v-232h-305v-270h-303v270h-291v232h283l-48 116h-235v232h141z" /> +<glyph unicode="§" horiz-adv-x="860" d="M20 -160l111 207q137 -76 274 -76q51 0 82 22.5t35 57.5q2 16 -17.5 35.5t-56 40t-68.5 35.5l-76 33q-44 18 -56 24q-104 51 -163.5 113.5t-59.5 159.5q0 92 55 172.5t143 105.5q-68 16 -129 79.5t-61 137.5q0 154 113.5 244t283.5 90t350 -109l-110 -196q-8 8 -26.5 18 t-81.5 26q-49 12 -105 12q-15 0 -31 -1q-98 -6 -98 -78q0 -8 10 -19t29 -22l39 -21q20 -11 42.5 -21.5t39.5 -17.5l32 -13l17 -7q55 -27 97 -52t87 -63t69.5 -85t24.5 -103q0 -90 -50 -173t-128 -111q74 -25 122 -83.5t48 -125.5q0 -164 -112.5 -261.5t-295.5 -97.5 q-100 0 -182 26.5t-197 96.5zM279 506q2 -31 35.5 -55.5t80.5 -24.5q100 0 135 35q33 33 33 87q0 35 -32.5 60t-88.5 25q-92 0 -130 -36q-34 -32 -34 -81q1 -5 1 -10z" /> +<glyph unicode="¨" horiz-adv-x="786" d="M43 1276q0 72 50 122t118 50q66 0 113 -47t47 -115q0 -33 -13 -63q-18 -49 -61 -79t-94 -30q-66 0 -113 47t-47 115zM410 1276q0 72 50 122t118 50q63 0 111 -47t48 -115q0 -33 -12 -63q-20 -49 -63.5 -79t-92.5 -30q-66 0 -112.5 47t-46.5 115z" /> +<glyph unicode="©" horiz-adv-x="1800" d="M242 500q0 178 86 329q86 150 243 240q164 88 328 88q170 0 328 -88q162 -94 243 -240q86 -152 86 -329q0 -184 -90 -334q-96 -160 -243 -240q-150 -86 -324 -86t-324 86q-147 80 -241 240q-92 160 -92 334zM406 500q0 -135 65 -254q70 -117 182 -180q111 -61 246 -64h6 q127 0 242 66.5t180.5 177t65.5 254.5q0 131 -66 245q-41 76 -110 132q-59 55 -148 88q-92 31 -170 30q-131 0 -248 -67q-115 -68 -180 -182.5t-65 -245.5zM559 514q0 158 96.5 266.5t259.5 108.5q66 0 131.5 -29t98.5 -57l33 -31l-121 -137l-17 12q-8 6 -17 10.5l-15.5 7.5 t-15.5 4t-14 3t-16 2h-15h-15h-14q-33 0 -71 -39t-38 -123q0 -80 31.5 -130t87.5 -50q82 0 162 65l96 -155q-14 -14 -40 -35t-95.5 -54.5t-128.5 -33.5q-158 0 -260.5 102.5t-102.5 292.5z" /> +<glyph unicode="ª" horiz-adv-x="774" d="M39 948q0 63 55 166l342 137v33q0 29 -26.5 45.5t-57.5 16.5q-94 0 -186 -72l-80 127q55 55 134 90t155 35q119 0 195.5 -57.5t76.5 -174.5v-405q0 -39 19 -39q14 0 22 6l35 -106q-70 -33 -121 -33q-57 0 -93 33.5t-42 89.5q-20 -55 -74.5 -89t-116.5 -34 q-237 0 -237 231zM250 952q0 -25 18.5 -40t42.5 -15q63 0 97 49t34 115v41l-178 -72q-14 -43 -14 -78z" /> +<glyph unicode="«" horiz-adv-x="1024" d="M72 512l280 432l207 -168l-147 -266l149 -266l-207 -164zM518 510l240 434l207 -168l-127 -262l129 -270l-207 -164z" /> +<glyph unicode="¬" horiz-adv-x="1095" d="M51 668v233h922v-545l-264 -53v365h-658z" /> +<glyph unicode="­" horiz-adv-x="618" d="M45 408v227h520v-227h-520z" /> +<glyph unicode="®" horiz-adv-x="1800" d="M242 500q0 178 86 329q86 150 243 240q164 88 328 88q170 0 328 -88q162 -94 243 -240q86 -152 86 -329q0 -184 -90 -334q-96 -160 -243 -240q-150 -86 -324 -86t-324 86q-147 80 -241 240q-92 160 -92 334zM406 500q0 -135 65 -254q70 -117 182 -180q111 -61 246 -64h6 q127 0 242 66.5t180.5 177t65.5 254.5q0 131 -66 245q-41 76 -110 132q-59 55 -148 88q-92 31 -170 30q-131 0 -248 -67q-115 -68 -180 -182.5t-65 -245.5zM651 129v760h219q139 0 230.5 -64.5t91.5 -197.5q0 -70 -40 -142.5t-101 -89.5l151 -123v-143h-176q-14 14 -143 172 l10 -172h-242zM893 535q6 -2 18 -3q61 0 62 80q0 66 -64 66l-16 -2v-141z" /> +<glyph unicode="¯" horiz-adv-x="686" d="M2 1143v182h680v-182h-680z" /> +<glyph unicode="°" horiz-adv-x="720" d="M49 1030q0 129 91 220t220 91t220.5 -91t91.5 -218q0 -129 -91.5 -220t-220.5 -91t-220 91t-91 218zM254 1034q0 -43 30.5 -73.5t73.5 -30.5t74 30.5t31 73.5t-31 74t-74 31t-73.5 -31t-30.5 -74z" /> +<glyph unicode="±" horiz-adv-x="915" d="M78 487v234h246v209h262v-209h243v-234h-243v-200h-262v200h-246zM86 0v231h737v-231h-737z" /> +<glyph unicode="²" d="M74 193q143 92 306 249.5t163 268.5q0 37 -29 54t-68 17q-72 0 -153.5 -32.5t-140.5 -79.5l-68 219q68 68 176.5 106.5t208.5 38.5q164 0 268.5 -78.5t104.5 -238.5q0 -127 -99.5 -248t-234.5 -215h360v-254h-751z" /> +<glyph unicode="³" d="M90 -133q72 2 148.5 19.5t151.5 50t123 94t48 139.5q0 39 -17.5 64.5t-51 36t-61.5 13.5t-66 3q-119 0 -238 -13v228l129 10q188 102 254 166q33 33 33 70q0 45 -49 45q-72 0 -308 -107l-71 203q92 55 208.5 99t198.5 44q131 0 222.5 -69.5t91.5 -194.5q0 -88 -59.5 -165 t-143.5 -116q96 -23 160.5 -102.5t64.5 -175.5q0 -147 -61.5 -258t-166 -172.5t-227 -91t-260.5 -29.5h-53v209z" /> +<glyph unicode="´" horiz-adv-x="692" d="M88 1284l115 -174q221 121 422 291l-205 203q-164 -193 -332 -320z" /> +<glyph unicode="µ" horiz-adv-x="1062" d="M27 -358q66 113 102 219q33 90 33 213q-51 76 -51 200v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119l-61 11q35 -45 34 -115q0 -39 -10 -94.5t-18 -92.5l-11 -36h-335z" /> +<glyph unicode="¶" horiz-adv-x="1112" d="M39 885q0 180 98.5 307t304.5 127q49 0 144.5 -16.5t126.5 -18.5l-8 86h249l4 -86l84 19v-222l-81 -22q-6 -330 -80 -682t-215 -625l-269 103q147 258 229.5 573t88.5 625l-16 5q-9 3 -36.5 11t-53 11t-57.5 2t-54 -9q-55 -23 -74 -109q-6 -30 -6 -59q0 -56 23 -107 q36 -79 116 -79h68q-10 -20 -28.5 -52t-81 -90.5t-132.5 -70.5q-26 -5 -51 -5q-41 0 -78 13q-59 20 -98 62.5t-67 98.5t-39 109.5t-11 100.5z" /> +<glyph unicode="·" horiz-adv-x="528" d="M80 520q0 72 56.5 122t129.5 50q70 0 123 -46t53 -116q0 -72 -57 -123t-129 -51q-70 0 -123 47t-53 117z" /> +<glyph unicode="¸" horiz-adv-x="823" d="M305 -274q193 66 201 110q6 39 -34 42q-6 0 -13 1q-34 0 -70 -14l-41 -17v152h301q111 -70 111 -162q0 -143 -287 -244z" /> +<glyph unicode="¹" d="M158 737v219q111 0 276 48h219v-1004h-293v737h-202z" /> +<glyph unicode="º" horiz-adv-x="802" d="M63 1120q0 154 89.5 279t253 125t254 -125t90.5 -279q0 -156 -90.5 -280.5t-254 -124.5t-253 124.5t-89.5 280.5zM289 1120q0 -102 31.5 -160.5t85.5 -58.5q55 0 85.5 58.5t30.5 161t-30.5 158.5t-85.5 56q-53 0 -85 -57.5t-32 -157.5z" /> +<glyph unicode="»" horiz-adv-x="1024" d="M63 244l207 -164l242 430l-240 434l-206 -168l127 -262zM469 244l207 -164l282 432l-280 432l-207 -168l147 -266z" /> +<glyph unicode="¼" horiz-adv-x="2537" d="M158 737v219q111 0 276 48h219v-1004h-293v737h-202zM698 -256l869 1769h288l-868 -1769h-289zM1659 201q188 414 489 803h271v-750h78v-254h-82v-283h-283v283h-461zM1935 254h193v348q-20 -25 -98.5 -164t-94.5 -184z" /> +<glyph unicode="½" horiz-adv-x="2537" d="M158 737v219q111 0 276 48h219v-1004h-293v737h-202zM698 -256l869 1769h288l-868 -1769h-289zM1690 193q143 92 306 249.5t163 268.5q0 37 -29 54t-68 17q-72 0 -153.5 -32.5t-141.5 -79.5l-67 219q68 68 176.5 106.5t208.5 38.5q164 0 268.5 -78.5t104.5 -238.5 q0 -127 -99.5 -248t-234.5 -215h360v-254h-751z" /> +<glyph unicode="¾" horiz-adv-x="2537" d="M90 -133q72 2 148.5 19.5t151.5 50t123 94t48 139.5q0 39 -17.5 64.5t-51 36t-61.5 13.5t-66 3q-119 0 -238 -13v228l129 10q188 102 254 166q33 33 33 70q0 45 -49 45q-72 0 -308 -107l-71 203q92 55 208.5 99t198.5 44q131 0 222.5 -69.5t91.5 -194.5q0 -88 -59.5 -165 t-143.5 -116q96 -23 160.5 -102.5t64.5 -175.5q0 -147 -61.5 -258t-166 -172.5t-227 -91t-260.5 -29.5h-53v209zM698 -256l869 1769h288l-868 -1769h-289zM1659 201q188 414 489 803h271v-750h78v-254h-82v-283h-283v283h-461zM1935 254h193v348q-20 -25 -98.5 -164 t-94.5 -184z" /> +<glyph unicode="¿" horiz-adv-x="733" d="M39 -117q0 125 111 242l129 137q90 96 90 221v111h249v-111q0 -121 -17 -193.5t-83 -139.5l-106 -111q-86 -90 -86 -152q0 -29 16 -43t32.5 -16t47.5 -2q90 0 186 27v-228q-184 -43 -276 -43q-137 0 -215 82t-78 219zM313 872.5q0 65.5 57.5 112.5t129 47t125 -43 t53.5 -108.5t-57.5 -112.5t-129 -47t-125 43t-53.5 108.5z" /> +<glyph unicode="À" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM246 1645l205 202q164 -193 331 -319l-114 -174q-221 121 -422 291zM428 506h193l-97 393z" /> +<glyph unicode="Á" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM264 1536l115 -174q221 121 422 291l-205 202q-164 -192 -332 -319zM428 506h193l-97 393z" /> +<glyph unicode="Â" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM115 1542l94 57q98 61 285 242q164 -164 284 -239l94 -58l-137 -186q-121 68 -241 188q-117 -117 -242 -188zM428 506h193l-97 393z" /> +<glyph unicode="Ã" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM129 1591q78 72 126 97.5t128 25.5q68 0 154 -33.5t112 -33.5q57 0 127 67l117 -137l-26 -26q-22 -22 -26 -25l-26 -22q-20 -17 -30 -22l-29 -17q-20 -11 -35.5 -14t-38.5 -7t-47 -4q-55 0 -138 33.5 t-120 33.5q-70 0 -144 -65zM428 506h193l-97 393z" /> +<glyph unicode="Ä" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM174 1559q0 72 50 122t118 50q63 0 110.5 -46.5t47.5 -115.5q0 -31 -11 -62q-20 -49 -63 -79.5t-94 -30.5q-66 0 -112 47t-46 115zM428 506h193l-97 393zM539 1559q0 72 50 122t118 50q66 0 112.5 -46.5 t46.5 -115.5q0 -33 -12 -62q-18 -49 -61 -79.5t-95 -30.5q-66 0 -112.5 47t-46.5 115z" /> +<glyph unicode="Å" horiz-adv-x="1044" d="M12 0l361 1290h301l362 -1290h-317l-66 266h-258l-65 -266h-318zM272 1595q0 104 74 179t180 75q104 0 179 -74.5t75 -179.5q0 -106 -74.5 -180t-179.5 -74q-106 0 -180 74t-74 180zM428 506h193l-97 393zM471 1595.5q0 -22.5 16.5 -39t39 -16.5t39 16.5t16.5 39 t-16.5 39t-39 16.5t-39 -16.5t-16.5 -39z" /> +<glyph unicode="Æ" horiz-adv-x="1497" d="M12 0l357 1290h307l98 -352v352h656v-262h-406v-225h295v-266h-295v-271h424v-266h-688l-80 289h-270l-80 -289h-318zM451 530h188l-94 369z" /> +<glyph unicode="Ç" horiz-adv-x="1017" d="M66 645q0 115 32.5 229.5t96 217t172 166t245.5 63.5q211 0 346 -94l7 -4l-113 -205q-96 47 -240 47q-43 0 -97 -39t-87 -106q-57 -106 -57 -283q0 -68 9 -130.5t32.5 -130t76 -108.5t127.5 -41q131 0 242 74l133 -203q-94 -80 -248 -114q86 -63 86 -146 q0 -143 -286 -244l-166 132q193 66 198 110q6 39 -33 42q-6 0 -13 1q-34 0 -70 -14l-41 -17v152h2q-96 35 -167 105.5t-111 161.5t-58 185.5t-18 192.5z" /> +<glyph unicode="È" horiz-adv-x="929" d="M106 0v1290h748v-262h-442v-225h340v-266h-340v-271h465v-266h-771zM215 1645l205 202q160 -193 332 -319l-115 -174q-215 117 -422 291z" /> +<glyph unicode="É" horiz-adv-x="929" d="M106 0v1290h748v-262h-442v-225h340v-266h-340v-271h465v-266h-771zM248 1536l114 -174q221 121 422 291l-204 202q-164 -192 -332 -319z" /> +<glyph unicode="Ê" horiz-adv-x="929" d="M92 1542l94 57q100 61 285 242q164 -164 285 -239l94 -58l-137 -186q-121 68 -242 188q-117 -117 -242 -188zM106 0v1290h748v-262h-442v-225h340v-266h-340v-271h465v-266h-771z" /> +<glyph unicode="Ë" horiz-adv-x="929" d="M106 0v1290h748v-262h-442v-225h340v-266h-340v-271h465v-266h-771zM139 1561q0 72 50.5 122t117.5 50q63 0 111.5 -47.5t48.5 -114.5q0 -35 -12 -62q-20 -49 -63.5 -79.5t-92.5 -30.5q-66 0 -113 47t-47 115zM506 1561q0 72 50 122t118 50q63 0 110 -47.5t47 -114.5 q0 -31 -10 -62q-20 -51 -63 -80.5t-94 -29.5q-66 0 -112 47t-46 115z" /> +<glyph unicode="Ì" horiz-adv-x="487" d="M-31 1645l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291zM92 0v1290h303v-1290h-303z" /> +<glyph unicode="Í" horiz-adv-x="487" d="M-20 1536l114 -174q221 121 422 291l-205 202q-163 -192 -331 -319zM92 0v1290h303v-1290h-303z" /> +<glyph unicode="Î" horiz-adv-x="487" d="M-137 1542l94 57q100 61 285 242q164 -164 284 -239l95 -58l-138 -186q-121 68 -241 188q-117 -117 -242 -188zM92 0v1290h303v-1290h-303z" /> +<glyph unicode="Ï" horiz-adv-x="487" d="M-98 1559q0 72 50 122t118 50q66 0 112.5 -47.5t46.5 -114.5q0 -33 -12 -62q-18 -51 -61 -80.5t-95 -29.5q-66 0 -112.5 47t-46.5 115zM92 0v1290h303v-1290h-303zM268 1559q0 72 50.5 122t117.5 50q66 0 113 -47.5t47 -114.5q0 -33 -12 -62q-18 -49 -62.5 -79.5 t-93.5 -30.5q-66 0 -113 47t-47 115z" /> +<glyph unicode="Ñ" horiz-adv-x="1134" d="M111 0v1290h329l224 -508q37 -80 55 -176v684h305v-1290h-281l-344 764q16 -82 17 -166v-598h-305zM178 1591q78 72 126 97.5t130 25.5q68 0 153 -33.5t111 -33.5q57 0 127 67l117 -137l-26 -26q-22 -22 -26 -25l-26 -22q-20 -17 -29 -22l-29 -17q-19 -11 -36 -14l-38 -7 q-22 -4 -46 -4q-55 0 -138 33.5t-120 33.5q-72 0 -145 -65z" /> +<glyph unicode="Ò" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM274 1645l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291z M367 645.5q0 -65.5 2 -113t13 -109t31.5 -99t57.5 -65.5t90 -28t90 28t57.5 67t32 100t13.5 108.5t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5t-2 -112.5z" /> +<glyph unicode="Ó" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM336 1536l115 -174q221 121 421 291l-204 202q-164 -192 -332 -319z M367 645.5q0 -65.5 2 -113t13 -109t31.5 -99t57.5 -65.5t90 -28t90 28t57.5 67t32 100t13.5 108.5t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5t-2 -112.5z" /> +<glyph unicode="Ô" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM160 1542l94 57q98 61 285 242q164 -164 284 -239l95 -58l-138 -186 q-121 68 -241 188q-117 -117 -242 -188zM367 645.5q0 -65.5 2 -113t13 -109t31.5 -99t57.5 -65.5t90 -28t90 28t57.5 67t32 100t13.5 108.5t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5t-2 -112.5z" /> +<glyph unicode="Õ" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM170 1591q78 72 126 97.5t130 25.5q68 0 154 -33.5t112 -33.5q25 0 55.5 16 t51.5 33l18 18l117 -137l-26 -26q-22 -22 -26 -25l-26 -22q-20 -17 -30 -22l-28 -17q-19 -11 -36 -14l-38 -7q-22 -4 -46 -4q-55 0 -138 33.5t-120 33.5q-72 0 -146 -65zM367 645.5q0 -65.5 2 -113t13 -108.5t31.5 -99t57.5 -66t90 -28t90 28t57.5 67t32 100t13.5 108.5 t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5t-2 -112.5z" /> +<glyph unicode="Ö" horiz-adv-x="1122" d="M61 645q0 115 27 227.5t82 216t156.5 168t234.5 64.5t234.5 -64.5t157 -168t82 -216t26.5 -227.5t-26.5 -227.5t-82 -216t-157 -168t-234.5 -64.5t-234.5 64.5t-156.5 168t-82 216t-27 227.5zM219 1561q0 72 50 122t118 50q66 0 113 -47.5t47 -114.5q0 -35 -12 -62 q-18 -49 -61.5 -79.5t-94.5 -30.5q-66 0 -113 47t-47 115zM367 645.5q0 -65.5 2 -113t13 -109t31.5 -99t57.5 -65.5t90 -28t90 28t57.5 67t32 100t13.5 108.5t2 110.5q0 66 -2 113t-13.5 107.5t-32 99.5t-57.5 66.5t-90 27.5t-90 -27.5t-57.5 -66.5t-31.5 -99.5t-13 -107.5 t-2 -112.5zM586 1561q0 72 50 122t118 50q63 0 111 -47.5t48 -114.5q0 -35 -12 -62q-20 -49 -63 -79.5t-95 -30.5q-66 0 -111.5 47t-45.5 115z" /> +<glyph unicode="Ø" horiz-adv-x="1122" d="M61 653q0 113 28 223.5t83 214t156.5 167t232.5 63.5q74 0 133 -18l68 231h102l-78 -272q139 -84 207 -258.5t68 -356.5q0 -113 -26.5 -225.5t-82 -217t-157 -170t-234.5 -65.5q-72 0 -139 21l-80 -277h-102l92 320q-137 86 -204 262t-67 358zM367 635q0 -217 49 -311 l207 725q-31 10 -62 10q-66 0 -107.5 -39t-59 -110.5t-22.5 -131t-5 -143.5zM494 244q31 -12 67 -13q45 0 79 18.5t54.5 53.5t33.5 72t18.5 90t7.5 91t2 91q0 227 -54 324z" /> +<glyph unicode="Ù" horiz-adv-x="1142" d="M109 514v776h305v-776q0 -147 37.5 -215t119.5 -68t120 68t38 215v776h305v-776q0 -104 -23.5 -195.5t-73.5 -172t-144.5 -129t-221.5 -48.5t-221 48.5t-144 129t-73.5 172t-23.5 195.5zM297 1645l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291z" /> +<glyph unicode="Ú" horiz-adv-x="1142" d="M109 514v776h305v-776q0 -147 37.5 -215t119.5 -68t120 68t38 215v776h305v-776q0 -104 -23.5 -195.5t-73.5 -172t-144.5 -129t-221.5 -48.5t-221 48.5t-144 129t-73.5 172t-23.5 195.5zM319 1536l115 -174q221 121 422 291l-205 202q-164 -192 -332 -319z" /> +<glyph unicode="Û" horiz-adv-x="1142" d="M109 514v776h305v-776q0 -147 37.5 -215t119.5 -68t120 68t38 215v776h305v-776q0 -104 -23.5 -195.5t-73.5 -172t-144.5 -129t-221.5 -48.5t-221 48.5t-144 129t-73.5 172t-23.5 195.5zM170 1542l94 57q100 61 285 242q164 -164 285 -239l94 -58l-137 -186 q-121 68 -242 188q-117 -117 -242 -188z" /> +<glyph unicode="Ü" horiz-adv-x="1142" d="M109 514v776h305v-776q0 -147 37.5 -215t119.5 -68t120 68t38 215v776h305v-776q0 -104 -23.5 -195.5t-73.5 -172t-144.5 -129t-221.5 -48.5t-221 48.5t-144 129t-73.5 172t-23.5 195.5zM231 1560.5q0 69.5 50.5 121t117.5 51.5q63 0 110.5 -47.5t47.5 -114.5 q0 -29 -10 -64q-20 -49 -63.5 -79.5t-94.5 -30.5q-66 0 -112 47t-46 116.5zM596 1561q0 70 50 121t118 51q66 0 113 -47.5t47 -114.5q0 -33 -13 -64q-18 -49 -61 -79.5t-94 -30.5q-66 0 -113 48t-47 116z" /> +<glyph unicode="Ý" horiz-adv-x="964" d="M4 1290h326l151 -502l154 502h326l-326 -807v-483h-305l2 483zM367 1618l114 -174q221 121 422 291l-205 202q-163 -192 -331 -319z" /> +<glyph unicode="ß" horiz-adv-x="1171" d="M27 786v218h88v108q0 106 66.5 184t157.5 113t191 35q207 0 333 -105.5t126 -279.5q0 -51 -20.5 -99.5t-44 -81t-39.5 -68.5q-10 -22 -10 -47q0 -15 4 -32q10 -43 55 -94l96 -115q55 -66 79 -120t24 -130q0 -127 -100.5 -215t-262.5 -88q-176 2 -283 80l101 232 q35 -23 85 -42.5t107 -20.5h4q55 0 80 32q16 19 16 45q0 51 -63 129l-88 104q-101 118 -101 211q0 9 1 17q4 33 31.5 72.5t52 92t24.5 121.5q0 82 -45 119t-149 37q-55 0 -93 -36t-38 -91v-1073h-297v768z" /> +<glyph unicode="à" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM213 1393l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102z" /> +<glyph unicode="á" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM217 1284l115 -174q223 121 422 291l-205 203q-160 -193 -332 -320zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102z" /> +<glyph unicode="â" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM70 1290l94 58q100 61 285 241q164 -166 284 -239l94 -58l-137 -186q-121 68 -241 188q-117 -117 -242 -188zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102z" /> +<glyph unicode="ã" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM86 1339q78 72 126 97.5t130 25.5q68 0 153 -33.5t111 -33.5q57 0 127 67l117 -137l-26 -26q-22 -22 -26 -25l-26 -22q-20 -17 -30 -22l-28 -17q-19 -11 -36 -14l-38 -7q-22 -4 -46 -4q-55 0 -138 33.5t-120 33.5q-72 0 -146 -65zM315 279 q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102z" /> +<glyph unicode="ä" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM137 1276q0 72 50.5 122t117.5 50q63 0 111.5 -47t48.5 -115q0 -23 -12 -63q-20 -49 -63.5 -79t-94.5 -30q-66 0 -112 47t-46 115zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102zM502 1276q0 72 50 122 t118 50q66 0 112.5 -47t46.5 -115q0 -29 -10 -63q-20 -49 -63 -79t-95 -30q-66 0 -112.5 47t-46.5 115z" /> +<glyph unicode="å" horiz-adv-x="954" d="M37 274q0 82 74 218l450 180v43q0 39 -34.5 60.5t-75.5 21.5q-127 0 -248 -95l-105 168q76 72 179.5 118t201.5 46q158 0 258.5 -75.5t100.5 -227.5v-534q0 -53 24 -54q12 0 29 9l45 -140q-86 -43 -158 -43q-78 0 -123 45t-55 117q-27 -74 -98.5 -118t-151.5 -44 q-313 0 -313 305zM223 1364q0 104 74 179t180 75q104 0 179 -75t75 -179t-74.5 -179t-179.5 -75q-106 0 -180 75t-74 179zM315 279q0 -31 23.5 -51.5t56.5 -20.5q84 0 128 63.5t44 151.5v55l-233 -96q-19 -55 -19 -102zM422 1363.5q0 -22.5 16.5 -38.5t39 -16t38.5 16 t16 38.5t-16 39t-38.5 16.5t-39 -16.5t-16.5 -39z" /> +<glyph unicode="æ" horiz-adv-x="1511" d="M37 274q0 82 74 218l450 180q0 59 -22.5 92t-87.5 33q-127 0 -248 -95l-105 168q84 70 144 103q106 61 237 61q88 0 163 -30.5t110 -81.5q45 51 123.5 81.5t168.5 30.5q184 0 291 -128t111 -267l4 -164h-586q-1 -17 -1 -33q0 -82 34 -149q41 -80 123 -80q156 2 280 88 l86 -184q-20 -16 -55 -41t-135 -67t-188 -42q-113 0 -209.5 62.5t-134.5 142.5q-59 -84 -142.5 -143.5t-171.5 -59.5q-84 0 -112 8q-104 31 -151.5 113t-49.5 184zM317 279q0 -72 68 -72q182 0 182 262l-231 -100q-19 -47 -19 -90zM887 672h278q-2 59 -32.5 112.5 t-102.5 53.5q-59 0 -96 -54.5t-47 -111.5z" /> +<glyph unicode="ç" horiz-adv-x="843" d="M45 494q0 221 109 364q68 86 162 131t184 45q98 0 176 -33.5t106 -66.5l29 -33l-145 -158q-57 47 -164 48q-53 0 -106.5 -72t-53.5 -209q0 -127 50 -213t116 -86q49 0 103.5 21.5t84.5 43.5l29 23l106 -190q-84 -74 -200 -115q102 -66 102 -156q0 -143 -287 -244 l-167 132q193 66 200 110q6 39 -34 42q-6 0 -12 1q-34 0 -68 -14l-43 -17v152h16q-94 41 -158 111q-135 150 -135 383z" /> +<glyph unicode="è" horiz-adv-x="960" d="M47 524q0 211 135 360.5t344 149.5q162 0 279 -116.5t121 -278.5q4 -111 4 -164h-584q-2 -10 -2 -33q0 -86 38 -157.5t118 -71.5q162 0 282 88l86 -184q-76 -63 -182 -106.5t-197 -43.5q-150 0 -253 84t-146 206t-43 267zM231 1393l205 202q160 -193 332 -319l-115 -174 q-221 121 -422 291zM367 664h284q-2 68 -39 121t-100 53q-57 0 -98 -56.5t-47 -117.5z" /> +<glyph unicode="é" horiz-adv-x="960" d="M47 524q0 211 135 360.5t344 149.5q162 0 279 -116.5t121 -278.5q4 -111 4 -164h-584q-2 -10 -2 -33q0 -86 38 -157.5t118 -71.5q162 0 282 88l86 -184q-76 -63 -182 -106.5t-197 -43.5q-150 0 -253 84t-146 206t-43 267zM272 1284l115 -174q221 121 422 291l-205 203 q-160 -193 -332 -320zM367 664h284q-2 68 -39 121t-100 53q-57 0 -98 -56.5t-47 -117.5z" /> +<glyph unicode="ê" horiz-adv-x="960" d="M47 524q0 211 135 360.5t344 149.5q162 0 279 -116.5t121 -278.5q4 -111 4 -164h-584q-2 -10 -2 -33q0 -86 38 -157.5t118 -71.5q162 0 282 88l86 -184q-76 -63 -182 -106.5t-197 -43.5q-150 0 -253 84t-146 206t-43 267zM104 1290l95 58q98 61 284 241 q164 -166 285 -239l94 -58l-137 -186q-121 68 -242 188q-117 -117 -241 -188zM367 664h284q-2 68 -39 121t-100 53q-57 0 -98 -56.5t-47 -117.5z" /> +<glyph unicode="ë" horiz-adv-x="960" d="M47 524q0 211 135 360.5t344 149.5q162 0 279 -116.5t121 -278.5q4 -111 4 -164h-584q-2 -10 -2 -33q0 -86 38 -157.5t118 -71.5q162 0 282 88l86 -184q-76 -63 -182 -106.5t-197 -43.5q-150 0 -253 84t-146 206t-43 267zM170 1276q0 72 50 122t118 50q66 0 113 -47 t47 -115q0 -33 -13 -63q-18 -49 -61 -79t-94 -30q-66 0 -113 47t-47 115zM367 664h284q-2 68 -39 121t-100 53q-57 0 -98 -56.5t-47 -117.5zM537 1276q0 72 50 122t118 50q66 0 112.5 -47t46.5 -115q0 -33 -12 -63q-18 -49 -62.5 -79t-93.5 -30q-66 0 -112.5 47t-46.5 115z " /> +<glyph unicode="ì" horiz-adv-x="487" d="M-29 1393l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291zM96 0v1004h293v-1004h-293z" /> +<glyph unicode="í" horiz-adv-x="487" d="M-20 1284l114 -174q221 121 422 291l-205 203q-159 -193 -331 -320zM96 0v1004h293v-1004h-293z" /> +<glyph unicode="î" horiz-adv-x="487" d="M-158 1290l95 58q100 61 284 241q164 -166 285 -239l94 -58l-137 -186q-121 68 -242 188q-117 -117 -241 -188zM96 0v1004h293v-1004h-293z" /> +<glyph unicode="ï" horiz-adv-x="487" d="M-98 1276q0 72 50 122t118 50q63 0 110 -47t47 -115q0 -29 -10 -63q-20 -49 -63 -79t-95 -30q-66 0 -111.5 47t-45.5 115zM96 0v1004h293v-1004h-293zM266 1276q0 72 50.5 122t117.5 50q66 0 113 -47t47 -115q0 -33 -12 -63q-18 -49 -61.5 -79t-94.5 -30q-66 0 -113 47 t-47 115z" /> +<glyph unicode="ñ" horiz-adv-x="1062" d="M113 0v1004h297v-74q147 104 233 104q94 0 158.5 -26.5t96.5 -73.5t44 -96.5t12 -112.5v-725h-297v725q0 63 -51 63q-33 0 -64.5 -9t-47 -17t-47.5 -30l-37 -25v-707h-297zM158 1339q78 72 126 97.5t128 25.5q70 0 154.5 -33.5t111.5 -33.5q57 0 127 67l117 -137l-26 -26 q-22 -22 -27 -25l-25 -22q-20 -17 -30 -22l-30 -17q-20 -11 -35.5 -14t-38 -7t-46.5 -4q-55 0 -137.5 33.5t-121.5 33.5q-70 0 -143 -65z" /> +<glyph unicode="ò" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM227 1393l205 202q164 -193 332 -319l-115 -174q-221 121 -422 291zM346 502q0 -133 41 -210t112.5 -77t112.5 77t41 210 t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5z" /> +<glyph unicode="ó" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM229 1284l115 -174q221 121 422 291l-205 203q-160 -193 -332 -320zM346 502q0 -133 41 -210t112.5 -77t112.5 77t41 210 t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5z" /> +<glyph unicode="ô" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM94 1290l94 58q100 61 285 241q164 -166 285 -239l94 -58l-137 -186q-121 68 -242 188q-117 -117 -242 -188zM346 502 q0 -133 41 -210t112.5 -77t112.5 77t41 210t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5z" /> +<glyph unicode="õ" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM117 1339q78 72 126 97.5t128 25.5q68 0 153.5 -33.5t112.5 -33.5q57 0 127 67l117 -137l-26 -26q-22 -22 -27 -25l-25 -22 q-20 -17 -30 -22l-30 -17q-20 -11 -35.5 -14t-38 -7t-46.5 -4q-55 0 -138.5 33.5t-119.5 33.5q-70 0 -144 -65zM346 502q0 -133 41 -210t112.5 -77t112.5 77t41 210t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5z" /> +<glyph unicode="ö" horiz-adv-x="999" d="M49 502q0 127 45 243.5t151.5 202.5t254 86t254 -86t151.5 -202.5t45 -243.5t-45 -244t-151.5 -203t-254 -86t-254 86t-151.5 203t-45 244zM156 1276q0 72 50 122t118 50q63 0 110 -47t47 -115q0 -29 -10 -63q-20 -49 -63 -79t-95 -30q-66 0 -111.5 47t-45.5 115z M346 502q0 -133 41 -210t112.5 -77t112.5 77t41 210t-41 208.5t-112.5 75.5t-112.5 -75.5t-41 -208.5zM520 1276q0 72 50.5 122t117.5 50q66 0 113 -47t47 -115q0 -33 -12 -63q-18 -49 -61.5 -79t-94.5 -30q-66 0 -113 47t-47 115z" /> +<glyph unicode="÷" horiz-adv-x="1001" d="M55 393v244h889v-244h-889zM301 160q0 74 59.5 126t139.5 52q76 0 128 -45t52 -115q0 -74 -59.5 -126t-139.5 -52q-76 0 -128 45t-52 115zM301 856q0 74 59.5 126t139.5 52q76 0 128 -45t52 -115q0 -74 -59.5 -126t-139.5 -52q-76 0 -128 45t-52 115z" /> +<glyph unicode="ø" horiz-adv-x="999" d="M49 500q0 94 27.5 185t80 171t141.5 129t202 49q49 0 96 -10l47 162h102l-55 -195q127 -63 193.5 -202.5t66.5 -288.5q0 -94 -27.5 -185.5t-79.5 -170t-141.5 -127t-201.5 -48.5q-47 0 -99 11l-61 -213h-102l69 245q-125 63 -191.5 200.5t-66.5 287.5zM346 500 q0 -133 41 -209l141 493q-10 2 -28 2q-72 0 -113 -76.5t-41 -209.5zM469 219q20 -4 31 -4q72 0 112.5 76t40.5 209q0 139 -43 213z" /> +<glyph unicode="ù" horiz-adv-x="1058" d="M111 274v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119q-317 0 -317 305zM252 1393l205 202q164 -193 331 -319l-114 -174q-221 121 -422 291z" /> +<glyph unicode="ú" horiz-adv-x="1058" d="M111 274v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119q-317 0 -317 305zM262 1284l115 -174q215 117 422 291l-205 203q-160 -193 -332 -320z" /> +<glyph unicode="û" horiz-adv-x="1058" d="M111 274v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119q-317 0 -317 305zM121 1290l94 58q98 61 285 241q162 -166 284 -239l95 -58l-138 -186q-121 68 -241 188q-117 -117 -242 -188z" /> +<glyph unicode="ü" horiz-adv-x="1058" d="M111 274v730h297v-730q0 -35 15 -48t50 -13q66 0 182 80v711h297v-795q0 -51 52 -84l-95 -156q-121 14 -194 119q-162 -119 -287 -119q-317 0 -317 305zM190 1276q0 72 50.5 122t117.5 50q66 0 113 -47t47 -115q0 -33 -12 -63q-18 -49 -61.5 -79t-94.5 -30q-66 0 -113 47 t-47 115zM557 1276q0 72 50 122t118 50q63 0 111.5 -47t48.5 -115q0 -33 -13 -63q-20 -49 -63 -79t-92 -30q-66 0 -113 47t-47 115z" /> +<glyph unicode="ý" horiz-adv-x="929" d="M18 1004h316q137 -459 149 -658q78 283 125 658h305q-29 -154 -70.5 -323t-106 -380t-152.5 -376t-181 -226q-37 -25 -96 -46.5t-102 -29.5l-41 -10l-68 217q72 25 113 50.5t57.5 46t46.5 73.5l13 20zM350 1331l115 -174q221 121 422 291l-205 203q-164 -193 -332 -320z " /> +<glyph unicode="ÿ" horiz-adv-x="929" d="M18 1004h316q137 -459 149 -658q78 283 125 658h305q-29 -154 -70.5 -323t-106 -380t-152.5 -376t-181 -226q-37 -25 -96 -46.5t-102 -29.5l-41 -10l-68 217q72 25 113 50.5t57.5 46t46.5 73.5l13 20zM139 1276q0 72 50.5 122t117.5 50q66 0 113 -47t47 -115 q0 -33 -12 -63q-18 -49 -61.5 -79t-94.5 -30q-66 0 -113 47t-47 115zM506 1276q0 72 50 122t118 50q66 0 113 -47t47 -115q0 -33 -13 -63q-18 -49 -62 -79t-93 -30q-66 0 -113 47t-47 115z" /> +<glyph unicode="Œ" horiz-adv-x="1290" d="M61 653q0 102 33 210t93.5 205t164 159.5t230.5 62.5h663v-262h-432v-225h322v-266h-322v-271h453v-266h-684q-129 0 -232.5 63.5t-164 164t-92.5 211t-32 214.5zM348 635q0 -113 43 -234.5t117 -152.5v794q-66 -18 -113 -155t-47 -252z" /> +<glyph unicode="œ" horiz-adv-x="1558" d="M63 500q0 94 28 185t80 171t141 129t202 49q170 0 289 -112q123 113 307 112.5t290.5 -128.5t110.5 -267l5 -164h-586l-2 -33q0 -82 35 -149q41 -80 122 -80q156 2 281 88l86 -184q-20 -16 -55 -41t-135.5 -67t-188.5 -42q-162 0 -278 107q-119 -104 -281 -105 q-113 0 -202 48.5t-141 127t-80 170t-28 185.5zM360 500q0 -133 41 -209t113 -76t113 76t41 209t-41 209.5t-113 76.5t-113 -76.5t-41 -209.5zM952 672h279q-2 59 -33 112.5t-102 53.5q-59 0 -96.5 -54.5t-47.5 -111.5z" /> +<glyph unicode="Ÿ" horiz-adv-x="964" d="M4 1290h326l151 -502l154 502h326l-328 -807l2 -483h-305v483zM139 1559q0 72 50.5 122t117.5 50q66 0 113 -47.5t47 -114.5q0 -33 -12 -62q-18 -49 -62.5 -79.5t-93.5 -30.5q-66 0 -113 47t-47 115zM506 1559q0 72 50 122t118 50q63 0 111.5 -47.5t48.5 -114.5 q0 -25 -13 -62q-20 -51 -63 -80.5t-94 -29.5q-66 0 -112 47t-46 115z" /> +<glyph unicode="ˆ" horiz-adv-x="823" d="M33 1290l94 58q98 61 285 241q164 -166 284 -239l95 -58l-138 -186q-121 68 -241 188q-117 -117 -242 -188z" /> +<glyph unicode="˜" horiz-adv-x="778" d="M0 1339q78 72 126 97.5t128 25.5q70 0 155 -33.5t111 -33.5q57 0 127 67l117 -137l-26 -26q-22 -22 -26 -25l-26 -22q-20 -17 -30 -22l-28 -17q-19 -11 -36 -14l-38 -7q-22 -4 -46 -4q-57 0 -139 33.5t-119 33.5q-72 0 -146 -65z" /> +<glyph unicode="–" horiz-adv-x="733" d="M-41 430v184h815v-184h-815z" /> +<glyph unicode="—" horiz-adv-x="1245" d="M-41 432v184h1327v-184h-1327z" /> +<glyph unicode="‘" horiz-adv-x="546" d="M35 985q154 289 309 428l145 -98q-117 -147 -116 -248q0 -2 18 -156q-90 -68 -162 -67q-47 0 -101 45t-93 96z" /> +<glyph unicode="’" horiz-adv-x="552" d="M55 891q115 152 115 248q0 25 -16 155q84 68 161 68q45 0 101.5 -46t93.5 -95q-158 -293 -309 -428z" /> +<glyph unicode="‚" horiz-adv-x="528" d="M27 -229q115 152 114 247q0 18 -16 156q90 68 162 68q45 0 101 -46t93 -96q-154 -289 -309 -428z" /> +<glyph unicode="“" horiz-adv-x="913" d="M29 985q160 291 309 428l145 -98q-115 -152 -114 -248q0 -29 7 -84l9 -72q-90 -68 -162 -67q-81 0 -194 141zM406 985q154 289 309 428l145 -98q-115 -152 -115 -248q0 -18 17 -156q-90 -68 -162 -67q-45 0 -100 45t-94 96z" /> +<glyph unicode="”" horiz-adv-x="913" d="M57 891q117 147 117 248q0 8 -18 155q88 68 161 68q47 0 102.5 -45t92.5 -96q-158 -293 -309 -428zM434 891q43 53 79 124.5t36 123.5q0 25 -17 155q84 68 162 68q47 0 102.5 -45t92.5 -96q-158 -293 -309 -428z" /> +<glyph unicode="„" horiz-adv-x="913" d="M41 -223q115 152 115 248q0 18 -17 155q90 68 162 68q45 0 100.5 -45t94.5 -97q-154 -289 -310 -428zM420 -223q115 152 115 248q0 18 -17 155q90 68 162 68q82 0 194 -142q-78 -143 -147.5 -244.5t-161.5 -183.5z" /> +<glyph unicode="•" horiz-adv-x="868" d="M29 649q0 168 117.5 286t285.5 118t286 -118t118 -286t-118 -285.5t-286 -117.5t-285.5 117.5t-117.5 285.5z" /> +<glyph unicode="…" horiz-adv-x="1236" d="M41 131q0 72 56.5 122t129.5 50q70 0 123 -46t53 -116q0 -72 -56 -122t-130 -50q-70 0 -123 46t-53 116zM434 131q0 72 57.5 122t131.5 50q70 0 123 -46t53 -116q0 -72 -56.5 -122t-130.5 -50q-72 0 -125 46t-53 116zM831 131q0 72 57.5 122t131.5 50q70 0 123 -46 t53 -116q0 -72 -56.5 -122t-129.5 -50q-72 0 -125.5 46t-53.5 116z" /> +<glyph unicode="‹" horiz-adv-x="614" d="M33 512l352 432l207 -168l-199 -262l201 -270l-207 -164z" /> +<glyph unicode="›" horiz-adv-x="614" d="M31 244l207 -164l354 432l-352 432l-207 -168l198 -262z" /> +<glyph unicode="™" horiz-adv-x="1642" d="M147 1237v170h496v-170h-154v-643h-190v643h-152zM680 596v813h211l178 -494l21 -86q6 51 18 86l180 494h211v-813h-192v356l10 99l-154 -455h-147l-154 455l10 -99v-356h-192z" /> +<glyph unicode="" horiz-adv-x="1004" d="M0 1005h1005v-1005h-1005v1005z" /> +<glyph unicode="fi" horiz-adv-x="1069" d="M27 797v207h90v94q0 156 131 250q125 90 319 105q40 3 77 3q143 0 239 -47q117 -53 116 -152q0 -33 -12 -63q-20 -49 -69.5 -79t-104.5 -30q-72 0 -123 44.5t-53 107.5q-15 4 -31 4q-51 0 -112 -40q-80 -52 -80 -116v-81h145v-238h-145v-766h-297v776zM672 0v1004h293 v-1004h-293z" /> +<glyph unicode="fl" horiz-adv-x="1069" d="M27 797v207h90v126q0 98 41 168t104.5 102t139.5 50q60 14 118 14q16 0 32 -1q73 -5 132 -7l281 -33v-987q0 -117 14 -225.5t29 -159.5l14 -51h-272q-78 127 -78 436v793h-88q-170 0 -170 -111v-114h145v-238h-145v-766h-297v776z" /> +<glyph unicode="ffi" horiz-adv-x="1658" d="M16 791v213h101v94q0 96 43 167.5t104.5 104.5t123 52.5t104.5 21.5l43 2l45 -211q-6 -2 -17.5 -4t-40 -13.5t-50 -26.5t-40 -43t-18.5 -63v-81h153v-240h-153v-764h-297v774zM602 791v213h100v94q0 96 43 167.5t104.5 104.5t123 52.5t104.5 21.5l43 2l45 -211 q-6 -2 -17 -4t-40 -13.5t-50.5 -26.5t-40 -43t-18.5 -63v-81h154v-240h-154v-764h-297v774zM1233 1268q0 72 57.5 123t129 51t124.5 -47t53 -117q0 -74 -56 -123t-130 -49q-72 0 -125 46t-53 116zM1268 0v1004h293v-1004h-293z" /> +<glyph unicode="ffl" horiz-adv-x="1654" d="M16 791v213h101v94q0 96 43 167.5t104.5 104.5t123 52.5t104.5 21.5l43 2l45 -211q-6 -2 -17.5 -4t-40 -13.5t-50 -26.5t-40 -43t-18.5 -63v-81h153v-240h-153v-764h-297v774zM602 791v213h100v94q0 96 43 167.5t104.5 104.5t123 52.5t104.5 21.5l43 2l45 -211 q-6 -2 -17 -4t-40 -13.5t-50.5 -26.5t-40 -43t-18.5 -63v-81h154v-240h-154v-764h-297v774zM1255 436v987h293v-987q0 -117 16.5 -225.5t32.5 -159.5l15 -51h-287q-70 157 -70 436z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-heavy-webfont.ttf b/app/assets/fonts/delicious-heavy-webfont.ttf Binary files differnew file mode 100755 index 000000000..deba1f94f --- /dev/null +++ b/app/assets/fonts/delicious-heavy-webfont.ttf diff --git a/app/assets/fonts/delicious-heavy-webfont.woff b/app/assets/fonts/delicious-heavy-webfont.woff Binary files differnew file mode 100755 index 000000000..b3c48915d --- /dev/null +++ b/app/assets/fonts/delicious-heavy-webfont.woff diff --git a/app/assets/fonts/delicious-italic-webfont.eot b/app/assets/fonts/delicious-italic-webfont.eot Binary files differnew file mode 100755 index 000000000..8bc7f8ce0 --- /dev/null +++ b/app/assets/fonts/delicious-italic-webfont.eot diff --git a/app/assets/fonts/delicious-italic-webfont.svg b/app/assets/fonts/delicious-italic-webfont.svg new file mode 100755 index 000000000..3cad32124 --- /dev/null +++ b/app/assets/fonts/delicious-italic-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : 40 I1995 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousItalic" horiz-adv-x="921" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="	" horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="!" horiz-adv-x="516" d="M90 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM182 332l82 704q20 174 92 375l160 21q-70 -209 -98 -396l-113 -704h-123z" /> +<glyph unicode=""" horiz-adv-x="585" d="M188 999l9 322l47 129h100l8 -129l-96 -324zM471 999l8 322l47 129h101l8 -129l-96 -324z" /> +<glyph unicode="#" horiz-adv-x="1138" d="M125 436l20 135h164l39 312h-145l20 135h142l51 403h123l-52 -403h236l45 364h123l-45 -364h174l-12 -135h-181l-39 -312h154l-12 -135h-158l-53 -418h-123l53 418h-235l-47 -379h-123l47 379h-166zM432 571h234l39 312h-234z" /> +<glyph unicode="$" horiz-adv-x="880" d="M45 57l66 121q133 -74 249 -80l76 484l-47 32q-41 29 -49 36t-41 33t-42 39t-30.5 39.5t-29 49.5t-14.5 53.5t-7 63.5q0 162 107.5 271.5t269.5 119.5l18 113h93l-19 -115q141 -14 236 -98l-74 -111q-72 59 -182 78l-74 -467q133 -94 168 -135q74 -82 74 -197 q0 -158 -101.5 -271.5t-257.5 -140.5l-20 -124h-92l18 118q-160 4 -295 88zM340 958q0 -41 21.5 -76.5t42 -55t63.5 -50.5l65 412q-86 -14 -139 -78t-53 -152zM455 113q76 25 126 90t50 143q0 98 -111 178z" /> +<glyph unicode="%" horiz-adv-x="1431" d="M94 1048.5q0 112.5 80 192.5t193 80q80 0 145 -41q82 -23 143 -23q57 0 114.5 15.5t86.5 30.5l27 16l104 -29l-455 -1290h-135l432 1196q-35 -18 -88 -27.5t-90 -9.5l-35 -2q23 -43 23 -108q0 -113 -80 -193t-192.5 -80t-192.5 80t-80 192.5zM215 1048.5 q0 -63.5 44 -107.5t107.5 -44t107.5 44t44 107.5t-44 107.5t-107.5 44t-107.5 -44t-44 -107.5zM788 241.5q0 112.5 80 192.5t193 80t192.5 -80t79.5 -192.5t-79.5 -192.5t-192.5 -80t-193 80t-80 192.5zM909 241.5q0 -63.5 44 -107.5t107.5 -44t107.5 44t44 107.5t-44 107.5 t-107.5 44t-107.5 -44t-44 -107.5z" /> +<glyph unicode="&" horiz-adv-x="1800" d="M117 584q0 260 189.5 462.5t449.5 245.5l18 -114q-197 -37 -343 -207t-146 -369q0 -74 22 -151l311 254q10 10 71 58l96 79q36 31 93.5 85t91.5 95t60.5 89t26.5 85q0 53 -56.5 100t-117.5 70l39 84q109 -23 195.5 -92.5t86.5 -167.5q0 -20 -2 -29q-4 -27 -19.5 -58.5 t-42.5 -66.5l-55 -66q-27 -32 -68 -72l-72 -68q-30 -28 -77 -67l-69 -58l-67 -54q-44 -35 -50 -41l-330 -268q150 -260 475 -260q172 0 328 83t253 231.5t97 322.5q0 92 -35 172h-180l-14 72q70 41 176 41q119 0 165 -79t46 -220q0 -211 -129 -383t-322.5 -262.5 t-398.5 -90.5q-285 0 -490.5 168t-205.5 447z" /> +<glyph unicode="'" horiz-adv-x="303" d="M188 999l9 322l47 129h100l8 -129l-96 -324z" /> +<glyph unicode="(" horiz-adv-x="716" d="M139 485q0 338 185.5 637t484.5 467l-2 -121q-248 -145 -401.5 -418.5t-153.5 -564.5q0 -203 84 -379q43 -92 83 -141t130 -129l-64 -82q-346 270 -346 731z" /> +<glyph unicode=")" horiz-adv-x="696" d="M-25 -166q106 80 168 134.5t129 137.5q256 322 256 715q0 197 -73.5 371t-221.5 278l33 119q186 -129 283.5 -326.5t97.5 -428.5q0 -383 -244 -728q-139 -199 -385 -364z" /> +<glyph unicode="*" horiz-adv-x="808" d="M86 1128l49 121l260 -100l-41 233h246l-106 -247l272 112l31 -117l-281 -106l150 -315h-136l-112 239l-201 -239l-115 34l246 291z" /> +<glyph unicode="+" horiz-adv-x="964" d="M92 451v122h330v355h119v-355h331v-122h-331v-355h-119v355h-330z" /> +<glyph unicode="," horiz-adv-x="528" d="M-45 -207q181 195 181 329q0 11 -1 21q49 23 90 23q68 0 127 -113q-111 -170 -313 -336z" /> +<glyph unicode="-" horiz-adv-x="618" d="M113 461v123h393v-123h-393z" /> +<glyph unicode="." horiz-adv-x="528" d="M82 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="/" horiz-adv-x="755" d="M-98 -303l823 1853h127l-823 -1853h-127z" /> +<glyph unicode="0" d="M49 375q0 109 33 222.5t93.5 212.5t158.5 161.5t215 62.5q164 0 243.5 -110.5t79.5 -280.5q0 -152 -51 -301t-166.5 -261t-273.5 -112q-164 0 -248 118t-84 288zM199 391q0 -119 47 -208t153 -89q109 0 187 98.5t107.5 215t29.5 221.5q0 279 -195 278q-104 0 -183 -93 t-112.5 -208t-33.5 -215z" /> +<glyph unicode="1" d="M238 856l16 98q88 0 199 29l75 18h142l-158 -1001h-152l136 856h-258z" /> +<glyph unicode="2" d="M96 127q195 98 383 293q78 80 142.5 179t64.5 177q0 66 -44 96.5t-112 30.5q-100 0 -260 -98l-51 100q100 72 175 100.5t184 28.5q274 0 274 -256q0 -117 -92 -251t-205 -231t-225 -161h456l-22 -135h-660z" /> +<glyph unicode="3" d="M57 -225q92 2 188.5 29.5t182.5 78.5t141.5 134t55.5 182q0 154 -244 153q-74 0 -166 -10l-8 117q10 2 28.5 4t33.5 4l30 4q23 4 134.5 73.5t150.5 98.5q115 80 114 170q0 45 -31.5 68.5t-78.5 23.5q-100 0 -297 -104l-43 100q84 55 192.5 94t198.5 39q98 0 157.5 -52 t59.5 -148q0 -64 -30.5 -120.5t-89 -102.5t-103.5 -72.5t-115 -63.5q111 0 188.5 -63.5t77.5 -171.5q0 -182 -106.5 -317.5t-263 -199t-334.5 -63.5z" /> +<glyph unicode="4" d="M35 0l12 133q266 434 641 871h166l-137 -871h131l-21 -133h-135q-53 -240 -123 -342l-114 51q47 98 90 291h-510zM201 133h364l111 666q-274 -338 -475 -666z" /> +<glyph unicode="5" d="M72 -221q170 4 323 80q96 47 170 144t74 198q0 82 -59.5 118.5t-147.5 36.5q-96 0 -242 -41l168 689h508l-20 -123h-367l-98 -412q43 12 88 12q332 0 332 -256q0 -164 -114 -298t-273 -202q-155 -67 -309 -67h-9h-10z" /> +<glyph unicode="6" d="M96 365q0 328 196.5 566t522.5 384l39 -101q-219 -127 -303 -200q-23 -18 -41 -37q-111 -115 -174 -238q-41 -80 -67.5 -188.5t-26.5 -194.5q0 -102 43 -184t137 -82q215 0 270 361q4 37 4 55q0 180 -229 180q-66 0 -150 -37l31 31l30 30q9 9 27.5 24.5t31.5 21.5l32 14 q18 8 39.5 11t46.5 3q127 0 210 -73.5t83 -198.5q0 -33 -6 -68q-31 -197 -149 -337t-304 -140q-106 0 -175 62.5t-93.5 147t-24.5 188.5z" /> +<glyph unicode="7" d="M100 -281q207 223 384.5 551t222.5 602h-525l21 132h671l-20 -132q-47 -291 -238.5 -652t-408.5 -591z" /> +<glyph unicode="8" d="M66 264q0 145 122 287q61 70 183 123q-82 51 -123 107.5t-41 140.5q0 162 118.5 265t282.5 103q127 0 204 -56t77 -175q0 -129 -79 -223.5t-208 -147.5q135 -78 174 -121q61 -68 62 -166q0 -59 -21 -127q-45 -143 -166 -225t-274 -82q-135 0 -223 82t-88 215zM223 297 q0 -90 51.5 -151.5t139.5 -61.5q106 0 187 92t81 200.5t-102 170.5l-117 71q-104 -51 -172 -134t-68 -187zM352 928q0 -111 140 -187q121 47 186 114t65 183q0 141 -168 142q-94 0 -158.5 -78t-64.5 -174z" /> +<glyph unicode="9" d="M80 -217q223 127 307 199q25 20 45 43q268 274 268 608q0 281 -163 280q-86 0 -149.5 -61t-93.5 -137t-42 -164q-6 -27 -6 -55q0 -178 231 -179q59 0 148 35q-16 -14 -52 -51t-51.5 -47t-52.5 -23.5t-82 -13.5q-127 0 -210 74t-83 198l6 68q33 209 144.5 343t308.5 134 q154 0 226.5 -111.5t72.5 -273.5q0 -86 -18 -172q-111 -522 -705 -790z" /> +<glyph unicode=":" horiz-adv-x="528" d="M82 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM184 708.5q0 49.5 40 87.5t89 38t85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 32.5t-35 82z" /> +<glyph unicode=";" horiz-adv-x="528" d="M-51 -207q181 195 181 329q0 11 -1 21q49 23 90 23q68 0 127 -113q-111 -170 -313 -336zM207 711q0 51 40 89t91 38q49 0 84 -33t35 -82q0 -51 -40 -89t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="<" horiz-adv-x="614" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="=" horiz-adv-x="983" d="M90 293v123h782v-123h-782zM90 598v123h782v-123h-782z" /> +<glyph unicode=">" horiz-adv-x="614" d="M68 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="?" horiz-adv-x="815" d="M121 84q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM197 1382l100 105q47 -66 113 -66h180q227 0 227 -176q0 -133 -119 -272l-182 -207q-35 -39 -58.5 -68.5t-40.5 -61.5l-27 -48q-9 -16 -18.5 -54t-11.5 -50.5t-10 -68t-12 -77.5h-125 q4 20 14.5 97t16.5 101.5t23.5 81t46 100.5t69.5 91l182 205q35 39 64.5 98t29.5 105q0 82 -100 81h-172q-135 0 -190 84z" /> +<glyph unicode="@" horiz-adv-x="1622" d="M88 727q0 299 223.5 529.5t519.5 230.5q295 0 490 -179.5t195 -471.5q0 -113 -56.5 -236t-160 -212t-218.5 -89q-72 0 -119 35t-47 104q0 12 5 33q-119 -102 -175 -127q-45 -20 -90 -20q-98 0 -152 80.5t-54 183.5q0 94 18 185t58 178t116 141.5t176 54.5q145 0 225 -117 l21 84h133l-141 -592q-12 -45 -13 -67q0 -55 56 -56q125 0 218 151.5t93 285.5q0 252 -163 394t-419 142q-254 0 -429 -195.5t-175 -453.5q0 -270 179.5 -457.5t449.5 -187.5q152 0 292 65.5t230 182.5l96 -52q-94 -139 -270 -225t-348 -86q-319 0 -541.5 222.5 t-222.5 541.5zM588 610q0 -59 21.5 -106t74.5 -47q18 0 43 6t41 12t47 22.5t41 22.5l43 26q33 19 35 21l84 369q-76 98 -205 98q-78 0 -132 -83t-73.5 -174t-19.5 -167z" /> +<glyph unicode="A" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM389 551h313l-69 569z" /> +<glyph unicode="B" horiz-adv-x="1017" d="M88 20l201 1268h317q346 0 346 -262q0 -109 -68.5 -208t-172.5 -128q104 -6 162.5 -73.5t58.5 -172.5q0 -217 -155.5 -346t-375.5 -129q-120 0 -313 51zM270 135q88 -27 150 -26q147 0 247.5 87t100.5 236q0 59 -23.5 98t-68.5 55.5t-86 21.5t-101 5h-143zM367 745 q27 0 76 -1t75 -1q115 0 194 75t79 190q0 154 -207 153h-152z" /> +<glyph unicode="C" horiz-adv-x="1007" d="M119 465q0 176 55 347t160 298q176 211 409 211q197 0 318 -88l-76 -100q-102 57 -262 57q-84 0 -165 -60.5t-134 -142.5q-141 -217 -141 -518q0 -152 64.5 -261.5t205.5 -109.5q164 0 291 90l55 -104q-74 -53 -177.5 -85t-197.5 -32q-209 0 -307 140.5t-98 357.5z" /> +<glyph unicode="D" horiz-adv-x="1128" d="M88 0l205 1290h373q199 0 292 -124t93 -330q0 -145 -36 -287.5t-105.5 -267.5t-191.5 -203t-278 -78h-352zM270 131h191q119 0 206 70.5t132 183.5t65.5 225.5t20.5 220.5q0 328 -248 328h-203z" /> +<glyph unicode="E" horiz-adv-x="913" d="M88 0l205 1290h663l-22 -137h-500l-65 -410h383l-21 -141h-383l-74 -461h521l-23 -141h-684z" /> +<glyph unicode="F" horiz-adv-x="847" d="M88 0l205 1290h606l-22 -135h-443l-65 -412h350l-21 -141h-350l-96 -602h-164z" /> +<glyph unicode="G" horiz-adv-x="1062" d="M119 465q0 176 55 347t160 298q176 211 409 211q162 0 320 -88l-82 -105q-98 63 -258 64q-84 0 -165 -61.5t-134 -143.5q-141 -217 -141 -518q0 -152 64.5 -261.5t205.5 -109.5q106 0 184 29l68 424h151l-84 -531q-139 -53 -344 -53q-211 0 -310 139.5t-99 358.5z" /> +<glyph unicode="H" horiz-adv-x="1122" d="M88 0l205 1290h164l-88 -547h467l88 547h163l-204 -1290h-164l96 604h-467l-96 -604h-164z" /> +<glyph unicode="I" horiz-adv-x="487" d="M88 0l205 1290h160l-205 -1290h-160z" /> +<glyph unicode="J" horiz-adv-x="487" d="M-113 -223q174 53 195 178l211 1335h160l-213 -1347q-33 -207 -336 -277z" /> +<glyph unicode="K" horiz-adv-x="931" d="M88 0l205 1290h164l-92 -585l327 342q72 74 72 190q0 27 -2 39l164 29q6 -31 6 -64q0 -141 -105 -250l-350 -356l432 -586v-49h-172l-399 539l-86 -539h-164z" /> +<glyph unicode="L" horiz-adv-x="763" d="M88 0l205 1290h156l-187 -1163h457l-19 -127h-612z" /> +<glyph unicode="M" horiz-adv-x="1519" d="M88 0l205 1290h217l188 -880q29 -141 35 -209q70 170 90 209l445 880h217l-205 -1290h-164l139 877q8 59 41 178l-520 -1055h-143l-223 1057q-8 -119 -19 -176l-139 -881h-164z" /> +<glyph unicode="N" horiz-adv-x="1134" d="M88 0l205 1290h215l280 -985l19 178l129 807h164l-205 -1290h-168l-319 1057q-2 -86 -17 -180l-139 -877h-164z" /> +<glyph unicode="O" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214z" /> +<glyph unicode="P" horiz-adv-x="993" d="M88 0l205 1290h278q182 0 302 -76.5t120 -248.5q0 -223 -137 -363.5t-344 -140.5q-96 0 -180 45l-80 -506h-164zM352 637q100 -43 160 -43q133 0 224 102.5t91 247.5q0 113 -78.5 164t-197.5 51h-115z" /> +<glyph unicode="Q" horiz-adv-x="1071" d="M94 485q0 129 35 269.5t100.5 269.5t179 213t255.5 84q98 0 169.5 -45t110.5 -121t56.5 -160t17.5 -174q0 -356 -170 -606t-502 -373q10 -4 40 -20l39 -22q9 -5 34.5 -17l38 -17.5t36 -13.5t41 -10t40 -5t46.5 -3q135 0 281 47l-33 -166q-68 -18 -168 -18q-47 0 -88 4 t-85 17l-69 21q-26 7 -76 33l-66 35q-15 8 -76 44l-76 44l-17 70q199 109 254 143q-170 41 -259 168t-89 309zM254 477q0 -334 311 -375q154 119 223.5 318t69.5 422q0 344 -217 344q-104 0 -184 -77t-121 -194.5t-61.5 -229.5t-20.5 -208z" /> +<glyph unicode="R" horiz-adv-x="1019" d="M88 0l205 1290h278q182 0 302 -76.5t120 -248.5q0 -176 -110.5 -319.5t-282.5 -174.5l315 -422l-8 -49h-159l-351 471q-37 12 -65 31l-80 -502h-164zM352 637q86 -43 146 -43q131 0 230 107.5t99 242.5q0 113 -78.5 164t-197.5 51h-115z" /> +<glyph unicode="S" horiz-adv-x="880" d="M53 88l84 113q119 -98 228 -99q94 0 176 76t82 170q0 63 -47.5 117.5t-114.5 97.5l-134 89q-67 46 -114 116t-47 158q0 176 120 285.5t296 109.5q76 0 152.5 -42t125.5 -103l-96 -80q-37 43 -93.5 70.5t-109.5 27.5q-92 0 -158.5 -65.5t-66.5 -157.5q0 -59 32.5 -108.5 t83.5 -82.5l108 -72q58 -40 108.5 -79t83 -102.5t32.5 -139.5q0 -182 -124.5 -300t-309.5 -118q-174 0 -297 119z" /> +<glyph unicode="T" horiz-adv-x="800" d="M102 1143l23 143h788l-22 -143h-316l-180 -1143h-159l180 1143h-314z" /> +<glyph unicode="U" horiz-adv-x="1114" d="M135 358q0 72 12 144l125 788h164l-125 -790q-12 -70 -12 -144q0 -254 195 -254q244 0 309 400l125 788h164l-127 -796q-16 -106 -52 -196.5t-94.5 -167.5t-147.5 -120t-200 -43q-172 0 -254 107.5t-82 283.5z" /> +<glyph unicode="V" horiz-adv-x="993" d="M143 1290h179l161 -1093l303 567q66 121 66 311v215h172v-211q0 -205 -86 -360l-391 -719h-189z" /> +<glyph unicode="W" horiz-adv-x="1519" d="M164 1290h174l53 -913q1 -15 2 -35q0 -40 -6 -100q20 78 43 135l363 913h163l66 -913q4 -36 4 -77q0 -28 -2 -58q20 63 61 147l189 404q6 12 21 46l23 49q7 15 18.5 45t17.5 50.5t10 48t4 53.5v205h172v-211q0 -57 -20.5 -128.5t-36.5 -107.5l-58 -120l-356 -723h-166 q-6 111 -23.5 328t-27.5 388t-14 326q-6 -18 -20.5 -53t-16.5 -43l-365 -946h-174z" /> +<glyph unicode="X" horiz-adv-x="1013" d="M8 0v45l455 617l-273 628h179l209 -473l350 473h137v-45l-422 -575l295 -670h-188l-222 512l-374 -512h-146z" /> +<glyph unicode="Y" horiz-adv-x="882" d="M92 1196l107 113q72 -45 136 -139.5t91 -180.5q52 -157 52 -272q0 -16 -1 -31l31 115q37 137 150.5 301t228.5 223l98 -104q-172 -104 -286.5 -343t-159.5 -483l-70 -395h-162l74 457q-8 133 -32.5 263t-91.5 272.5t-165 203.5z" /> +<glyph unicode="Z" horiz-adv-x="909" d="M25 0v139l661 1008h-457l13 143h649l-14 -143l-672 -1004h629l-23 -143h-786z" /> +<glyph unicode="[" horiz-adv-x="589" d="M18 -319l289 1818h318l-21 -119h-190l-250 -1581h190l-18 -118h-318z" /> +<glyph unicode="\" horiz-adv-x="686" d="M25 1550h127l530 -1853h-127z" /> +<glyph unicode="]" horiz-adv-x="595" d="M-8 -319l18 118h191l250 1581h-191l21 119h317l-289 -1818h-317z" /> +<glyph unicode="^" horiz-adv-x="1277" d="M145 655l435 795h114q195 -381 434 -795h-143l-348 629l-348 -629h-144z" /> +<glyph unicode="_" d="M0 -154h922v-122h-922v122z" /> +<glyph unicode="`" horiz-adv-x="518" d="M102 1411l148 129q86 -150 268 -299l-80 -92q-211 123 -336 262z" /> +<glyph unicode="a" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5z" /> +<glyph unicode="b" horiz-adv-x="1038" d="M102 92l211 1329h156l-78 -493q113 106 266 106q143 0 216 -107.5t73 -256.5q0 -170 -54 -325t-180 -265.5t-302 -110.5q-76 0 -161.5 35t-146.5 88zM272 178q72 -84 191 -84q80 0 145.5 62.5t102.5 156t56 186.5t19 167q0 240 -172 239q-129 0 -245 -112z" /> +<glyph unicode="c" horiz-adv-x="849" d="M92 365q0 113 32 227.5t90 213.5t156.5 162.5t219.5 63.5q117 0 246 -100l-80 -94q-94 70 -183 69q-102 0 -179 -92t-109.5 -202.5t-32.5 -209.5q0 -53 10 -104t31.5 -99t64.5 -78t101 -30q49 0 132 48t134 96l59 -82q-74 -78 -174 -131.5t-196 -53.5q-154 0 -238 118 t-84 278z" /> +<glyph unicode="d" horiz-adv-x="1024" d="M92 379q0 162 56.5 308.5t179.5 246.5t288 100q123 0 187 -86q33 176 33 269q0 113 -43 176l120 53q74 -104 74 -258q0 -41 -6 -78l-119 -721q-31 -184 -134 -302t-279 -118q-174 0 -265.5 115t-91.5 295zM250 367q0 -119 48 -197t161 -78q43 0 77.5 14.5t58 33 t45.5 57.5t32 63.5t22.5 75.5t16.5 70.5t12 70.5l59 340q-66 92 -180 92q-94 0 -165.5 -53t-110.5 -139t-57.5 -175t-18.5 -175z" /> +<glyph unicode="e" horiz-adv-x="894" d="M92 362q0 158 56.5 307.5t179.5 257t284 107.5q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q-2 -16 -2 -47q0 -100 47 -187.5t139 -87.5q70 0 148 44t133 104l61 -84q-176 -186 -376 -187q-156 0 -235 115.5t-79 277.5zM268 539q426 92 426 284q0 88 -114 88 q-117 0 -202 -120.5t-110 -251.5z" /> +<glyph unicode="f" horiz-adv-x="550" d="M20 -330q125 209 150 475q49 494 66 740l-144 18l17 101h133l6 96q16 322 241 321q47 0 93 -16l-15 -111q-18 6 -43 6q-113 0 -121 -208q-4 -66 -4 -88h166l-2 -117l-172 -6q-8 -123 -24.5 -318.5t-30.5 -329.5l-12 -133q-20 -225 -213 -483z" /> +<glyph unicode="g" horiz-adv-x="907" d="M-41 -217q0 104 104.5 214t221.5 177l65 -96q-76 -51 -154.5 -130t-78.5 -136q0 -53 49 -75t110 -22q119 0 242 71t123 179q0 80 -55 124q-53 42 -128 42h-7l-7 43q2 4 19 24l21 25l21 26q15 19 20 27.5t18.5 27t19.5 29.5t16.5 27.5t15.5 29.5l11 29q6 15 8 30 q-43 -55 -116.5 -94.5t-141.5 -39.5q-125 0 -187.5 78t-62.5 205q0 193 109 314.5t301 121.5q127 0 303 -28q-8 -137 -39 -318q-43 -254 -84 -352q-31 -72 -71 -121q76 -12 123 -76.5t47 -154.5q0 -119 -88 -209t-204 -130t-231 -40q-51 0 -98 6t-99 23.5t-84 55.5t-32 93z M260 608q0 -68 36 -111.5t105 -43.5q84 0 153 76.5t89 164.5q18 92 29 211q-61 14 -183 15q-109 0 -169 -98.5t-60 -213.5z" /> +<glyph unicode="h" horiz-adv-x="1042" d="M90 0l225 1421h156l-96 -606q59 88 160.5 153.5t199.5 65.5q104 0 159.5 -41t55.5 -141q0 -29 -8 -90l-74 -469q-16 -104 -55 -174t-121 -152l-88 76q94 121 117 266l65 453q4 37 5 55q0 49 -26 69.5t-77 20.5q-76 0 -170 -70.5t-145 -146.5q-25 -37 -25 -45l-102 -645 h-156z" /> +<glyph unicode="i" horiz-adv-x="479" d="M90 0l160 1004h151l-159 -1004h-152zM244 1286q0 49 40 87t91 38q49 0 84 -32.5t35 -82.5q0 -51 -40 -88t-92 -37q-49 0 -83.5 33t-34.5 82z" /> +<glyph unicode="j" horiz-adv-x="479" d="M-123 -285q176 61 195 181l176 1108h151l-178 -1123q-16 -90 -57 -133q-92 -94 -270 -143zM244 1286q0 49 40 87t91 38q49 0 84 -32.5t35 -82.5q0 -51 -40 -88t-92 -37q-49 0 -83.5 33t-34.5 82z" /> +<glyph unicode="k" horiz-adv-x="983" d="M90 0l225 1421h152l-133 -837q158 10 284 69q59 29 110.5 79t51.5 104q0 43 -30 61q-25 15 -59 15q-7 0 -15 -1l-10 123q27 8 67 8q96 0 157.5 -50t61.5 -144q0 -100 -67.5 -175t-158.5 -109t-189 -38l370 -526h-196l-381 559l-88 -559h-152z" /> +<glyph unicode="l" horiz-adv-x="495" d="M137 225q0 94 15 178l161 1018h152l-162 -1018q-16 -94 -16 -182q0 -133 28 -196l-145 -43q-33 91 -33 243z" /> +<glyph unicode="m" horiz-adv-x="1576" d="M90 0l160 1004h156l-33 -193q39 72 145.5 147.5t181.5 75.5q104 0 168 -53q56 -47 57 -133q0 -11 -1 -23q49 82 136 145.5t171 63.5q233 0 233 -198q0 -37 -6 -74l-121 -762h-155l121 762q4 33 4 49q0 96 -99 96q-74 0 -167 -83t-146 -165l-102 -659h-156l121 762 q4 33 4 47q0 96 -111 96q-72 0 -157.5 -82t-147.5 -172l-100 -651h-156z" /> +<glyph unicode="n" horiz-adv-x="1062" d="M90 0l160 1004h156l-31 -189q51 82 157.5 150.5t202.5 68.5q104 0 159.5 -41t55.5 -141q0 -29 -8 -90l-121 -762h-155l120 762q6 55 7 57q0 88 -91 88q-98 0 -200.5 -82t-155.5 -180l-100 -645h-156z" /> +<glyph unicode="o" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222z" /> +<glyph unicode="p" horiz-adv-x="1097" d="M68 -358l168 1069l-146 -78l16 106l150 90l27 175h155l-10 -76q178 106 287 106q143 0 217 -107.5t74 -256.5q0 -104 -30 -222t-85 -227.5t-149.5 -181.5t-209.5 -72q-84 0 -146 41t-85 119l-78 -485h-155zM346 362q0 -111 46 -191.5t151 -80.5q78 0 139 64.5t95 161 t50.5 187.5t16.5 163q0 244 -160 243q-86 0 -276 -110l-50 -301q-12 -91 -12 -136z" /> +<glyph unicode="q" horiz-adv-x="1060" d="M92 342q0 111 33 228.5t92 223t158.5 173t216.5 67.5q80 0 174 -15t147 -32l54 -16l-211 -1331h-156l76 487q-96 -160 -283 -160q-152 0 -226.5 107.5t-74.5 267.5zM256 360q0 -106 37 -187t129 -81q68 0 126 42t95 106.5t59.5 130t32.5 127.5l60 381q-104 29 -164 28 q-100 0 -177 -52t-118 -137t-60.5 -175.5t-19.5 -182.5z" /> +<glyph unicode="r" horiz-adv-x="712" d="M90 0l160 1004h158l-31 -185l45 64q106 152 213 151q39 0 74.5 -27.5t54.5 -66.5l-119 -139q-31 63 -92 63q-43 0 -152 -131l-49 -61l-106 -672h-156z" /> +<glyph unicode="s" horiz-adv-x="843" d="M47 92l76 103q49 -39 115.5 -71t119.5 -32q80 0 146.5 49t66.5 127t-104 133l-197 107q-139 74 -139 211q0 154 112.5 234.5t272.5 80.5q86 0 163 -35.5t124 -103.5l-86 -76q-92 92 -223 92q-74 0 -138.5 -41t-64.5 -110q0 -33 19.5 -59.5t41 -41t56.5 -32.5l174 -92 q143 -76 143 -222q0 -156 -105.5 -250t-261.5 -94q-147 0 -311 123z" /> +<glyph unicode="t" horiz-adv-x="690" d="M143 903l17 101h143l29 178h143l-29 -178h203l-18 -119h-199l-108 -680q-3 -21 -4 -39q0 -26 8 -44q12 -30 63 -30q84 0 170 74l45 -100q-117 -96 -256 -97q-111 0 -155 61q-33 44 -33 113q0 25 4 54l110 688z" /> +<glyph unicode="u" horiz-adv-x="1038" d="M111 156q0 29 8 86l121 762h155l-121 -762q-6 -31 -6 -62q0 -84 94 -84q168 0 347 250l106 658h156l-135 -850q-4 -29 -5 -41q0 -39 35 -41l-67 -103h-6h-6q-49 0 -80 33q-33 35 -33 88t18 103q-63 -90 -162.5 -157t-199.5 -67q-109 0 -164 42.5t-55 144.5z" /> +<glyph unicode="v" horiz-adv-x="866" d="M70 1004h168l145 -828l279 457q39 63 38 153q0 111 -41 199l152 33q35 -78 35 -193q0 -141 -70 -256l-344 -569h-158z" /> +<glyph unicode="w" horiz-adv-x="1357" d="M82 1004h164l100 -816l324 816h161l89 -816l229 435q41 80 41 176t-33 184l150 35q31 -96 30 -203q0 -131 -53 -227l-321 -586l-168 -2l-62 512q-15 121 -14 226q0 42 2 81q-45 -176 -103 -307l-219 -512h-172z" /> +<glyph unicode="x" horiz-adv-x="864" d="M-20 2v37l370 481l-221 484h170l168 -386l192 191q27 27 27 82q0 53 -16 98l151 31q14 -43 15 -105q0 -109 -68 -176l-242 -237l244 -502h-174l-182 387l-299 -387z" /> +<glyph unicode="y" horiz-adv-x="882" d="M-16 -276q102 41 174.5 108.5t136.5 169.5l-211 1002h166l149 -832l297 504q20 37 21 94q0 106 -43 217l151 31q37 -92 37 -205q0 -119 -53 -207l-389 -649q-80 -135 -171 -215t-229 -123z" /> +<glyph unicode="z" horiz-adv-x="843" d="M20 0l11 145q258 358 604 734h-520l14 125h700l-16 -127q-324 -350 -623 -744h574l-16 -133h-728z" /> +<glyph unicode="{" horiz-adv-x="722" d="M74 541l16 96q76 0 134.5 81t72.5 165l57 360q18 127 72.5 196.5t169.5 69.5h121l6 -110h-121q-16 0 -29.5 -3t-23.5 -7.5t-19.5 -14.5l-14.5 -15t-11 -21.5t-9 -21.5t-8 -25l-5 -24q-1 -5 -5 -27.5t-4 -25.5l-55 -360q-14 -82 -68.5 -156.5t-126.5 -113.5 q51 -29 82 -89.5t31 -123.5q0 -33 -4 -49l-58 -361q-8 -66 -8 -96q0 -49 19.5 -67.5t70.5 -18.5h109l-39 -111h-117q-90 0 -131 42t-41 134q0 31 8 88l58 361q4 37 4 53q0 70 -35 132.5t-98 62.5z" /> +<glyph unicode="|" horiz-adv-x="454" d="M166 -37v1585h123v-1585h-123z" /> +<glyph unicode="}" horiz-adv-x="630" d="M-47 -221h108q35 0 58.5 11t37 23.5t22.5 44t12.5 46t9.5 57.5l57 361q14 82 67.5 154.5t127.5 107.5q-51 33 -82 94t-31 125q0 35 4 51l55 360q10 55 10.5 100.5t-16 65t-61.5 19.5h-121l39 110h121q88 0 128 -42t40 -132q0 -31 -9 -92l-57 -360q-4 -18 -4 -54 q0 -68 35 -130t98 -62l-16 -96q-76 0 -135.5 -82t-71.5 -166l-57 -361q-18 -127 -73.5 -195.5t-172.5 -68.5h-117z" /> +<glyph unicode="~" horiz-adv-x="1142" d="M281 504l20 127q8 8 28.5 35.5t26.5 33.5l24 23q17 16 31.5 20.5t37 9.5t51.5 5q76 0 201.5 -70t197.5 -70q111 0 184 119l-24 -147q-8 -8 -23.5 -26.5t-21.5 -23.5t-18.5 -17.5t-18.5 -15.5l-20 -10q-13 -7 -25 -8.5t-29.5 -3.5t-40.5 -2q-74 0 -200.5 70t-198.5 70 q-66 0 -100.5 -26.5t-81.5 -92.5z" /> +<glyph unicode="¡" horiz-adv-x="516" d="M25 -426q63 190 98 395l110 705h123l-80 -705q-25 -184 -92 -375zM201 911.5q0 49.5 40 87t91 37.5q49 0 84 -32.5t35 -82t-40 -87t-92 -37.5q-49 0 -83.5 32.5t-34.5 82z" /> +<glyph unicode="¢" horiz-adv-x="868" d="M100 365q0 133 44 271t124 228q133 143 285 164l22 139h93l-23 -137q76 -8 123 -40t100 -99l-102 -80q-66 82 -139 94l-129 -811q74 6 182 78l63 -74q-121 -117 -266 -129l-18 -119h-92l18 121q-143 23 -214 131.5t-71 262.5zM260 399q0 -246 148 -295l124 799 q-88 -25 -153.5 -116t-92 -192.5t-26.5 -195.5z" /> +<glyph unicode="£" horiz-adv-x="905" d="M61 791l41 102h80v270q0 129 97.5 191.5t232.5 62.5q145 0 213 -57l-35 -94q-63 37 -166 37q-186 0 -186 -170v-240h274v-123h-274v-260q0 -180 -47 -385h145q119 -39 180 -39q47 0 96.5 26.5t80.5 65.5l22 -102q-41 -45 -102.5 -76t-118.5 -31q-27 0 -72 7.5t-76 15.5 l-30 8h-307q74 330 73 514v264z" /> +<glyph unicode="¤" horiz-adv-x="1110" d="M16 498l82 129h119q4 41 8 63q2 23 13 66h-181l82 129h137q59 227 226.5 377.5t390.5 150.5q197 0 326 -143l-78 -123q-92 137 -279 137q-152 0 -271.5 -106.5t-170.5 -292.5h561l-82 -129h-516q-14 -57 -14 -68q-1 -4 -7 -61h457l-82 -129h-379q-1 -18 0 -35 q0 -167 72 -258q80 -100 223 -101q86 0 178.5 43t165.5 119l-26 -164q-145 -127 -348 -127q-195 0 -309 142q-101 125 -101 328q0 26 2 53h-199z" /> +<glyph unicode="¥" horiz-adv-x="882" d="M55 334l21 123h305q-4 63 -25 190h-247l20 123h203q-76 332 -240 432l107 107q72 -45 136 -139.5t91 -180.5q52 -157 52 -272q0 -16 -1 -31l31 115q37 137 150.5 301t228.5 223l98 -104q-104 -61 -192 -187.5t-146 -263.5h268l-20 -123h-293q-37 -111 -51 -190h311 l-20 -123h-314l-59 -334h-162l55 334h-307z" /> +<glyph unicode="§" horiz-adv-x="860" d="M43 -190l57 112q147 -90 308 -90q86 0 141 42t55 126q0 20 -7 38.5t-15.5 33t-26.5 31t-29.5 25.5t-37 24.5t-35 19.5t-37.5 18l-33 17q-6 2 -62.5 28.5t-71.5 35.5l-59 37q-43 28 -58 49.5t-31.5 57.5t-16.5 74q0 127 74.5 197t199.5 86q-6 2 -35 16l-38 19 q-8 4 -35 18.5t-36 20.5l-32 21q-23 15 -31 26.5t-22 29t-20.5 34t-11.5 38t-5 43.5q0 139 98.5 213t241.5 74q158 0 295 -78l-55 -106q-139 66 -244 65q-80 0 -131 -42t-51 -120q0 -49 53 -91t129 -75.5t150.5 -71.5t128 -97.5t53.5 -133.5q0 -113 -58.5 -194.5 t-166.5 -106.5q49 -27 77.5 -45t65.5 -48.5t55.5 -69.5t18.5 -84q0 -154 -98.5 -236t-254.5 -82q-188 1 -356 101zM238 496q0 -57 52 -93t111 -36q92 0 153.5 41t61.5 129q0 66 -52 105.5t-120 39.5q-94 0 -150 -47t-56 -139z" /> +<glyph unicode="¨" horiz-adv-x="638" d="M121 1276q0 45 34.5 83t82.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM436 1276q0 45 35 83t82 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="©" horiz-adv-x="1800" d="M246 501.5q0 272.5 192.5 465t464.5 192.5q274 0 467 -192.5t193 -465t-193.5 -466t-466 -193.5t-465 193.5t-192.5 466zM346 502q0 -236 164 -397.5t399 -161.5q229 0 391 163.5t162 395t-163.5 395.5t-395 164t-394.5 -164t-163 -395zM594 500q0 154 82 262t231 108 q123 0 209 -79l-55 -70q-61 51 -154 51q-104 0 -150 -77t-46 -189q0 -117 50 -196t161 -79q76 0 172 70l43 -68q-41 -39 -104.5 -68.5t-115.5 -29.5q-152 0 -237.5 104.5t-85.5 260.5z" /> +<glyph unicode="ª" horiz-adv-x="745" d="M154 954q0 123 42 255t139 237.5t224 105.5q74 0 217 -24l-94 -656q0 -2 -1 -6t-1 -6q0 -37 35 -37q18 0 39 11l20 -60q-51 -31 -104 -31q-49 0 -80 32t-31 81q0 16 6 43t8 35q-37 -74 -120.5 -132.5t-161.5 -58.5q-137 0 -137 211zM281 965q0 -119 57 -119 q80 0 166 103.5t102 191.5q14 100 47 301q-35 12 -92 12q-86 0 -153.5 -93t-97 -202.5t-29.5 -193.5z" /> +<glyph unicode="«" horiz-adv-x="970" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78zM434 512l379 385l96 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="¬" horiz-adv-x="1095" d="M94 760v123h826v-523l-121 -22v422h-705z" /> +<glyph unicode="­" horiz-adv-x="618" d="M113 461v123h393v-123h-393z" /> +<glyph unicode="®" horiz-adv-x="1800" d="M242 500q0 274 191.5 465.5t466 191.5t465.5 -191.5t191 -466t-192.5 -467t-464.5 -192.5t-464.5 194t-192.5 466zM342 500q0 -238 163 -398.5t400 -160.5q231 0 391 163.5t160 395t-164 395.5t-393 164q-231 0 -394 -164t-163 -395zM688 145v727h176q125 0 212 -59 t87 -178q0 -84 -57 -159t-139 -83l225 -217v-31h-109l-245 248q-23 6 -45 19v-267h-105zM793 498q57 -27 94 -27q76 0 122 45t46 123t-54.5 113.5t-136.5 35.5h-71v-290z" /> +<glyph unicode="¯" horiz-adv-x="632" d="M51 1174l21 120h561l-21 -120h-561z" /> +<glyph unicode="°" horiz-adv-x="641" d="M63 995q0 129 102.5 228.5t231.5 99.5q104 0 174 -68.5t70 -173.5q0 -133 -99.5 -230t-232.5 -97q-104 0 -175 68.5t-71 172.5zM164 1008q0 -72 44 -117t114 -45q88 0 154.5 69.5t66.5 153.5q0 72 -44 118t-116 46q-82 0 -150.5 -68.5t-68.5 -156.5z" /> +<glyph unicode="±" horiz-adv-x="915" d="M92 451v122h330v355h119v-355h331v-122h-331v-256h-119v256h-330zM113 -2v123h739v-123h-739z" /> +<glyph unicode="²" d="M96 127q195 98 383 293q78 80 142.5 179t64.5 177q0 66 -44 96.5t-112 30.5q-100 0 -260 -98l-51 100q100 72 175 100.5t184 28.5q274 0 274 -256q0 -117 -92 -251t-205 -231t-225 -161h456l-22 -135h-660z" /> +<glyph unicode="³" d="M57 -225q92 2 188.5 29.5t182.5 78.5t141.5 134t55.5 182q0 154 -244 153q-74 0 -166 -10l-8 117q10 2 28.5 4t33.5 4l30 4q23 4 134.5 73.5t150.5 98.5q115 80 114 170q0 45 -31.5 68.5t-78.5 23.5q-100 0 -297 -104l-43 100q84 55 192.5 94t198.5 39q98 0 157.5 -52 t59.5 -148q0 -63 -30.5 -120t-89 -103t-103.5 -72.5t-115 -63.5q111 0 188.5 -63.5t77.5 -171.5q0 -182 -106.5 -317.5t-263 -199t-334.5 -63.5z" /> +<glyph unicode="´" horiz-adv-x="573" d="M104 1241q225 147 363 299l106 -129q-160 -137 -417 -262z" /> +<glyph unicode="µ" horiz-adv-x="1062" d="M127 -360q59 137 59 229q0 61 -11 186t-11 187v762h155v-762q0 -76 21.5 -111t93.5 -35q70 0 162 36t143 81v791h156v-850q0 -80 43 -82l-51 -103q-55 0 -95 32t-49 85q-63 -51 -152 -84t-171 -33q-31 0 -88 31q39 -117 39 -180q0 -76 -39 -144z" /> +<glyph unicode="¶" horiz-adv-x="1132" d="M102 831q0 203 125 346.5t326 143.5q27 0 39 -2l326 -31l12 90h112q-2 -14 -5 -28l-7 -33q-4 -18 -6 -29l109 21l-23 -105l-111 -24q-109 -266 -108 -496q0 -94 27 -176q35 -111 34 -238q0 -211 -65 -354l-185 74q78 150 78 370q0 45 -9 131.5t-9 131.5q0 270 125 559 l-289 24q-6 1 -12 1q-60 0 -109 -55q-53 -60 -75.5 -137t-22.5 -145q0 -82 39 -145t114 -66l-98 -139q-33 -4 -49 -4q-137 0 -210 88t-73 227z" /> +<glyph unicode="·" horiz-adv-x="559" d="M145 494q0 51 40 87.5t91 36.5q49 0 84 -32.5t35 -81.5q0 -51 -39 -88t-90 -37q-49 0 -85 33t-36 82z" /> +<glyph unicode="¸" horiz-adv-x="686" d="M371 -293q193 96 192 162q0 51 -57 51q-27 0 -105 -18l17 96h213q55 -47 55 -109q0 -74 -82 -143.5t-166 -101.5z" /> +<glyph unicode="¹" d="M238 856l16 98q88 0 199 29l75 18h142l-158 -1001h-152l136 856h-258z" /> +<glyph unicode="º" horiz-adv-x="790" d="M170 1057q0 176 104.5 334.5t270.5 158.5q123 0 184.5 -94t61.5 -225q0 -109 -43 -218.5t-131.5 -190.5t-200.5 -81q-123 0 -184.5 92.5t-61.5 223.5zM287 1044q0 -213 143 -213q84 0 142.5 77t80 166t21.5 169q0 213 -144 213q-63 0 -112 -44t-77 -110.5t-41 -133.5 t-13 -124z" /> +<glyph unicode="»" horiz-adv-x="1048" d="M137 205l96 -78l383 385l-378 385l-97 -78l305 -307zM504 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="¼" horiz-adv-x="2844" d="M238 856l16 98q88 0 199 29l75 18h142l-158 -1001h-152l136 856h-258zM631 -268l1163 1794h129l-1163 -1794h-129zM1958 0l12 133q266 434 641 871h166l-137 -871h131l-21 -133h-135q-53 -240 -123 -342l-114 51q47 98 90 291h-510zM2124 133h364l111 666 q-274 -338 -475 -666z" /> +<glyph unicode="½" horiz-adv-x="2844" d="M238 856l16 98q88 0 199 29l75 18h142l-158 -1001h-152l136 856h-258zM631 -268l1163 1794h129l-1163 -1794h-129zM2019 127q195 98 383 293q78 80 142.5 179t64.5 177q0 66 -44 96.5t-111 30.5q-100 0 -261 -98l-51 100q100 72 175 100.5t184 28.5q274 0 274 -256 q0 -117 -92 -251t-205 -231t-225 -161h457l-23 -135h-659z" /> +<glyph unicode="¾" horiz-adv-x="2844" d="M57 -225q92 2 188.5 29.5t182.5 78.5t141.5 134t55.5 182q0 154 -244 153q-74 0 -166 -10l-8 117q10 2 28.5 4t33.5 4l30 4q23 4 134.5 73.5t150.5 98.5q115 80 114 170q0 45 -31.5 68.5t-78.5 23.5q-100 0 -297 -104l-43 100q84 55 192.5 94t198.5 39q98 0 157.5 -52 t59.5 -148q0 -63 -30.5 -120t-89 -103t-103.5 -72.5t-115 -63.5q111 0 188.5 -63.5t77.5 -171.5q0 -182 -106.5 -317.5t-263 -199t-334.5 -63.5zM631 -268l1163 1794h129l-1163 -1794h-129zM1958 0l12 133q266 434 641 871h166l-137 -871h131l-21 -133h-135 q-53 -240 -123 -342l-114 51q47 98 90 291h-510zM2124 133h364l111 666q-274 -338 -475 -666z" /> +<glyph unicode="¿" horiz-adv-x="829" d="M33 -242q0 135 121 273l180 207q41 45 67 83l42 59q15 22 27.5 64.5t15.5 55t13.5 78t14.5 88.5h125l-15.5 -97.5t-17.5 -100.5t-23.5 -83t-46 -99t-69.5 -91l-182 -205q-35 -39 -65 -98.5t-30 -104.5q0 -82 103 -82h170q135 0 190 -84l-100 -104q-47 66 -113 65h-180 q-227 0 -227 176zM479 909q0 49 40 87t91 38q49 0 84 -32.5t35 -81.5q0 -51 -39 -88t-90 -37q-49 0 -85 32.5t-36 81.5z" /> +<glyph unicode="À" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM389 551h313l-69 569zM463 1677l147 129q84 -150 269 -299l-82 -92q-211 123 -334 262z" /> +<glyph unicode="Á" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM389 551h313l-69 569zM492 1507q225 147 362 299l107 -129q-162 -137 -418 -262z" /> +<glyph unicode="Â" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM338 1520q94 51 222 137t179 131q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -314 -193zM389 551h313l-69 569z" /> +<glyph unicode="Ã" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM299 1587q53 66 110 91q43 19 101 20q19 0 39 -2q37 -4 127 -46t147 -42q70 0 97 100l102 -20q-20 -109 -85 -159q-54 -42 -129 -42q-15 0 -32 2q-59 6 -136 46t-138 40q-37 0 -74 -15.5t-55 -31.5 l-21 -17zM389 551h313l-69 569z" /> +<glyph unicode="Ä" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM389 551h313l-69 569zM432 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM748 1542q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5 q0 -45 -39 -80t-84 -35q-33 0 -55 21.5t-22 54.5z" /> +<glyph unicode="Å" horiz-adv-x="1052" d="M-45 0l612 1290h178l205 -1290h-168l-61 416h-393l-203 -416h-170zM389 551h313l-69 569zM494 1571q0 98 77.5 175t176.5 77q78 0 129 -52.5t51 -130.5q0 -98 -78 -174.5t-176 -76.5q-78 0 -129 52t-51 130zM586 1587q0 -43 29.5 -73.5t72.5 -30.5q55 0 100.5 44t45.5 97 q0 43 -28 74t-71 31q-57 0 -103 -43.5t-46 -98.5z" /> +<glyph unicode="Æ" horiz-adv-x="1476" d="M-45 0l612 1290h191l28 -432l70 432h664l-23 -137h-500l-65 -410h383l-21 -141h-383l-73 -461h520l-23 -141h-684l66 416h-389l-203 -416h-170zM389 551h305l-63 569z" /> +<glyph unicode="Ç" horiz-adv-x="1007" d="M119 465q0 176 55 347t160 298q176 211 409 211q197 0 318 -88l-78 -107q-109 66 -260 66q-84 0 -166 -62.5t-133 -142.5q-141 -217 -141 -518q0 -152 64.5 -261.5t205.5 -109.5q164 0 291 90l55 -104q-106 -76 -252 -104q39 -47 39 -91q0 -74 -82 -143.5t-166 -101.5 l-67 63q193 88 192 162q0 51 -57 51q-27 0 -105 -18l13 78q-158 39 -226.5 173t-68.5 312z" /> +<glyph unicode="È" horiz-adv-x="913" d="M88 0l205 1290h663l-22 -137h-500l-65 -410h383l-21 -141h-383l-74 -461h521l-23 -141h-684zM461 1677l147 129q86 -150 269 -299l-80 -92q-211 123 -336 262z" /> +<glyph unicode="É" horiz-adv-x="913" d="M88 0l205 1290h663l-22 -137h-500l-65 -410h383l-21 -141h-383l-74 -461h521l-23 -141h-684zM440 1507q225 147 363 299l106 -129q-162 -137 -420 -262z" /> +<glyph unicode="Ê" horiz-adv-x="913" d="M88 0l205 1290h663l-22 -137h-500l-65 -410h383l-21 -141h-383l-74 -461h521l-23 -141h-684zM307 1520q92 53 220 138t180 130q104 -143 243 -275l-92 -90q-125 125 -176 195q-197 -127 -313 -193z" /> +<glyph unicode="Ë" horiz-adv-x="913" d="M88 0l205 1290h663l-22 -137h-500l-65 -410h383l-21 -141h-383l-74 -461h521l-23 -141h-684zM420 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM735 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5 q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ì" horiz-adv-x="487" d="M88 0l205 1290h160l-205 -1290h-160zM166 1677l147 129q84 -150 267 -299l-80 -92q-211 123 -334 262z" /> +<glyph unicode="Í" horiz-adv-x="487" d="M88 0l205 1290h160l-205 -1290h-160zM195 1507q102 66 193 140.5t132 117.5l39 41l107 -129q-162 -137 -420 -262z" /> +<glyph unicode="Î" horiz-adv-x="487" d="M88 1520q94 51 222 137t179 131q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -313 -193zM111 0l204 1290h160l-205 -1290h-159z" /> +<glyph unicode="Ï" horiz-adv-x="487" d="M88 0l205 1290h160l-205 -1290h-160zM147 1542q0 45 35 83t82 38q33 0 57.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -55.5 21.5t-22.5 54.5zM463 1542q0 45 34.5 83t82.5 38q33 0 57.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ñ" horiz-adv-x="1134" d="M88 0l205 1290h215l280 -985l19 178l129 807h164l-205 -1290h-168l-319 1057q-2 -86 -17 -180l-139 -877h-164zM395 1587q53 66 110 91q43 19 101 20q19 0 39 -2q37 -4 127 -46t148 -42q70 0 96 100l102 -20q-20 -109 -85 -159q-54 -42 -129 -42q-15 0 -32 2 q-59 6 -136 46t-138 40q-37 0 -74 -15.5t-57 -31.5l-18 -17z" /> +<glyph unicode="Ò" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM498 1677l147 129q84 -150 268 -299l-79 -92q-213 123 -336 262z" /> +<glyph unicode="Ó" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM545 1507q225 147 362 299l107 -129q-162 -137 -418 -262z" /> +<glyph unicode="Ô" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM387 1520q94 51 222 137t179 131q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -313 -193z" /> +<glyph unicode="Õ" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM365 1587q53 66 109 91q43 19 101 20q19 0 39 -2q37 -4 127 -46t148 -42q70 0 96 100l102 -20q-20 -109 -85 -159q-54 -42 -129 -42q-15 0 -31 2 q-59 6 -136 46t-139 40q-37 0 -73.5 -15.5t-55.5 -31.5l-20 -17z" /> +<glyph unicode="Ö" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q98 0 170 -45t111 -121t56 -159t17 -175q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-98 0 -171 46t-110.5 123t-55 160t-17.5 177zM276 436q0 -334 218 -334q86 0 155.5 55.5 t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 344 -217 344q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM481 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38 -80t-85 -35q-33 0 -55.5 21.5t-22.5 54.5zM797 1542q0 45 35.5 83t80.5 38 q35 0 59.5 -23.5t24.5 -58.5q0 -45 -37.5 -80t-85.5 -35q-33 0 -55 21.5t-22 54.5z" /> +<glyph unicode="Ø" horiz-adv-x="1120" d="M119 473q0 100 21.5 211t67.5 225.5t110.5 205.5t160.5 148.5t209 57.5q78 0 146 -31l114 260h103l-140 -315q131 -133 131 -414q0 -127 -34.5 -270t-101 -276.5t-180.5 -220.5t-253 -87q-66 0 -115 19l-129 -289h-102l149 336q-157 125 -157 440zM276 436 q0 -174 64 -260l434 979q-47 31 -108 31q-104 0 -185.5 -84t-122.5 -209t-61.5 -243t-20.5 -214zM416 117q39 -14 78 -15q86 0 155.5 55.5t111.5 139.5t70.5 187.5t40 193.5t11.5 164q0 141 -43 233z" /> +<glyph unicode="Ù" horiz-adv-x="1114" d="M135 358q0 72 12 144l125 788h164l-125 -790q-12 -70 -12 -144q0 -254 195 -254q244 0 309 400l125 788h164l-127 -796q-16 -106 -52 -196.5t-94.5 -167.5t-147.5 -120t-200 -43q-172 0 -254 107.5t-82 283.5zM520 1677l148 129q86 -150 268 -299l-80 -92 q-211 123 -336 262z" /> +<glyph unicode="Ú" horiz-adv-x="1114" d="M135 358q0 72 12 144l125 788h164l-125 -790q-12 -70 -12 -144q0 -254 195 -254q244 0 309 400l125 788h164l-127 -796q-16 -106 -52 -196.5t-94.5 -167.5t-147.5 -120t-200 -43q-172 0 -254 107.5t-82 283.5zM502 1507q225 147 362 299l107 -129q-162 -137 -418 -262z " /> +<glyph unicode="Û" horiz-adv-x="1114" d="M135 358q0 72 12 144l125 788h164l-125 -790q-12 -70 -12 -144q0 -254 195 -254q244 0 309 400l125 788h164l-127 -796q-16 -106 -52 -196.5t-94.5 -167.5t-147.5 -120t-200 -43q-172 0 -254 107.5t-82 283.5zM401 1520q94 51 222.5 137t179.5 131q104 -143 244 -275 l-93 -90q-125 125 -176 195q-197 -127 -313 -193z" /> +<glyph unicode="Ü" horiz-adv-x="1114" d="M135 358q0 72 12 144l125 788h164l-125 -790q-12 -70 -12 -144q0 -254 195 -254q244 0 309 400l125 788h164l-127 -796q-16 -106 -52 -196.5t-94.5 -167.5t-147.5 -120t-200 -43q-172 0 -254 107.5t-82 283.5zM475 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5 q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM791 1542q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55 21.5t-22 54.5z" /> +<glyph unicode="Ý" horiz-adv-x="882" d="M92 1196l107 113q72 -45 136 -139.5t91 -180.5q52 -157 52 -272q0 -16 -1 -31l31 115q37 137 150.5 301t228.5 223l98 -104q-172 -104 -286.5 -343t-159.5 -483l-70 -395h-162l74 457q-8 133 -32.5 263t-91.5 272.5t-165 203.5zM860 1540q225 147 363 299l106 -129 q-160 -137 -418 -262z" /> +<glyph unicode="ß" horiz-adv-x="1122" d="M20 -330q127 213 152 475l64 740l-144 18l17 101h139l16 155q12 133 139 212t271 79q147 0 245.5 -82t98.5 -225q0 -115 -68 -244q-86 14 -110 14q-76 0 -140.5 -41t-64.5 -112q0 -33 20.5 -60.5t41 -41t57.5 -31.5l174 -92q154 -82 153 -248q0 -154 -114.5 -237 t-274.5 -83q-80 0 -163 35t-136 92l76 105q45 -43 108.5 -77t116.5 -34q76 0 150 48t74 122q0 57 -45.5 100t-111 74l-130 64.5t-109.5 90t-45 132.5q0 143 108.5 227t258.5 88q29 72 28 129q0 86 -57 125t-145 39q-90 0 -166 -54t-84 -140l-111 -1096q-18 -170 -198 -420z " /> +<glyph unicode="à" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM473 1411l148 129q86 -150 268 -299l-80 -92q-211 123 -336 262z" /> +<glyph unicode="á" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM485 1241q225 147 363 299l106 -129q-160 -137 -417 -262z" /> +<glyph unicode="â" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM334 1253q94 51 222 137.5t179 131.5q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -314 -193z" /> +<glyph unicode="ã" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -155 q106 0 220 136t132 251l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM315 1321q53 66 110 91q43 19 101 19q19 0 39 -1q37 -4 127 -46.5t148 -42.5q72 0 96 101l102 -21q-20 -109 -85 -158q-54 -42 -129 -42q-15 0 -31 2q-59 6 -136 46 t-139 40q-37 0 -73.5 -15.5t-55.5 -31.5l-20 -17z" /> +<glyph unicode="ä" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM442 1276q0 45 35 83t82 38q33 0 57.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -55.5 21.5t-22.5 54.5zM758 1276q0 45 34.5 83t81.5 38q33 0 57.5 -23.5 t24.5 -58.5q0 -45 -37.5 -80t-82.5 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="å" horiz-adv-x="1003" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q117 0 284 -33l-125 -860q0 -4 -1 -9t-1 -7q0 -49 47 -49q20 0 50 12l26 -78q-66 -39 -135 -39q-63 0 -104 41t-41 107q0 20 7 55t9 47q-45 -96 -156.5 -174t-214.5 -78q-180 0 -180 279zM258 262q0 -156 76 -156 q106 0 220 136.5t132 251.5l64 397q-55 14 -121 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM494 1350q14 88 88.5 152.5t163 64.5t140.5 -64q41 -50 41 -116q0 -18 -3 -37q-14 -88 -88 -152.5t-164 -64.5q-88 0 -141 63q-41 50 -41 116q1 19 4 38zM586 1350 q-2 -10 -2 -21q0 -37 24 -65q31 -37 78 -37q49 0 92 36.5t51 86.5q2 11 2 20q0 36 -23 66q-30 37 -77 37q-51 0 -94 -37t-51 -86z" /> +<glyph unicode="æ" horiz-adv-x="1361" d="M92 248q0 117 34 249t97.5 254.5t169 202.5t234.5 80q86 0 262 -43q90 43 190 43q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q0 -8 -1 -24.5t-1 -22.5q0 -100 47 -187.5t139 -87.5q72 0 149 44t134 104l59 -84q-172 -186 -377 -187q-197 0 -272 183 q-61 -74 -151.5 -128.5t-176.5 -54.5q-180 0 -180 279zM258 262q0 -156 76 -156q113 0 233 154q-8 46 -8 102q0 145 50 288t145 241q-57 14 -125 14q-86 0 -161 -72.5t-118 -177t-67.5 -210t-24.5 -183.5zM735 539q426 92 426 284q0 88 -114 88q-117 0 -202 -120.5 t-110 -251.5z" /> +<glyph unicode="ç" horiz-adv-x="849" d="M92 365q0 113 32 227.5t90 213.5t156.5 162.5t219.5 63.5q117 0 246 -100l-80 -94q-94 70 -183 69q-102 0 -179 -92t-109.5 -202.5t-32.5 -209.5q0 -53 10 -104t31.5 -99t64.5 -78t101 -30q49 0 132 48t134 96l59 -82q-109 -115 -252 -164q49 -45 50 -101 q0 -74 -82 -143.5t-166 -101.5l-68 63l21 10l24 13.5l25.5 14.5t30.5 18l26 19q16 11 26 20l19 21q10 11 14.5 23.5t4.5 22.5q0 51 -58 51q-29 0 -102 -18l12 86q-104 37 -160.5 142.5t-56.5 234.5z" /> +<glyph unicode="è" horiz-adv-x="894" d="M92 362q0 158 56.5 307.5t179.5 257t284 107.5q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q-2 -16 -2 -47q0 -100 47 -187.5t139 -87.5q70 0 148 44t133 104l61 -84q-176 -186 -376 -187q-156 0 -235 115.5t-79 277.5zM268 539q426 92 426 284q0 88 -114 88 q-117 0 -202 -120.5t-110 -251.5zM408 1411l147 129q86 -150 268 -299l-80 -92q-210 123 -335 262z" /> +<glyph unicode="é" horiz-adv-x="894" d="M92 362q0 158 56.5 307.5t179.5 257t284 107.5q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q-2 -16 -2 -47q0 -100 47 -187.5t139 -87.5q70 0 148 44t133 104l61 -84q-176 -186 -376 -187q-156 0 -235 115.5t-79 277.5zM268 539q426 92 426 284q0 88 -114 88 q-117 0 -202 -120.5t-110 -251.5zM410 1241q225 147 362 299l107 -129q-160 -137 -418 -262z" /> +<glyph unicode="ê" horiz-adv-x="894" d="M92 362q0 158 56.5 307.5t179.5 257t284 107.5q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q-2 -16 -2 -47q0 -100 47 -187.5t139 -87.5q70 0 148 44t133 104l61 -84q-176 -186 -376 -187q-156 0 -235 115.5t-79 277.5zM260 1253q94 51 222 137.5t180 131.5 q104 -143 243 -275l-92 -90q-125 125 -176 195q-197 -127 -313 -193zM268 539q426 92 426 284q0 88 -114 88q-117 0 -202 -120.5t-110 -251.5z" /> +<glyph unicode="ë" horiz-adv-x="894" d="M92 362q0 158 56.5 307.5t179.5 257t284 107.5q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q-2 -16 -2 -47q0 -100 47 -187.5t139 -87.5q70 0 148 44t133 104l61 -84q-176 -186 -376 -187q-156 0 -235 115.5t-79 277.5zM268 539q426 92 426 284q0 88 -114 88 q-117 0 -202 -120.5t-110 -251.5zM358 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38 -80t-85 -35q-33 0 -55.5 21.5t-22.5 54.5zM674 1276q0 45 35.5 83t81.5 38q35 0 59 -23.5t24 -58.5q0 -45 -37.5 -80t-84.5 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ì" horiz-adv-x="479" d="M90 0l160 1004h151l-159 -1004h-152zM125 1411l147 129q86 -150 269 -299l-80 -92q-211 123 -336 262z" /> +<glyph unicode="í" horiz-adv-x="479" d="M90 0l160 1004h151l-159 -1004h-152zM127 1241q102 66 193.5 140.5t130.5 117.5l41 41l106 -129q-162 -137 -420 -262z" /> +<glyph unicode="î" horiz-adv-x="479" d="M90 1253q94 51 222 137.5t180 131.5q104 -143 243 -275l-92 -90q-125 125 -176 195q-197 -127 -313 -193zM176 0l160 1004h151l-159 -1004h-152z" /> +<glyph unicode="ï" horiz-adv-x="479" d="M90 0l160 1004h151l-159 -1004h-152zM109 1276q0 45 34.5 83t81.5 38q33 0 58.5 -24.5t25.5 -57.5q0 -45 -39 -80t-84 -35q-33 0 -55 21.5t-22 54.5zM424 1276q0 45 35 83t82 38q33 0 58.5 -24.5t25.5 -57.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ñ" horiz-adv-x="1062" d="M90 0l160 1004h156l-31 -189q51 82 157.5 150.5t202.5 68.5q104 0 159.5 -41t55.5 -141q0 -29 -8 -90l-121 -762h-155l120 762q6 55 7 57q0 88 -91 88q-98 0 -200.5 -82t-155.5 -180l-100 -645h-156zM311 1321q53 66 110 91q43 19 101 19q19 0 39 -1q37 -4 127 -46.5 t148 -42.5q72 0 96 101l102 -21q-20 -109 -85 -158q-54 -42 -129 -42q-15 0 -32 2q-59 6 -136 46t-138 40q-37 0 -74 -15.5t-55 -31.5l-20 -17z" /> +<glyph unicode="ò" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM414 1411l147 129q86 -150 268 -299l-79 -92q-211 123 -336 262z" /> +<glyph unicode="ó" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM418 1241q225 147 362 299l107 -129q-162 -137 -418 -262z" /> +<glyph unicode="ô" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM289 1253q94 51 222 137.5t179 131.5q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -314 -193z" /> +<glyph unicode="õ" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM266 1321q53 66 110 91q43 19 101 19q19 0 39 -1q37 -4 127 -46.5t148 -42.5q72 0 96 101l102 -21q-18 -109 -84 -158q-54 -42 -130 -42q-15 0 -32 2q-57 6 -135 46t-139 40q-37 0 -74 -15.5t-55 -31.5l-21 -17z" /> +<glyph unicode="ö" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q162 0 242.5 -124t80.5 -296q0 -141 -56 -286.5t-173 -252t-264 -106.5q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 117 -43 198.5t-148 81.5 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM383 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -56.5 21.5t-23.5 54.5zM698 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -56.5 21.5t-23.5 54.5z" /> +<glyph unicode="÷" horiz-adv-x="1001" d="M111 451v122h778v-122h-778zM377 209q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-89 -38t-85 33t-36 82zM377 817q0 51 40 88t91 37q49 0 84 -32.5t35 -82t-40 -87.5t-89 -38t-85 33t-36 82z" /> +<glyph unicode="ø" horiz-adv-x="1001" d="M92 385q0 143 55.5 288.5t172 253t266.5 107.5q55 0 96 -14l74 166h102l-94 -209q145 -111 145 -363q0 -141 -56 -286.5t-173 -252t-264 -106.5q-55 0 -97 15l-98 -217h-102l117 260q-144 110 -144 358zM246 369q0 -133 49 -209l334 741q-31 10 -64 10 q-109 0 -185.5 -101t-105 -219t-28.5 -222zM371 100q31 -10 65 -10q84 0 148.5 57.5t100.5 145.5t53.5 175t17.5 163q0 137 -51 211z" /> +<glyph unicode="ù" horiz-adv-x="1038" d="M111 156q0 29 8 86l121 762h155l-121 -762q-6 -31 -6 -62q0 -84 94 -84q168 0 347 250l106 658h156l-135 -850q-4 -29 -5 -41q0 -39 35 -41l-67 -103h-6h-6q-49 0 -80 33q-33 35 -33 88t18 103q-63 -90 -162.5 -157t-199.5 -67q-109 0 -164 42.5t-55 144.5zM453 1411 l147 129q86 -150 268 -299l-80 -92q-210 123 -335 262z" /> +<glyph unicode="ú" horiz-adv-x="1038" d="M111 156q0 29 8 86l121 762h155l-121 -762q-6 -31 -6 -62q0 -84 94 -84q168 0 347 250l106 658h156l-135 -850q-4 -29 -5 -41q0 -39 35 -41l-67 -103h-6h-6q-49 0 -80 33q-33 35 -33 88t18 103q-63 -90 -162.5 -157t-199.5 -67q-109 0 -164 42.5t-55 144.5zM430 1241 q225 147 363 299l106 -129q-160 -137 -418 -262z" /> +<glyph unicode="û" horiz-adv-x="1038" d="M111 156q0 29 8 86l121 762h155l-121 -762q-6 -31 -6 -62q0 -84 94 -84q168 0 347 250l106 658h156l-135 -850q-4 -29 -5 -41q0 -39 35 -41l-67 -103h-6h-6q-49 0 -80 33q-33 35 -33 88t18 103q-63 -90 -162.5 -157t-199.5 -67q-109 0 -164 42.5t-55 144.5zM326 1253 q94 51 222 137.5t179 131.5q104 -143 244 -275l-92 -90q-125 125 -177 195q-197 -127 -313 -193z" /> +<glyph unicode="ü" horiz-adv-x="1038" d="M111 156q0 29 8 86l121 762h155l-121 -762q-6 -31 -6 -62q0 -84 94 -84q168 0 347 250l106 658h156l-135 -850q-4 -29 -5 -41q0 -39 35 -41l-67 -103h-6h-6q-49 0 -80 33q-33 35 -33 88t18 103q-63 -90 -162.5 -157t-199.5 -67q-109 0 -164 42.5t-55 144.5zM406 1276 q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55 21.5t-22 54.5zM721 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ý" horiz-adv-x="882" d="M-16 -276q102 41 174.5 108.5t136.5 169.5l-211 1002h166l149 -832l297 504q20 37 21 94q0 106 -43 217l151 31q37 -92 37 -205q0 -119 -53 -207l-389 -649q-80 -135 -171 -215t-229 -123zM801 1249q225 147 362 299l107 -129q-160 -137 -418 -262z" /> +<glyph unicode="ÿ" horiz-adv-x="882" d="M-16 -276q102 41 174.5 108.5t136.5 169.5l-211 1002h166l149 -832l297 504q20 37 21 94q0 106 -43 217l151 31q37 -92 37 -205q0 -119 -53 -207l-389 -649q-80 -135 -171 -215t-229 -123zM274 1276q0 45 35 83t82 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35 q-33 0 -55.5 21.5t-22.5 54.5zM590 1276q0 45 34.5 83t82.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Œ" horiz-adv-x="1282" d="M119 481q0 92 14 172q16 104 57 211t103.5 205t159 159.5t206.5 61.5h666l-22 -137h-500l-64 -410h383l-22 -141h-383l-74 -461h520l-22 -141h-686q-94 0 -163 43t-104.5 116.5t-52 153.5t-16.5 168zM270 446q0 -317 207 -317l164 1030q-98 0 -175 -80t-117 -198.5 t-59.5 -231.5t-19.5 -203z" /> +<glyph unicode="œ" horiz-adv-x="1486" d="M92 385q0 141 56.5 287.5t173 254t264.5 107.5q184 0 270 -170q147 170 348 170q113 0 181.5 -57t68.5 -166q0 -270 -606 -397q0 -8 -1 -24.5t-1 -22.5q0 -100 48 -187.5t138 -87.5q72 0 148.5 44t134.5 104l59 -84q-176 -186 -377 -187q-180 0 -262 160 q-139 -160 -319 -160q-162 0 -243 123t-81 293zM246 369q0 -115 43 -197t147 -82q160 0 252 209q-4 41 -4 63q0 178 68 332q-20 217 -187 217q-84 0 -148.5 -57t-100 -145t-53 -176t-17.5 -164zM860 539q426 92 426 284q0 88 -115 88q-117 0 -201.5 -120.5t-109.5 -251.5z " /> +<glyph unicode="Ÿ" horiz-adv-x="882" d="M92 1196l107 113q72 -45 136 -139.5t91 -180.5q52 -157 52 -272q0 -16 -1 -31l31 115q37 137 150.5 301t228.5 223l98 -104q-172 -104 -286.5 -343t-159.5 -483l-70 -395h-162l74 457q-8 133 -32.5 263t-91.5 272.5t-165 203.5zM350 1542q0 45 36 83t81 38 q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -56.5 21.5t-23.5 54.5zM666 1542q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -37.5 -80t-83.5 -35q-33 0 -56 21.5t-23 54.5z" /> +<glyph unicode="ˆ" horiz-adv-x="737" d="M84 1253q94 51 222 137.5t179 131.5q104 -143 244 -275l-92 -90q-125 125 -176 195q-197 -127 -314 -193z" /> +<glyph unicode="˜" horiz-adv-x="763" d="M23 1321q53 66 109 91q43 19 101 19q19 0 39 -1q37 -4 127 -46.5t148 -42.5q72 0 96 101l102 -21q-20 -109 -85 -158q-54 -42 -129 -42q-15 0 -31 2q-59 6 -136 46t-139 40q-37 0 -73.5 -15.5t-57.5 -31.5l-18 -17z" /> +<glyph unicode="–" horiz-adv-x="733" d="M0 461v123h733v-123h-733z" /> +<glyph unicode="—" horiz-adv-x="1245" d="M0 463v123h1245v-123h-1245z" /> +<glyph unicode="‘" horiz-adv-x="559" d="M199 1008q109 166 315 335l82 -75q-197 -211 -180 -350q-45 -23 -88 -23q-66 0 -129 113z" /> +<glyph unicode="’" horiz-adv-x="610" d="M160 938q181 195 181 328q0 11 -1 22q49 23 90 23q68 0 127 -113q-111 -170 -313 -336z" /> +<glyph unicode="‚" horiz-adv-x="528" d="M-51 -229q117 139 162 239q14 35 21 69t7 48l-2 16q63 35 103 35q59 -2 114 -94q-113 -178 -323 -377z" /> +<glyph unicode="“" horiz-adv-x="944" d="M203 1008q109 166 315 335l82 -75q-181 -195 -181 -329q0 -11 1 -21q-45 -23 -88 -23q-66 0 -129 113zM565 1008q109 166 316 335l82 -75q-181 -195 -182 -329q0 -11 1 -21q-45 -23 -88 -23q-66 0 -129 113z" /> +<glyph unicode="”" horiz-adv-x="942" d="M166 938q181 195 181 328q0 11 -1 22q49 23 90 23q68 0 127 -113q-111 -170 -313 -336zM528 938q181 195 182 328q0 11 -1 22q49 23 90 23q68 0 127 -113q-111 -170 -314 -336z" /> +<glyph unicode="„" horiz-adv-x="935" d="M-43 -201q181 195 181 329q0 11 -1 22q45 23 88 22q68 0 127 -113q-111 -170 -313 -335zM319 -201q181 195 182 329q0 11 -1 22q45 23 88 22q68 0 127 -113q-111 -170 -314 -335z" /> +<glyph unicode="•" horiz-adv-x="868" d="M80 645.5q0 145.5 103.5 248.5t249 103t249.5 -103t104 -248.5t-104 -249t-249.5 -103.5t-249 103.5t-103.5 249z" /> +<glyph unicode="…" horiz-adv-x="1204" d="M78 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM430 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM778 84q0 51 39 88t90 37q49 0 85 -33t36 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="‹" horiz-adv-x="589" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="›" horiz-adv-x="667" d="M133 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="™" horiz-adv-x="1583" d="M225 1325l15 92h497l-14 -92h-199l-114 -719h-101l115 719h-199zM721 608l129 813h137l119 -555q18 -106 20 -131q45 106 58 131l280 555h138l-129 -813h-105l88 553l25 111l-326 -664h-90l-141 666q-2 -55 -13 -111l-86 -555h-104z" /> +<glyph unicode="" horiz-adv-x="1020" d="M0 1020h1020v-1020h-1020v1020z" /> +<glyph unicode="fi" horiz-adv-x="1056" d="M20 -330q121 203 150 475q2 12 37 404q20 213 29 336l-144 18l17 101h133l12 167q12 150 127 215.5t278 65.5q86 0 221.5 -28.5t159.5 -80.5q6 -18 7 -36q0 -49 -42 -89t-92 -40q-27 0 -53 18t-26 43q0 10 4 26.5t4 26.5q0 33 -52.5 46.5t-101.5 13.5 q-117 0 -198.5 -56.5t-86.5 -167.5q-4 -82 -4 -124h166l-2 -117l-170 -6q-10 -158 -43 -453q-25 -262 -41 -391q-25 -180 -198 -420zM659 0l158 1004h152l-158 -1004h-152z" /> +<glyph unicode="fl" horiz-adv-x="1056" d="M20 -330l36 67t63 167t51 241q57 610 66 740l-144 18l17 101h133l12 167q10 129 100 199t215 77q35 2 70 2q89 0 176 -13q121 -18 215 -58l-156 -975q-16 -111 -16 -195q0 -116 31 -183l-148 -43q-6 18 -13 52t-17 146q-3 35 -4 70q0 77 16 153l143 900q-59 37 -137 46 q-26 3 -51 3q-51 0 -98 -13q-72 -18 -123 -73.5t-54 -137.5l-4 -124h166l-2 -117l-170 -6l-84 -844q-8 -76 -57 -181.5t-96 -170.5l-45 -68z" /> +<glyph unicode="ffi" horiz-adv-x="1581" d="M20 -330q125 209 150 475q49 494 66 740l-144 18l17 101h133l6 96q16 322 241 321q47 0 93 -16l-15 -111q-18 6 -43 6q-113 0 -121 -208q-4 -66 -4 -88h166l-2 -117l-172 -6q-8 -123 -24.5 -318.5t-30.5 -329.5l-12 -133q-20 -225 -213 -483zM571 -330q125 209 150 475 q49 494 65 740l-143 18l16 101h134l6 96q16 322 241 321q47 0 93 -16l-15 -111q-18 6 -43 6q-113 0 -121 -208q-4 -66 -4 -88h166l-2 -117l-172 -6q-8 -123 -24.5 -318.5t-30.5 -329.5l-13 -133q-20 -225 -212 -483zM1192 0l160 1004h151l-160 -1004h-151zM1346 1286 q0 49 39.5 87t91.5 38q49 0 83.5 -32.5t34.5 -82.5q0 -51 -39.5 -88t-91.5 -37q-49 0 -83.5 33t-34.5 82z" /> +<glyph unicode="ffl" horiz-adv-x="1597" d="M20 -330q125 209 150 475q49 494 66 740l-144 18l17 101h133l6 96q16 322 241 321q47 0 93 -16l-15 -111q-18 6 -43 6q-113 0 -121 -208q-4 -66 -4 -88h166l-2 -117l-172 -6q-8 -123 -24.5 -318.5t-30.5 -329.5l-12 -133q-20 -225 -213 -483zM571 -330q125 209 150 475 q49 494 65 740l-143 18l16 101h134l6 96q16 322 241 321q47 0 93 -16l-15 -111q-18 6 -43 6q-113 0 -121 -208q-4 -66 -4 -88h166l-2 -117l-172 -6q-8 -123 -24.5 -318.5t-30.5 -329.5l-13 -133q-20 -225 -212 -483zM1239 225q0 94 14 178l162 1018h152l-162 -1018 q-16 -94 -16 -182q0 -133 28 -196l-145 -43q-33 91 -33 243z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-italic-webfont.ttf b/app/assets/fonts/delicious-italic-webfont.ttf Binary files differnew file mode 100755 index 000000000..01d11991b --- /dev/null +++ b/app/assets/fonts/delicious-italic-webfont.ttf diff --git a/app/assets/fonts/delicious-italic-webfont.woff b/app/assets/fonts/delicious-italic-webfont.woff Binary files differnew file mode 100755 index 000000000..32aa7611e --- /dev/null +++ b/app/assets/fonts/delicious-italic-webfont.woff diff --git a/app/assets/fonts/delicious-roman-webfont.eot b/app/assets/fonts/delicious-roman-webfont.eot Binary files differnew file mode 100755 index 000000000..2e1b7b24e --- /dev/null +++ b/app/assets/fonts/delicious-roman-webfont.eot diff --git a/app/assets/fonts/delicious-roman-webfont.svg b/app/assets/fonts/delicious-roman-webfont.svg new file mode 100755 index 000000000..c7c20b31c --- /dev/null +++ b/app/assets/fonts/delicious-roman-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : copyright 19941996 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousRoman" horiz-adv-x="921" > +<font-face units-per-em="2048" ascent="1638" descent="-410" /> +<missing-glyph horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="	" horiz-adv-x="614" /> +<glyph unicode=" " horiz-adv-x="614" /> +<glyph unicode="!" horiz-adv-x="540" d="M174 84q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM213 1085q0 180 31 326l155 21q-35 -203 -34 -396v-704h-123q-29 528 -29 753z" /> +<glyph unicode=""" horiz-adv-x="708" d="M131 1321l27 129h100l29 -129l-45 -322h-68zM414 1321l26 129h101l28 -129l-45 -322h-67z" /> +<glyph unicode="#" horiz-adv-x="1138" d="M125 436l20 135h164l39 312h-145l20 135h142l51 403h123l-52 -403h236l45 364h123l-45 -364h174l-12 -135h-181l-39 -312h154l-12 -135h-158l-53 -418h-123l53 418h-235l-47 -379h-123l47 379h-166zM432 571h234l39 312h-234z" /> +<glyph unicode="$" horiz-adv-x="911" d="M127 983q0 147 86 236.5t233 99.5v113h93v-115q147 -18 249 -98l-55 -111q-82 59 -194 78v-467q158 -100 206 -152q92 -98 93 -248q0 -135 -83 -228t-216 -116v-124h-93v118q-164 4 -309 88l47 123q154 -76 262 -82v484l-46 29l-49 32l-43 30q-34 24 -44 33l-37 33 q-27 25 -35 40.5t-25 41t-23.5 49t-11.5 53t-5 60.5zM287 997q0 -55 25.5 -98t51 -63.5t82.5 -59.5v412q-76 -12 -117.5 -63.5t-41.5 -127.5zM539 113q68 23 104.5 79t36.5 130q0 111 -141 202v-411z" /> +<glyph unicode="%" horiz-adv-x="1431" d="M94 1048.5q0 112.5 80 192.5t193 80q80 0 145 -41q82 -23 143 -23q57 0 114.5 15.5t86.5 30.5l27 16l104 -29l-455 -1290h-135l432 1196q-35 -18 -88 -27.5t-90 -9.5l-35 -2q23 -43 23 -108q0 -113 -80 -193t-192.5 -80t-192.5 80t-80 192.5zM215 1048.5 q0 -63.5 44 -107.5t107.5 -44t107.5 44t44 107.5t-44 107.5t-107.5 44t-107.5 -44t-44 -107.5zM788 241.5q0 112.5 80 192.5t193 80t192.5 -80t79.5 -192.5t-79.5 -192.5t-192.5 -80t-193 80t-80 192.5zM909 241.5q0 -63.5 44 -107.5t107.5 -44t107.5 44t44 107.5t-44 107.5 t-107.5 44t-107.5 -44t-44 -107.5z" /> +<glyph unicode="&" horiz-adv-x="1290" d="M86 479q0 174 90 324.5t252 157.5q-141 59 -141 184q0 123 107.5 199.5t236.5 76.5q141 0 249.5 -77.5t108.5 -213.5v-126h203v-119h-199v-678q0 -113 80 -113q53 0 119 29l35 -96q-115 -57 -209 -58q-96 0 -138 64.5t-42 165.5v686h-199q-203 0 -290 -97.5t-87 -302.5 q0 -193 64 -313q41 -76 77 -76q23 0 56 34t79 67.5t103 33.5q51 0 100 -22l-30 -111q-41 12 -64 13q-47 0 -112.5 -71t-133.5 -71q-84 0 -148.5 52.5t-98 134.5t-51 164.5t-17.5 158.5zM442 1145q0 -76 65.5 -108.5t149.5 -32.5h189v96q0 94 -51.5 155.5t-143.5 61.5 q-82 0 -145.5 -46t-63.5 -126z" /> +<glyph unicode="'" horiz-adv-x="425" d="M131 1321l27 129h100l29 -129l-45 -322h-68z" /> +<glyph unicode="(" horiz-adv-x="688" d="M96 680q0 272 140.5 518t373.5 391l17 -116q-190 -125 -300 -343.5t-110 -453.5q0 -518 412 -848l-15 -127q-262 199 -390 431.5t-128 547.5z" /> +<glyph unicode=")" horiz-adv-x="694" d="M63 -172q414 330 414 848q0 236 -110.5 454t-300.5 343l16 116q233 -145 373.5 -391t140.5 -518q0 -315 -128 -547.5t-390 -431.5z" /> +<glyph unicode="*" horiz-adv-x="868" d="M72 1128v132l307 -111l-74 231h238l-70 -231l305 106v-127l-268 -86l217 -333h-143l-158 252l-156 -252h-133l201 333z" /> +<glyph unicode="+" horiz-adv-x="964" d="M92 451v122h330v355h119v-355h331v-122h-331v-355h-119v355h-330z" /> +<glyph unicode="," horiz-adv-x="528" d="M59 -229q98 141 123 239q10 41 11 84l-5 49q57 35 97 35q27 0 59.5 -23.5t52.5 -48.5l19 -22q-84 -178 -264 -377z" /> +<glyph unicode="-" horiz-adv-x="618" d="M111 461v123h391v-123h-391z" /> +<glyph unicode="." horiz-adv-x="528" d="M147 84q0 51 40 88t92 37q49 0 83.5 -33t34.5 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="/" horiz-adv-x="729" d="M35 -303l530 1853h129l-530 -1853h-129z" /> +<glyph unicode="0" d="M57 502q0 133 39 249.5t133.5 199.5t231.5 83t231 -83t133 -199.5t39 -249.5t-39 -250t-133 -200t-231 -83t-231.5 83t-133.5 200t-39 250zM205 502q0 -92 20.5 -177t81 -156t154.5 -71t154.5 71t81 156t20.5 177t-20.5 177t-81 155.5t-154.5 70.5t-154.5 -70.5 t-81 -155.5t-20.5 -177z" /> +<glyph unicode="1" d="M203 858v98q88 0 194 29l74 19h141v-1004h-151v858h-258z" /> +<glyph unicode="2" d="M127 127q156 84 325 279.5t169 343.5q0 74 -52.5 113.5t-128.5 39.5q-133 0 -284 -117l-29 119q92 72 161.5 100.5t176.5 28.5q143 0 228 -70.5t85 -211.5q0 -188 -190 -404q-131 -147 -228 -213h455v-135h-659z" /> +<glyph unicode="3" d="M147 -225q68 0 146 20q358 82 358 365q0 193 -289 192q-59 0 -174 -12v119l91 12q133 72 258 172q90 70 90 152q0 51 -39 81.5t-92 30.5q-51 0 -142.5 -38t-156.5 -76l-37 108q207 133 370 133q109 0 179.5 -63.5t70.5 -171.5q0 -57 -24.5 -106.5t-73.5 -91.5t-86 -66.5 t-98 -59.5q129 0 218 -78t89 -204q0 -178 -95.5 -304.5t-238.5 -180.5q-138 -52 -303 -52h-12h-9v119z" /> +<glyph unicode="4" d="M63 133q139 387 502 871h168v-871h131v-133h-135v-283h-145v283h-512zM215 133h369v680q-242 -354 -369 -680z" /> +<glyph unicode="5" d="M137 -221q164 4 312 80q193 100 192 305q0 98 -65.5 145t-167.5 47q-98 0 -236 -41l57 689h508v-123h-366l-33 -412q57 12 86 12q10 0 26.5 -1t26.5 -1q133 -6 226.5 -80.5t93.5 -201.5q0 -322 -285 -457q-168 -80 -349 -80h-18h-8v119z" /> +<glyph unicode="6" d="M96 524q0 264 160 465t420 326l55 -101q-184 -115 -284.5 -213t-157.5 -264q127 47 215 47q150 0 244 -95t94 -245q0 -125 -37 -229t-125 -175t-215 -71q-102 0 -178 53.5t-115 139.5t-57.5 176t-18.5 186zM246 481q0 -82 18.5 -163.5t73.5 -153.5t139 -72q213 0 213 359 q0 113 -64.5 173t-179.5 60q-66 0 -184 -51q-16 -78 -16 -152z" /> +<glyph unicode="7" d="M121 872v132h672v-132q0 -272 -137.5 -640.5t-313.5 -591.5l-135 57q180 238 309 570.5t129 604.5h-524z" /> +<glyph unicode="8" d="M84 317q0 117 66.5 211.5t177.5 145.5q-102 61 -154.5 128.5t-52.5 172.5q0 143 100.5 229t247.5 86q152 0 237 -77.5t85 -229.5q0 -96 -69 -174t-167 -121q66 -33 91.5 -48t73.5 -49t68.5 -63.5t38 -77t17.5 -104.5q0 -174 -110.5 -275.5t-287.5 -101.5 q-147 0 -254.5 100.5t-107.5 247.5zM238 340q0 -100 64.5 -177t162.5 -77t161.5 76t63.5 176q0 49 -13 86t-45 65.5t-54.5 45t-74.5 44t-73 39.5q-192 -100 -192 -278zM266 967q0 -82 44 -132.5t128 -93.5l69 36q13 7 52 33t50.5 46.5t24.5 55t13 79.5q0 188 -194 189 q-84 0 -135.5 -63.5t-51.5 -149.5z" /> +<glyph unicode="9" d="M88 557q0 199 92 338t281 139q102 0 179 -56t118 -146.5t59.5 -184.5t18.5 -188q0 -266 -190.5 -480.5t-461.5 -324.5l-43 117q203 102 321 208.5t179 284.5q-131 -47 -219 -47q-68 0 -131 29q-203 86 -203 311zM240 551q0 -98 61 -165t160 -67q119 0 207 54 q16 78 16 151q0 82 -18.5 163t-73.5 152.5t-139 71.5q-213 0 -213 -360z" /> +<glyph unicode=":" horiz-adv-x="528" d="M147 84q0 51 40 88t92 37q49 0 83.5 -33t34.5 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM147 713q0 51 40 88t92 37q49 0 83.5 -33t34.5 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode=";" horiz-adv-x="528" d="M53 -229q98 141 123 239q10 41 10 84l-4 49q57 35 97 35q27 0 59.5 -23.5t50.5 -48.5l21 -22q-84 -178 -265 -377zM147 709q0 51 40 89t92 38q49 0 83.5 -33t34.5 -82q0 -51 -40 -89t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="<" horiz-adv-x="614" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="=" horiz-adv-x="983" d="M90 293v123h782v-123h-782zM90 598v123h782v-123h-782z" /> +<glyph unicode=">" horiz-adv-x="614" d="M68 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="?" horiz-adv-x="733" d="M147 84q0 51 40 88t92 37q49 0 83.5 -33t34.5 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM211 332v104q0 86 2 127t20.5 103.5t55.5 111.5l168 226q70 96 69 161q0 57 -30.5 92t-87.5 35q-88 0 -172 -18v117q96 27 184 26q121 0 190.5 -63.5t69.5 -184.5q0 -98 -100 -229 l-164 -217q-45 -59 -62.5 -120.5t-17.5 -166.5v-104h-125z" /> +<glyph unicode="@" horiz-adv-x="1622" d="M88 727q0 299 223.5 529.5t519.5 230.5q295 0 490 -179.5t195 -471.5q0 -113 -56.5 -236t-160 -212t-218.5 -89q-72 0 -119 35t-47 104q0 12 5 33q-119 -102 -175 -127q-45 -20 -90 -20q-98 0 -152 80.5t-54 183.5q0 94 18 185t58 178t116 141.5t176 54.5q145 0 225 -117 l21 84h133l-141 -592q-12 -45 -13 -67q0 -55 56 -56q125 0 218 151.5t93 285.5q0 252 -163 394t-419 142q-254 0 -429 -195.5t-175 -453.5q0 -270 179.5 -457.5t449.5 -187.5q152 0 292 65.5t230 182.5l96 -52q-94 -139 -270 -225t-348 -86q-319 0 -541.5 222.5 t-222.5 541.5zM588 610q0 -59 21.5 -106t74.5 -47q18 0 43 6t41 12t47 22.5t41 22.5l43 26q33 19 35 21l84 369q-76 98 -205 98q-78 0 -132 -83t-73.5 -174t-19.5 -167z" /> +<glyph unicode="A" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM369 551h307l-154 569z" /> +<glyph unicode="B" horiz-adv-x="1028" d="M164 20v1270h317q123 0 203 -30q190 -76 190 -295q0 -86 -57 -169t-137 -106q125 -8 195.5 -97t70.5 -218q0 -195 -131 -300.5t-330 -105.5q-124 0 -321 51zM328 745h151q104 0 169 59.5t65 162.5q0 197 -234 196h-151v-418zM328 135q92 -27 153 -26q135 0 218 67.5 t83 200.5q0 74 -23.5 123t-69.5 72.5t-96 31.5t-122 8h-143v-477z" /> +<glyph unicode="C" horiz-adv-x="1017" d="M113 645q0 119 29.5 233.5t87 216t157.5 164t227 62.5q195 0 332 -88l-59 -107q-119 66 -273 66q-86 0 -154.5 -58.5t-106.5 -147.5t-57.5 -180t-19.5 -169q0 -90 16.5 -176t52.5 -171t106.5 -136.5t166.5 -51.5q166 0 279 84l70 -102q-68 -53 -166.5 -84t-190.5 -31 q-135 0 -235.5 60.5t-155.5 161t-80.5 214t-25.5 240.5z" /> +<glyph unicode="D" horiz-adv-x="1120" d="M164 0v1290h373q133 0 228 -56t146.5 -152.5t74 -205t22.5 -233.5t-27 -234.5t-82 -203.5t-153.5 -149.5t-229.5 -55.5h-352zM326 131h190q98 0 166 47t100.5 128t46 161t13.5 172q0 520 -314 520h-202v-1028z" /> +<glyph unicode="E" horiz-adv-x="929" d="M164 0v1290h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684z" /> +<glyph unicode="F" horiz-adv-x="843" d="M164 0v1290h606v-135h-442v-412h350v-141h-350v-602h-164z" /> +<glyph unicode="G" horiz-adv-x="1073" d="M113 645q0 119 29.5 233.5t87 216t157.5 164t227 62.5q96 0 168 -20.5t166 -67.5l-63 -109q-113 70 -271 70q-111 0 -190.5 -96.5t-113.5 -219t-34 -241.5q0 -90 15.5 -176t51.5 -171t106.5 -137.5t168.5 -52.5q109 0 181 29v422h151v-528q-129 -53 -336 -54 q-135 0 -236.5 59.5t-156.5 160t-81.5 215t-26.5 241.5z" /> +<glyph unicode="H" horiz-adv-x="1122" d="M164 0v1290h164v-547h467v547h163v-1290h-163v604h-467v-604h-164z" /> +<glyph unicode="I" horiz-adv-x="487" d="M164 0v1290h160v-1290h-160z" /> +<glyph unicode="J" horiz-adv-x="501" d="M12 -223q166 55 166 178v1335h160v-1347q0 -205 -291 -277z" /> +<glyph unicode="K" horiz-adv-x="993" d="M164 0v1290h164v-590l450 590h174l-495 -663l520 -578v-49h-164l-485 539v-539h-164z" /> +<glyph unicode="L" horiz-adv-x="782" d="M164 0v1290h155v-1155h445v-135h-600z" /> +<glyph unicode="M" horiz-adv-x="1519" d="M164 0v1290h217l379 -1089l379 1089h217v-1290h-164v877l10 178l-371 -1055h-143l-371 1057q10 -106 11 -176v-881h-164z" /> +<glyph unicode="N" horiz-adv-x="1134" d="M164 0v1290h217l436 -987q-10 98 -10 180v807h164v-1290h-168l-486 1053q10 -96 11 -176v-877h-164z" /> +<glyph unicode="O" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5 t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156z" /> +<glyph unicode="P" horiz-adv-x="1001" d="M164 0v1290h278q211 0 345.5 -98t134.5 -299q0 -190 -114 -311t-304 -121q-100 0 -176 41v-502h-164zM328 637q98 -43 166 -43q121 0 192.5 89t71.5 212q0 133 -89 198.5t-227 65.5h-114v-522z" /> +<glyph unicode="Q" horiz-adv-x="1122" d="M113 645q0 111 23.5 222.5t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222.5q0 -209 -80 -402.5t-244 -252.5q129 -137 262 -137q41 0 74 8l-43 -156q-33 -4 -49 -4q-51 0 -96.5 13.5t-88.5 45t-67.5 52t-69.5 66.5l-57 59l-37 34q-106 12 -188 83 t-126 172.5t-65.5 208t-21.5 210.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156z" /> +<glyph unicode="R" horiz-adv-x="1034" d="M164 0v1290h278q211 0 345.5 -98t134.5 -299q0 -158 -84 -274.5t-236 -147.5l381 -422v-49h-160l-424 471q-39 12 -71 31v-502h-164zM328 637q94 -43 151 -43q129 0 204 85t75 216q0 133 -89 198.5t-227 65.5h-114v-522z" /> +<glyph unicode="S" horiz-adv-x="888" d="M92 983q0 162 97.5 250t261.5 88q88 0 173 -41t130 -111l-92 -77q-78 98 -211 98q-86 0 -142.5 -49t-56.5 -133q0 -82 57.5 -146.5t138 -109.5t161.5 -96.5t138.5 -136.5t57.5 -200q0 -158 -110.5 -254t-270.5 -96q-76 0 -161 34t-146 87l69 117q129 -106 240 -107 q96 0 158.5 62.5t62.5 159.5q0 72 -57.5 132t-137.5 107l-163 99q-82 52 -139.5 134.5t-57.5 188.5z" /> +<glyph unicode="T" horiz-adv-x="829" d="M20 1147v143h789v-143h-315v-1147h-160v1147h-314z" /> +<glyph unicode="U" horiz-adv-x="1142" d="M162 502v788h164v-790q0 -395 245.5 -395.5t245.5 397.5v788h164v-796q0 -225 -98.5 -375t-311.5 -150t-311 152t-98 381z" /> +<glyph unicode="V" horiz-adv-x="1044" d="M12 1290h178l336 -1093l338 1093h170l-422 -1290h-180z" /> +<glyph unicode="W" horiz-adv-x="1449" d="M20 1290h175l198 -913q16 -72 17 -135l235 1048h164l235 -1048q0 63 17 135l207 913h166l-304 -1290h-174l-229 1042l-229 -1042h-174z" /> +<glyph unicode="X" horiz-adv-x="1007" d="M20 0l383 662l-372 628h178l285 -473l274 473h168l-354 -620l401 -670h-188l-303 512l-293 -512h-179z" /> +<glyph unicode="Y" horiz-adv-x="964" d="M27 1290h174l286 -643l289 643h164l-377 -817v-473h-162v473z" /> +<glyph unicode="Z" horiz-adv-x="937" d="M74 0v139q283 559 557 1010h-512v141h702v-141q-301 -496 -567 -1006h623v-143h-803z" /> +<glyph unicode="[" horiz-adv-x="507" d="M57 -319v1818h318v-119h-191v-1581h191v-118h-318z" /> +<glyph unicode="\" horiz-adv-x="686" d="M25 1550h127l530 -1853h-127z" /> +<glyph unicode="]" horiz-adv-x="509" d="M135 -201h191v1581h-191v119h318v-1818h-318v118z" /> +<glyph unicode="^" horiz-adv-x="1277" d="M145 655l435 795h114l434 -795h-143l-348 629l-348 -629h-144z" /> +<glyph unicode="_" d="M0 -154h922v-122h-922v122z" /> +<glyph unicode="`" horiz-adv-x="692" d="M92 1411l127 129q106 -147 316 -299l-66 -92q-240 127 -377 262z" /> +<glyph unicode="a" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106z" /> +<glyph unicode="b" horiz-adv-x="1030" d="M168 92v1329h156v-493q43 49 113.5 77.5t135.5 28.5q172 0 264.5 -146.5t92.5 -328.5q0 -92 -12.5 -172t-43 -159t-78.5 -135t-125 -90t-175 -34q-78 0 -169 36t-159 87zM324 178q35 -39 92 -61.5t110 -22.5q72 0 123 46t76 120t35 141.5t10 135.5q0 63 -8 119.5 t-30.5 117t-69 96t-111.5 35.5q-59 0 -122.5 -32.5t-104.5 -79.5v-615z" /> +<glyph unicode="c" horiz-adv-x="843" d="M100 492q0 129 39 247.5t134.5 206.5t228.5 88q156 0 262 -102l-70 -92q-86 72 -190 71q-86 0 -145.5 -71.5t-81 -157.5t-21.5 -170q0 -90 21.5 -180t84 -165t154.5 -75q86 0 225 90l52 -96q-57 -49 -137.5 -83t-151.5 -34q-197 0 -300.5 158t-103.5 365z" /> +<glyph unicode="d" horiz-adv-x="1048" d="M102 444q0 119 20.5 218.5t68 186.5t134.5 136t207 49q88 0 197 -30v417h156v-1233q0 -113 55 -118l-43 -101q-78 4 -113 39q-47 51 -47 142q-23 -86 -102.5 -133.5t-173.5 -47.5q-172 0 -265.5 146.5t-93.5 328.5zM262 467q0 -55 11.5 -114.5t34 -121t68.5 -100.5 t105 -39q115 0 181.5 121t66.5 246v413q-88 37 -184 37q-283 0 -283 -442z" /> +<glyph unicode="e" horiz-adv-x="964" d="M100 528q0 197 118 351.5t308 154.5q145 0 241.5 -114.5t106.5 -264.5q2 -16 2.5 -51t2.5 -53h-619v-39q0 -84 20.5 -174t79 -168t144.5 -78q100 0 284 127l50 -98q-72 -61 -170.5 -106.5t-180.5 -45.5q-104 0 -183 52.5t-121 138.5t-62.5 179t-20.5 189zM279 668h436 q0 94 -55.5 168.5t-147.5 74.5q-90 0 -151.5 -73.5t-81.5 -169.5z" /> +<glyph unicode="f" horiz-adv-x="585" d="M51 903v101h135v159q0 242 351 285l28 -121q-8 0 -26.5 -2t-54 -14.5t-66.5 -30.5t-53.5 -56t-22.5 -87v-133h199v-119h-199v-885h-156v885z" /> +<glyph unicode="g" horiz-adv-x="931" d="M82 -170q0 47 15.5 85t49 67.5t60 47t76.5 44.5q-55 6 -95.5 42t-40.5 87q0 37 15.5 66.5t48.5 52t56.5 34t64.5 27.5q-98 35 -157.5 121t-59.5 190q0 152 96 246t248 94q90 0 149 -30h277v-123h-146q47 -82 47 -192.5t-70.5 -197t-178.5 -118.5q-4 -2 -23.5 -7.5 t-30 -9.5t-32 -11t-36 -13t-32.5 -15.5t-30 -18.5l-23 -19q-12 -10 -17.5 -23.5t-5.5 -26.5q0 -72 254 -71h70q100 0 168.5 -56.5t68.5 -152.5q0 -166 -137 -262.5t-309 -96.5q-137 0 -238.5 56.5t-101.5 183.5zM233 -139q0 -80 56.5 -119t140.5 -39q102 0 194.5 64.5 t92.5 162.5q0 119 -197 119h-121q-37 -20 -58.5 -34.5t-50 -39t-43 -53t-14.5 -61.5zM264 700q0 -86 51.5 -156.5t135.5 -70.5t135 70.5t51 156.5t-51 157t-135 71t-135.5 -71t-51.5 -157z" /> +<glyph unicode="h" horiz-adv-x="1062" d="M168 0v1421h156v-491q55 39 151 71.5t174 32.5q147 0 198.5 -69.5t51.5 -202.5v-762h-156v762q0 76 -24.5 110.5t-95.5 34.5q-78 0 -167 -33.5t-132 -70.5v-803h-156z" /> +<glyph unicode="i" horiz-adv-x="487" d="M123 1284q0 47 35 78t82 31q45 0 78.5 -30t33.5 -75q0 -47 -34.5 -77.5t-81.5 -30.5q-45 0 -79 29.5t-34 74.5zM168 0v1004h151v-1004h-151z" /> +<glyph unicode="j" horiz-adv-x="487" d="M2 -285q166 63 166 181v1108h151v-1123q0 -195 -282 -274zM123 1284q0 47 35 78t82 31q45 0 78.5 -30t33.5 -75q0 -47 -34.5 -77.5t-81.5 -30.5q-45 0 -79 29.5t-34 74.5z" /> +<glyph unicode="k" horiz-adv-x="964" d="M168 0v1421h156v-837l362 420h184l-368 -433l454 -534v-37h-163l-469 559v-559h-156z" /> +<glyph unicode="l" horiz-adv-x="483" d="M156 403v1018h151v-1018q0 -203 62 -403h-162q-51 139 -51 403z" /> +<glyph unicode="m" horiz-adv-x="1579" d="M168 0v1004h156v-74q145 104 276 104q182 0 238 -106q53 43 146 74.5t161 31.5q270 0 270 -272v-762h-155v762q0 74 -29 109.5t-101 35.5q-150 0 -262 -92q2 -18 2 -53v-762h-155v762q0 74 -29 109.5t-100 35.5q-131 0 -262 -104v-803h-156z" /> +<glyph unicode="n" horiz-adv-x="1062" d="M168 0v1004h156v-74q55 39 151 71.5t174 32.5q147 0 198.5 -69.5t51.5 -202.5v-762h-156v762q0 76 -24.5 110.5t-95.5 34.5q-78 0 -168 -34.5t-131 -69.5v-803h-156z" /> +<glyph unicode="o" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM256 500q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5 t-82 -163.5t-23.5 -175z" /> +<glyph unicode="p" horiz-adv-x="1056" d="M172 -360v1364h156v-76q51 47 126.5 76.5t143.5 29.5q172 0 264 -146.5t92 -328.5q0 -78 -9 -152.5t-33.5 -156.5t-64.5 -142.5t-107.5 -99.5t-153.5 -39q-188 0 -258 158v-487h-156zM328 498q0 -68 10 -131.5t35.5 -129t78 -105.5t123.5 -40q68 0 115 47t68.5 124 t29 141.5t7.5 132.5q0 63 -8.5 120.5t-31 119t-68.5 98t-112 36.5q-61 0 -128.5 -34.5t-118.5 -83.5v-295z" /> +<glyph unicode="q" horiz-adv-x="1056" d="M102 481q0 96 24 190.5t70 177.5t126.5 134t183.5 51q150 0 381 -63v-1331h-156v487q-74 -158 -258 -158q-98 0 -173 47t-116 124t-61.5 163t-20.5 178zM264 537q0 -442 217 -443q51 0 99 29q152 92 151 375v376q-84 33 -190 33q-137 0 -207 -112.5t-70 -257.5z" /> +<glyph unicode="r" horiz-adv-x="653" d="M168 0v1004h158v-80q4 4 18 19t22 22l23 19q16 13 29.5 20.5t33 15.5t42 11t49.5 3q25 0 50 -7t38 -15l12 -8l-55 -140q-33 27 -92 27q-33 0 -70 -22.5t-51.5 -36t-50.5 -50.5v-782h-156z" /> +<glyph unicode="s" horiz-adv-x="860" d="M96 768q0 131 101.5 198.5t238.5 67.5q90 0 175 -35.5t134 -103.5l-81 -82q-100 100 -232 100q-72 0 -127 -34.5t-55 -102.5q0 -37 25.5 -68.5t49.5 -44.5l66 -36l189 -92q184 -90 184 -275q0 -143 -103.5 -217t-250.5 -74q-160 0 -293 119l57 111q141 -109 244 -109 q82 0 139 38t57 116q0 94 -127 157l-213 107q-178 90 -178 260z" /> +<glyph unicode="t" horiz-adv-x="626" d="M51 903v101h144v178h143v-178h203v-119h-199v-678q0 -113 80 -113q23 0 52.5 7.5t47.5 13.5l19 8l34 -96q-90 -57 -208 -58q-80 0 -130.5 54.5t-50.5 175.5v686z" /> +<glyph unicode="u" horiz-adv-x="1062" d="M164 242v762h155v-762q0 -76 21.5 -111t93.5 -35q68 0 161 36t144 81v791h156v-850q0 -78 43 -82l-51 -103q-55 0 -95 32t-49 85q-61 -53 -151 -85t-172 -32q-139 0 -197.5 67t-58.5 206z" /> +<glyph unicode="v" horiz-adv-x="925" d="M20 1004h168l277 -828q199 453 278 828h156q-129 -543 -360 -1004h-154z" /> +<glyph unicode="w" horiz-adv-x="1372" d="M25 1004h163l230 -816l194 816h162l213 -816q125 379 203 816h149q-86 -520 -276 -1004h-166l-139 512q-14 53 -29.5 130t-23.5 128l-9 49q-25 -182 -57 -307l-137 -512h-172z" /> +<glyph unicode="x" horiz-adv-x="868" d="M25 0l317 520l-301 484h170l217 -349l211 349h164l-293 -478l334 -526h-174l-244 391l-238 -391h-163z" /> +<glyph unicode="y" horiz-adv-x="929" d="M23 1004h163l234 -658q39 -115 55 -205q154 383 275 863h155q-250 -973 -493 -1256q-78 -90 -240 -129l-33 119q70 16 152 86q43 37 106 160z" /> +<glyph unicode="z" horiz-adv-x="872" d="M70 0v133q279 463 518 739h-477v132h657v-136q-291 -354 -518 -735h557v-133h-737z" /> +<glyph unicode="{" horiz-adv-x="681" d="M82 541v96q63 0 115.5 71.5t52.5 174.5v360q0 160 55 213t146 53h120l23 -110h-121q-57 0 -77.5 -36t-20.5 -149v-360q0 -82 -38 -149.5t-75 -94.5l-39 -26l16.5 -10.5t37 -30t46 -51t36 -73.5t16.5 -97v-361q0 -113 21.5 -147.5t88.5 -34.5h109l-23 -111h-116 q-41 0 -72 8.5t-64.5 33t-51 80.5t-17.5 142v361q0 102 -52.5 175t-115.5 73z" /> +<glyph unicode="|" horiz-adv-x="454" d="M166 -37v1585h123v-1585h-123z" /> +<glyph unicode="}" horiz-adv-x="681" d="M82 1399l22 110h121q41 0 72 -8t62.5 -33.5t49 -82t17.5 -142.5v-360q0 -102 52 -174t116 -72v-96q-63 0 -115.5 -73t-52.5 -175v-361q0 -86 -17.5 -142t-51 -80.5t-64.5 -33t-72 -8.5h-117l-22 111h108q68 0 89.5 34.5t21.5 147.5v361q0 51 15.5 97t38 74.5t45 50 t38.5 29.5l15 11q-6 4 -16.5 10t-37 31.5t-46 54.5t-36 76t-16.5 98v360q0 113 -20.5 149t-77.5 36h-121z" /> +<glyph unicode="~" horiz-adv-x="1363" d="M299 504v127q25 53 74 90t104 37q78 0 213 -70t209 -70q113 0 166 119l-2 -147q-25 -47 -75 -77t-103 -30q-76 0 -213 70t-209 70q-107 0 -164 -119z" /> +<glyph unicode="¡" horiz-adv-x="540" d="M119 905q0 51 40 89t91 38q49 0 84 -33.5t35 -83t-40 -87t-91 -37.5q-49 0 -84 32.5t-35 81.5zM143 -430q35 199 35 395v705h123q31 -565 31 -754q0 -170 -33 -326z" /> +<glyph unicode="¢" horiz-adv-x="843" d="M100 492q0 238 101 372q96 133 248 164v141h92v-139q137 -14 223 -98l-70 -92q-63 51 -153 65v-811q82 10 200 88l52 -96q-98 -92 -252 -115v-123h-92v123q-133 23 -226 131q-123 142 -123 390zM256 512q0 -125 47 -249t146 -161v795q-100 -33 -146.5 -147.5 t-46.5 -237.5z" /> +<glyph unicode="£" horiz-adv-x="905" d="M61 791l41 102h80v270q0 129 97.5 191.5t232.5 62.5q145 0 213 -57l-35 -94q-63 37 -166 37q-186 0 -186 -170v-240h274v-123h-274v-260q0 -180 -47 -385h145q119 -39 180 -39q47 0 96.5 26.5t80.5 65.5l22 -102q-41 -45 -102.5 -76t-118.5 -31q-27 0 -72 7.5t-76 15.5 l-30 8h-307q74 330 73 514v264z" /> +<glyph unicode="¤" horiz-adv-x="1110" d="M20 498l62 129h149q-2 27 -2 63q0 39 2 66h-211l62 129h164q25 229 167 378.5t365 149.5q205 0 348 -143l-57 -123q-113 137 -301 137q-330 0 -379 -399h561l-61 -129h-516q-4 -53 -4 -68q0 -16 4 -61h456l-61 -129h-379q23 -190 119 -292t240 -102q86 0 171 43t146 119 v-164q-127 -127 -328 -127q-195 0 -332 141.5t-159 381.5h-226z" /> +<glyph unicode="¥" horiz-adv-x="974" d="M25 1290h174l288 -643l289 643h172q-238 -518 -239 -518h247v-123h-303l-84 -182v-10h383v-123h-383v-334h-163v334h-375v123h375v12l-84 180h-287v123h229z" /> +<glyph unicode="§" horiz-adv-x="860" d="M43 -190l57 112q147 -90 308 -90q86 0 141 42t55 126q0 61 -53 108.5t-130 80.5l-154 71q-77 37 -130 95.5t-53 133.5q0 125 73.5 195t200.5 88q-147 70 -192 111q-74 66 -74 155q0 139 98.5 213t241.5 74q158 0 295 -78l-55 -106q-139 66 -244 65q-80 0 -131 -42 t-51 -120q0 -51 53 -92t129 -74.5t150.5 -72.5t128 -105.5t53.5 -152.5q0 -109 -59.5 -179.5t-165.5 -93.5q63 -33 103 -59.5t77 -77.5t37 -110q0 -154 -98.5 -236t-254.5 -82q-186 1 -356 101zM238 496q0 -57 52 -93t111 -36q92 0 153.5 41t61.5 129q0 66 -52 105.5 t-120 39.5q-94 0 -150 -47t-56 -139z" /> +<glyph unicode="¨" horiz-adv-x="786" d="M121 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM436 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="©" horiz-adv-x="1800" d="M246 501.5q0 272.5 192.5 465t464.5 192.5q274 0 467 -192.5t193 -465t-193.5 -466t-466 -193.5t-465 193.5t-192.5 466zM346 502q0 -236 164 -397.5t399 -161.5q229 0 391 163.5t162 395t-163.5 395.5t-395 164t-394.5 -164t-163 -395zM594 500q0 154 82 262t231 108 q123 0 209 -79l-55 -70q-61 51 -154 51q-104 0 -150 -77t-46 -189q0 -117 50 -196t161 -79q76 0 172 70l43 -68q-41 -39 -104.5 -68.5t-115.5 -29.5q-152 0 -237.5 104.5t-85.5 260.5z" /> +<glyph unicode="ª" horiz-adv-x="860" d="M96 768q0 131 101.5 198.5t238.5 67.5q90 0 175 -35.5t134 -103.5l-81 -82q-100 100 -232 100q-72 0 -127 -34.5t-55 -102.5q0 -37 25.5 -68.5t49.5 -44.5l66 -36l189 -92q184 -90 184 -275q0 -143 -103.5 -217t-250.5 -74q-160 0 -293 119l57 111q141 -109 244 -109 q82 0 139 38t57 116q0 94 -127 157l-213 107q-178 90 -178 260zM117 1409l65 92q119 -63 248 -205q166 158 252 203l66 -92q-203 -145 -316 -299q-137 178 -315 301z" /> +<glyph unicode="«" horiz-adv-x="1024" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78zM432 512l379 385l96 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="¬" horiz-adv-x="1095" d="M94 760v123h826v-523l-121 -22v422h-705z" /> +<glyph unicode="­" horiz-adv-x="618" d="M111 461v123h391v-123h-391z" /> +<glyph unicode="®" horiz-adv-x="1800" d="M242 500q0 274 191.5 465.5t466 191.5t465.5 -191.5t191 -466t-192.5 -467t-464.5 -192.5t-464.5 194t-192.5 466zM342 500q0 -238 163 -398.5t400 -160.5q231 0 391 163.5t160 395t-164 395.5t-393 164q-231 0 -394 -164t-163 -395zM688 145v727h176q125 0 212 -59 t87 -178q0 -84 -57 -159t-139 -83l225 -217v-31h-109l-245 248q-23 6 -45 19v-267h-105zM793 498q57 -27 94 -27q76 0 122 45t46 123t-54.5 113.5t-136.5 35.5h-71v-290z" /> +<glyph unicode="¯" horiz-adv-x="686" d="M61 1174v120h562v-120h-562z" /> +<glyph unicode="°" horiz-adv-x="720" d="M68 1038.5q0 116.5 84 200.5t200.5 84t200.5 -84t84 -200.5t-84 -200.5t-200.5 -84t-200.5 84t-84 200.5zM160 1038q0 -80 56 -136t136 -56t136.5 56t56.5 136t-56.5 136.5t-136.5 56.5t-136 -56.5t-56 -136.5z" /> +<glyph unicode="±" horiz-adv-x="915" d="M92 451v122h330v355h119v-355h331v-122h-331v-256h-119v256h-330zM113 -2v123h739v-123h-739z" /> +<glyph unicode="²" d="M127 127q156 84 325 279.5t169 343.5q0 74 -52.5 113.5t-128.5 39.5q-133 0 -284 -117l-29 119q92 72 161.5 100.5t176.5 28.5q143 0 228 -70.5t85 -211.5q0 -188 -190 -404q-131 -147 -228 -213h455v-135h-659z" /> +<glyph unicode="³" d="M147 -225q68 0 146 20q358 82 358 365q0 193 -289 192q-59 0 -174 -12v119l91 12q133 72 258 172q90 70 90 152q0 51 -39 81.5t-92 30.5q-51 0 -142.5 -38t-156.5 -76l-37 108q207 133 370 133q109 0 179.5 -63.5t70.5 -171.5q0 -57 -24.5 -106.5t-73.5 -91.5t-86 -66.5 t-98 -59.5q129 0 218 -78t89 -204q0 -178 -95.5 -304.5t-238.5 -180.5q-138 -52 -303 -52h-12h-9v119z" /> +<glyph unicode="´" horiz-adv-x="692" d="M121 1241q201 145 315 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="µ" horiz-adv-x="1062" d="M127 -360q59 137 59 229q0 61 -11 186t-11 187v762h155v-762q0 -76 21.5 -111t93.5 -35q70 0 162 36t143 81v791h156v-850q0 -80 43 -82l-51 -103q-55 0 -95 32t-49 85q-63 -51 -152 -84t-171 -33q-31 0 -88 31q39 -117 39 -180q0 -76 -39 -144z" /> +<glyph unicode="¶" horiz-adv-x="1112" d="M55 928q0 172 105.5 282.5t275.5 110.5q53 0 113.5 -5t133.5 -13l112 -13l-2 88h114q0 -59 -2 -88l113 19l-6 -105l-107 -24q0 -348 -69.5 -741.5t-200.5 -659.5l-131 41q141 256 212.5 635t71.5 725q-256 27 -292 26q-86 0 -122 -72.5t-36 -166.5q0 -51 7 -99.5 t25.5 -98.5t56.5 -81t93 -31q88 0 166 62q-102 -203 -293 -203q-164 0 -251 121t-87 291z" /> +<glyph unicode="·" horiz-adv-x="528" d="M147 494q0 51 40 87.5t92 36.5q49 0 83.5 -32.5t34.5 -81.5t-40 -87t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="¸" horiz-adv-x="823" d="M389 -2h213q76 -55 76 -127q0 -68 -67.5 -131.5t-143.5 -95.5l-78 63q166 90 166 154q0 27 -18.5 43t-47.5 16q-27 0 -100 -18v96z" /> +<glyph unicode="¹" d="M203 858v98q88 0 194 29l74 19h141v-1004h-151v858h-258z" /> +<glyph unicode="º" horiz-adv-x="843" d="M100 492q0 129 39 247.5t134.5 206.5t228.5 88q156 0 262 -102l-70 -92q-86 72 -190 71q-86 0 -145.5 -71.5t-81 -157.5t-21.5 -170q0 -90 21.5 -180t84 -165t154.5 -75q86 0 225 90l52 -96q-57 -49 -137.5 -83t-151.5 -34q-197 0 -300.5 158t-103.5 365zM256 1241 q201 145 315 299l127 -129q-139 -137 -376 -262z" /> +<glyph unicode="»" horiz-adv-x="1024" d="M131 205l96 -78l383 385l-379 385l-96 -78l305 -307zM496 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="¼" horiz-adv-x="2537" d="M203 858v98q88 0 194 29l74 19h141v-1004h-151v858h-258zM772 -268l881 1794h129l-881 -1794h-129zM1679 133q139 387 502 871h168v-871h131v-133h-135v-283h-145v283h-512zM1831 133h369v680q-242 -354 -369 -680z" /> +<glyph unicode="½" horiz-adv-x="2537" d="M203 858v98q88 0 194 29l74 19h141v-1004h-151v858h-258zM772 -268l881 1794h129l-881 -1794h-129zM1743 127q156 84 324.5 279.5t168.5 343.5q0 74 -52 113.5t-128 39.5q-133 0 -284 -117l-29 119q92 72 161.5 100.5t176.5 28.5q143 0 228 -70.5t85 -211.5 q0 -188 -190 -404q-131 -147 -228 -213h455v-135h-659z" /> +<glyph unicode="¾" horiz-adv-x="2537" d="M147 -225q68 0 146 20q358 82 358 365q0 193 -289 192q-59 0 -174 -12v119l91 12q133 72 258 172q90 70 90 152q0 51 -39 81.5t-92 30.5q-51 0 -142.5 -38t-156.5 -76l-37 108q207 133 370 133q109 0 179.5 -63.5t70.5 -171.5q0 -57 -24.5 -106.5t-73.5 -91.5t-86 -66.5 t-98 -59.5q129 0 218 -78t89 -204q0 -178 -95.5 -304.5t-238.5 -180.5q-138 -52 -303 -52h-12h-9v119zM772 -268l881 1794h129l-881 -1794h-129zM1679 133q139 387 502 871h168v-871h131v-133h-135v-283h-145v283h-512zM1831 133h369v680q-242 -354 -369 -680z" /> +<glyph unicode="¿" horiz-adv-x="733" d="M86 -164q0 94 100 230l164 215q47 61 63.5 121.5t16.5 164.5v99h125v-99q0 -86 -2 -127t-20.5 -103.5t-55.5 -111.5l-168 -225q-70 -92 -69 -160q0 -59 30.5 -94t87.5 -35q88 0 172 19v-117q-111 -27 -184 -27q-121 0 -190.5 63.5t-69.5 186.5zM369 909q0 51 40 88t91 37 q49 0 83.5 -32.5t34.5 -82t-39.5 -87t-91.5 -37.5q-49 0 -83.5 32.5t-34.5 81.5z" /> +<glyph unicode="À" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM307 1677l127 129q106 -147 316 -299l-66 -92q-240 127 -377 262zM369 551h307l-154 569z" /> +<glyph unicode="Á" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM307 1507q201 145 316 299l127 -129q-139 -137 -377 -262zM369 551h307l-154 569z" /> +<glyph unicode="Â" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM203 1505q164 113 313 301q127 -164 313 -299l-65 -94q-117 61 -248 205q-133 -145 -248 -205zM369 551h307l-154 569z" /> +<glyph unicode="Ã" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM139 1581q152 119 246 119q59 0 146 -45t135 -45q53 0 83.5 13t88.5 52l63 -78q-74 -68 -125 -91t-125 -23q-41 0 -128 45t-144 45q-18 0 -35.5 -4t-40.5 -16l-33 -16q-10 -5 -38.5 -23.5t-32.5 -20.5z M369 551h307l-154 569z" /> +<glyph unicode="Ä" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM258 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM369 551h307l-154 569zM573 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5 q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Å" horiz-adv-x="1044" d="M25 0l409 1290h176l410 -1290h-168l-137 416h-385l-137 -416h-168zM309 1606q0 88 64.5 152.5t152.5 64.5t150.5 -63.5t62.5 -153.5t-64.5 -153.5t-152.5 -63.5t-150.5 63.5t-62.5 153.5zM369 551h307l-154 569zM401 1605.5q0 -49.5 37 -86t84 -36.5q49 0 86 36.5t37 86 t-35 86.5t-82 37q-51 0 -89 -37t-38 -86.5z" /> +<glyph unicode="Æ" horiz-adv-x="1486" d="M25 0l407 1290h191l98 -432v432h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684v416h-389l-137 -416h-170zM371 551h305l-154 569z" /> +<glyph unicode="Ç" horiz-adv-x="1017" d="M113 645q0 119 29.5 233.5t87 216t157.5 164t227 62.5q199 0 332 -88l-59 -107q-127 68 -273 68q-82 0 -154.5 -62.5t-111.5 -144.5q-72 -145 -72 -350q0 -90 16.5 -176t52.5 -172t106.5 -138.5t166.5 -52.5q164 0 277 90l72 -104q-98 -80 -250 -107q53 -53 53 -106 q0 -68 -67.5 -131.5t-143.5 -95.5l-78 63q166 90 166 154q0 27 -18.5 43t-46.5 16q-27 0 -101 -18v82q-102 29 -177 98.5t-114.5 164.5t-58 194.5t-18.5 203.5z" /> +<glyph unicode="È" horiz-adv-x="929" d="M164 0v1290h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684zM285 1677l127 129q113 -152 315 -299l-65 -92q-234 123 -377 262z" /> +<glyph unicode="É" horiz-adv-x="929" d="M164 0v1290h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684zM289 1507q201 145 315 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="Ê" horiz-adv-x="929" d="M164 0v1290h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684zM184 1505q164 113 314 301q131 -170 313 -299l-66 -94q-117 61 -247 205q-141 -150 -248 -205z" /> +<glyph unicode="Ë" horiz-adv-x="929" d="M164 0v1290h663v-137h-499v-410h383v-141h-383v-461h520v-141h-684zM240 1542q0 45 34.5 83t81.5 38q35 0 58.5 -23.5t23.5 -58.5q0 -45 -38 -80t-83 -35q-33 0 -55 21.5t-22 54.5zM555 1542q0 45 35 83t82 38q35 0 58.5 -23.5t23.5 -58.5q0 -45 -38 -80t-83 -35 q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ì" horiz-adv-x="487" d="M25 1677l127 129q111 -154 315 -299l-66 -92q-235 123 -376 262zM164 0v1290h160v-1290h-160z" /> +<glyph unicode="Í" horiz-adv-x="487" d="M23 1507q201 145 315 299l127 -129q-139 -137 -377 -262zM164 0v1290h160v-1290h-160z" /> +<glyph unicode="Î" horiz-adv-x="487" d="M-70 1505q164 113 314 301q127 -164 313 -299l-65 -94q-117 61 -248 205q-133 -145 -248 -205zM164 0v1290h160v-1290h-160z" /> +<glyph unicode="Ï" horiz-adv-x="487" d="M-14 1542q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-84.5 -35q-33 0 -55 21.5t-22 54.5zM164 0v1290h160v-1290h-160zM301 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ñ" horiz-adv-x="1134" d="M164 0v1290h217l436 -987q-10 98 -10 180v807h164v-1290h-168l-486 1053q10 -96 11 -176v-877h-164zM195 1581q152 119 245 119q59 0 146.5 -45t134.5 -45q53 0 84 13t88 52l63 -78q-74 -68 -125 -91t-124 -23q-41 0 -128 45t-145 45q-18 0 -35.5 -4t-40.5 -16l-32 -16 q-10 -5 -39 -23.5t-33 -20.5z" /> +<glyph unicode="Ò" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5 t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156zM336 1677l127 129q106 -147 315 -299l-65 -92q-240 127 -377 262z" /> +<glyph unicode="Ó" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5 t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156zM342 1507q201 145 315 299l127 -129q-139 -137 -376 -262z" /> +<glyph unicode="Ô" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM246 1511q160 109 313 301q127 -164 313 -299l-65 -94q-113 57 -248 205 q-129 -145 -248 -205zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156z" /> +<glyph unicode="Õ" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM176 1581q152 119 246 119q59 0 146 -45t134 -45q53 0 84 13t88 52 l64 -78q-74 -68 -125 -91t-125 -23q-41 0 -128 45t-144 45q-18 0 -36 -4t-40 -16l-33 -16q-10 -5 -38.5 -23.5t-32.5 -20.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5 t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156z" /> +<glyph unicode="Ö" horiz-adv-x="1122" d="M113 645.5q0 110.5 23.5 222t73.5 217t141 171t210 65.5t210 -65.5t141.5 -171t74 -217t23.5 -222t-23.5 -222.5t-74 -217.5t-141.5 -171t-210 -65.5t-210 65.5t-141 171t-73.5 217.5t-23.5 222.5zM276 645q0 -72 14.5 -155.5t44.5 -175t89 -151t137 -59.5t137.5 59.5 t89 151t44 175t14.5 155.5t-14.5 156t-44 175t-89 150.5t-137.5 59.5t-137 -59.5t-89 -150.5t-44.5 -175t-14.5 -156zM299 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM614 1542q0 45 36 83t81 38 q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ø" horiz-adv-x="1122" d="M113 653q0 111 24.5 221.5t73.5 214t140 168t210 64.5q78 0 139 -27l70 240h102l-84 -293q115 -92 168.5 -257t53.5 -339q0 -111 -23.5 -222.5t-73 -217t-140.5 -171t-212 -65.5q-63 0 -131 23l-80 -279h-102l94 330q-119 92 -174 262t-55 348zM276 633q0 -307 109 -443 l279 971q-45 25 -103 25q-88 0 -149.5 -57.5t-88 -152.5t-37 -176t-10.5 -167zM467 123q45 -18 94 -19q90 0 150.5 55.5t87 147.5t37 172t10.5 166q0 297 -103 442z" /> +<glyph unicode="Ù" horiz-adv-x="1142" d="M162 502v788h164v-790q0 -395 245.5 -395.5t245.5 397.5v788h164v-796q0 -225 -98.5 -375t-311.5 -150t-311 152t-98 381zM354 1677l127 129q106 -147 316 -299l-66 -92q-240 127 -377 262z" /> +<glyph unicode="Ú" horiz-adv-x="1142" d="M162 502v788h164v-790q0 -395 245.5 -395.5t245.5 397.5v788h164v-796q0 -225 -98.5 -375t-311.5 -150t-311 152t-98 381zM358 1507q201 145 316 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="Û" horiz-adv-x="1142" d="M162 502v788h164v-790q0 -395 245.5 -395.5t245.5 397.5v788h164v-796q0 -225 -98.5 -375t-311.5 -150t-311 152t-98 381zM254 1505q164 113 313 301q127 -164 314 -299l-66 -94q-117 61 -248 205q-133 -145 -248 -205z" /> +<glyph unicode="Ü" horiz-adv-x="1126" d="M162 502v788h164v-790q0 -395 245.5 -395.5t245.5 397.5v788h164v-796q0 -225 -98.5 -375t-311.5 -150t-311 152t-98 381zM311 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM627 1542q0 45 35.5 83t80.5 38 q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-83.5 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Ý" horiz-adv-x="964" d="M27 1290h174l286 -643l289 643h164l-377 -817v-473h-162v473zM418 1536q201 145 315 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="ß" horiz-adv-x="1171" d="M47 903v101h135v155q0 133 110.5 211t250.5 78q156 0 260 -92t104 -246q0 -53 -41 -209q-35 18 -90 19q-80 0 -131 -42t-51 -120q0 -49 53 -90t129 -75t152 -74t129 -107.5t53 -157.5q0 -141 -103.5 -213t-250.5 -72q-188 0 -357 103l58 112q147 -92 307 -92 q84 0 140.5 45t56.5 125q0 59 -53.5 106.5t-130.5 81.5l-153 70q-77 37 -130.5 95.5t-53.5 136.5q0 137 90.5 210.5t231.5 73.5q14 53 14 94q0 94 -62.5 145.5t-156.5 51.5q-88 0 -153.5 -54t-65.5 -140v-1133h-156v885z" /> +<glyph unicode="à" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106zM244 1411l127 129q106 -147 315 -299l-65 -92q-240 127 -377 262z" /> +<glyph unicode="á" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106zM258 1241q201 145 315 299l127 -129q-139 -137 -376 -262z" /> +<glyph unicode="â" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM147 1239q164 113 314 301q127 -164 313 -299l-65 -94q-117 61 -248 205q-133 -145 -248 -205zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106z" /> +<glyph unicode="ã" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM86 1315q152 119 246 119q59 0 146 -45.5t134 -45.5q53 0 84 13.5t88 52.5l64 -78q-74 -68 -125 -91t-125 -23q-41 0 -128 45t-144 45q-18 0 -36 -4.5t-40 -15.5l-33 -16q-10 -5 -38.5 -23.5t-33.5 -20.5zM240 252q0 -68 36.5 -114 t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106z" /> +<glyph unicode="ä" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM203 1276q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-83.5 -35q-33 0 -55.5 21.5t-22.5 54.5zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106zM518 1276 q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="å" horiz-adv-x="954" d="M86 246q0 72 59 182l482 205v129q0 72 -43 108.5t-115 36.5q-70 0 -146.5 -44t-129.5 -103l-58 96q61 78 157.5 128t190.5 50q137 0 218 -68.5t81 -203.5v-625q0 -61 66 -61q25 0 41 12l39 -78q-59 -37 -119 -37q-82 0 -131 49.5t-49 131.5q-25 -86 -106 -135.5 t-175 -49.5q-123 0 -192.5 76t-69.5 201zM240 252q0 -68 36.5 -114t102.5 -46q109 0 178.5 109.5t69.5 224.5v84l-361 -152q-26 -57 -26 -106zM244 1350q0 88 64.5 152.5t152.5 64.5t150.5 -64.5t62.5 -152.5t-64.5 -152.5t-152.5 -64.5t-150.5 64.5t-62.5 152.5zM336 1350 q0 -49 36 -86t85 -37t86 36.5t37 86t-34 86.5t-83 37q-51 0 -89 -36t-38 -87z" /> +<glyph unicode="æ" horiz-adv-x="1484" d="M86 252q0 111 59 180q33 37 482 201v129q0 72 -43 108.5t-115 36.5t-147.5 -43t-128.5 -104l-58 96q61 78 157.5 128t190.5 50q197 0 269 -137q121 137 295 137q145 0 243.5 -114.5t104.5 -264.5l4 -104h-619v-39q0 -63 12.5 -131t40 -135.5t80 -110.5t121.5 -43 q156 0 254 60l47 -101q-133 -82 -327 -82q-102 0 -190.5 61.5t-133.5 155.5l-170 -145q-84 -72 -174 -72q-125 0 -189.5 77t-64.5 206zM240 247.5q0 -59.5 33.5 -104.5t91.5 -45q72 0 145 62l141 119q-27 88 -30 229q-319 -115 -345 -150q-36 -51 -36 -110.5zM799 668h436 q0 94 -56.5 170.5t-146.5 76.5t-151.5 -74.5t-81.5 -172.5z" /> +<glyph unicode="ç" horiz-adv-x="843" d="M100 492q0 238 101 372q55 74 135 122t166 48q68 0 141.5 -27.5t120.5 -74.5l-70 -92q-84 70 -190 69q-84 0 -142.5 -68.5t-82.5 -158.5q-23 -76 -23 -170q0 -86 21.5 -176t81 -167t147.5 -77q96 0 235 90l52 -96q-72 -66 -179 -98q63 -59 64 -117q0 -68 -67.5 -131.5 t-143.5 -95.5l-78 63q166 90 166 154q0 27 -18.5 43t-47.5 16q-27 0 -100 -18v84q-147 51 -218 192.5t-71 313.5z" /> +<glyph unicode="è" horiz-adv-x="964" d="M100 528q0 197 118 351.5t308 154.5q145 0 241.5 -114.5t106.5 -264.5q2 -16 2.5 -51t2.5 -53h-619v-39q0 -84 20.5 -174t79 -168t144.5 -78q100 0 284 127l50 -98q-72 -61 -170.5 -106.5t-180.5 -45.5q-104 0 -183 52.5t-121 138.5t-62.5 179t-20.5 189zM279 668h436 q0 94 -55.5 168.5t-147.5 74.5q-90 0 -151.5 -73.5t-81.5 -169.5zM293 1411l127 129q106 -147 315 -299l-65 -92q-240 127 -377 262z" /> +<glyph unicode="é" horiz-adv-x="964" d="M100 528q0 197 118 351.5t308 154.5q145 0 241.5 -114.5t106.5 -264.5q2 -16 2.5 -51t2.5 -53h-619v-39q0 -84 20.5 -174t79 -168t144.5 -78q100 0 284 127l50 -98q-72 -61 -170.5 -106.5t-180.5 -45.5q-104 0 -183 52.5t-121 138.5t-62.5 179t-20.5 189zM279 668h436 q0 94 -55.5 168.5t-147.5 74.5q-90 0 -151.5 -73.5t-81.5 -169.5zM313 1241q201 145 316 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="ê" horiz-adv-x="964" d="M100 528q0 197 118 351.5t308 154.5q145 0 241.5 -114.5t106.5 -264.5q2 -16 2.5 -51t2.5 -53h-619v-39q0 -84 20.5 -174t79 -168t144.5 -78q100 0 284 127l50 -98q-72 -61 -170.5 -106.5t-180.5 -45.5q-104 0 -183 52.5t-121 138.5t-62.5 179t-20.5 189zM199 1239 q164 113 313 301q127 -164 313 -299l-65 -94q-117 61 -248 205q-133 -145 -248 -205zM279 668h436q0 94 -55.5 168.5t-147.5 74.5q-90 0 -151.5 -73.5t-81.5 -169.5z" /> +<glyph unicode="ë" horiz-adv-x="964" d="M100 528q0 197 118 351.5t308 154.5q145 0 241.5 -114.5t106.5 -264.5q2 -16 2.5 -51t2.5 -53h-619v-39q0 -84 20.5 -174t79 -168t144.5 -78q100 0 284 127l50 -98q-72 -61 -170.5 -106.5t-180.5 -45.5q-104 0 -183 52.5t-121 138.5t-62.5 179t-20.5 189zM270 1276 q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM279 668h436q0 94 -55.5 168.5t-147.5 74.5q-90 0 -151.5 -73.5t-81.5 -169.5zM586 1276q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-83.5 -35 q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ì" horiz-adv-x="487" d="M23 1411l127 129q106 -147 315 -299l-66 -92q-239 127 -376 262zM168 0v1004h151v-1004h-151z" /> +<glyph unicode="í" horiz-adv-x="487" d="M41 1241q201 145 315 299l127 -129q-139 -137 -377 -262zM168 0v1004h151v-1004h-151z" /> +<glyph unicode="î" horiz-adv-x="487" d="M-76 1239q164 113 314 301q127 -164 313 -299l-66 -94q-117 61 -247 205q-133 -145 -248 -205zM168 0v1004h151v-1004h-151z" /> +<glyph unicode="ï" horiz-adv-x="487" d="M-16 1276q0 45 34.5 83t81.5 38q33 0 58.5 -24.5t25.5 -57.5q0 -45 -38.5 -80t-84.5 -35q-33 0 -55 21.5t-22 54.5zM168 0v1004h151v-1004h-151zM299 1276q0 45 35 83t82 38q33 0 58.5 -24.5t25.5 -57.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ñ" horiz-adv-x="1062" d="M166 1315q152 119 246 119q59 0 146 -45.5t134 -45.5q53 0 84 13.5t88 52.5l64 -78q-74 -68 -125 -91t-125 -23q-41 0 -128 45t-145 45q-18 0 -35.5 -4.5t-39.5 -15.5l-33 -16q-10 -5 -39 -23.5t-33 -20.5zM168 0v1004h156v-74q55 39 151 71.5t174 32.5 q147 0 198.5 -69.5t51.5 -202.5v-762h-156v762q0 76 -24.5 110.5t-95.5 34.5q-78 0 -168 -34.5t-131 -69.5v-803h-156z" /> +<glyph unicode="ò" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM256 500q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5 t-82 -163.5t-23.5 -175zM281 1411l127 129q106 -147 315 -299l-66 -92q-239 127 -376 262z" /> +<glyph unicode="ó" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM256 500q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5 t-82 -163.5t-23.5 -175zM293 1241q201 145 315 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="ô" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM193 1239q164 113 313 301q127 -164 313 -299l-65 -94q-117 61 -248 205q-133 -145 -248 -205zM256 500 q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5t-82 -163.5t-23.5 -175z" /> +<glyph unicode="õ" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM117 1315q152 119 245 119q59 0 146.5 -45.5t134.5 -45.5q53 0 84 13.5t88 52.5l64 -78q-74 -68 -125.5 -91t-124.5 -23 q-41 0 -128 45t-145 45q-18 0 -35.5 -4.5t-39.5 -15.5l-33 -16q-10 -5 -39 -23.5t-33 -20.5zM256 500q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5t-82 -163.5t-23.5 -175z" /> +<glyph unicode="ö" horiz-adv-x="999" d="M100 500q0 127 40 243.5t134.5 203.5t225.5 87t225 -87t134 -203.5t40 -243.5q0 -125 -41 -243t-135 -203t-223 -85t-223.5 85t-135.5 203t-41 243zM250 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM256 500 q0 -139 64.5 -273.5t179 -134.5t179 134t64.5 274q0 84 -23.5 175t-81.5 163.5t-138 72.5t-138.5 -72.5t-82 -163.5t-23.5 -175zM565 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="÷" horiz-adv-x="1001" d="M111 451v122h778v-122h-778zM381 209q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM381 817q0 51 40 88t91 37q49 0 84 -32.5t35 -82t-40 -87.5t-91 -38q-49 0 -84 33t-35 82z" /> +<glyph unicode="ø" horiz-adv-x="999" d="M100 500q0 92 25 183t72 171t125.5 130t177.5 50q49 0 94 -12l49 170h102l-61 -213q106 -72 160.5 -204t54.5 -275q0 -125 -41 -243t-135 -203t-223 -85q-55 0 -101 15l-59 -211h-102l71 254q-104 72 -156.5 202t-52.5 271zM256 500q0 -229 92 -338l213 741q-27 8 -61 8 q-72 0 -123 -41t-76 -108.5t-35 -131.5t-10 -130zM434 100q35 -10 66 -10q72 0 123 41t75.5 107.5t34.5 130t10 131.5q0 233 -96 346z" /> +<glyph unicode="ù" horiz-adv-x="1058" d="M164 242v762h155v-762q0 -76 21.5 -111t93.5 -35q68 0 161 36t144 81v791h156v-850q0 -78 43 -82l-51 -103q-55 0 -95 32t-49 85q-61 -53 -151 -85t-172 -32q-139 0 -197.5 67t-58.5 206zM313 1411l127 129q106 -147 316 -299l-66 -92q-240 127 -377 262z" /> +<glyph unicode="ú" horiz-adv-x="1058" d="M164 242v762h155v-762q0 -76 21.5 -111t93.5 -35q68 0 161 36t144 81v791h156v-850q0 -78 43 -82l-51 -103q-55 0 -95 32t-49 85q-61 -53 -151 -85t-172 -32q-139 0 -197.5 67t-58.5 206zM313 1241q201 145 316 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="û" horiz-adv-x="1058" d="M164 242v762h155v-762q0 -76 21.5 -111t93.5 -35q68 0 161 36t144 81v791h156v-850q0 -78 43 -82l-51 -103q-55 0 -95 32t-49 85q-61 -53 -151 -85t-172 -32q-139 0 -197.5 67t-58.5 206zM207 1239q164 113 313 301q127 -164 314 -299l-66 -94q-117 61 -248 205 q-133 -145 -248 -205z" /> +<glyph unicode="ü" horiz-adv-x="1058" d="M164 242v762h155v-762q0 -76 21.5 -111t93.5 -35q68 0 161 36t144 81v791h156v-850q0 -78 43 -82l-51 -103q-55 0 -95 32t-49 85q-61 -53 -151 -85t-172 -32q-139 0 -197.5 67t-58.5 206zM270 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35 q-33 0 -55.5 21.5t-22.5 54.5zM586 1276q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-83.5 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ý" horiz-adv-x="929" d="M23 1004h163l234 -658q39 -115 55 -205q154 383 275 863h155q-250 -973 -493 -1256q-78 -90 -240 -129l-33 119q70 16 152 86q43 37 106 160zM399 1249q201 145 316 299l127 -129q-139 -137 -377 -262z" /> +<glyph unicode="ÿ" horiz-adv-x="929" d="M23 1004h163l234 -658q39 -115 55 -205q154 383 275 863h155q-250 -973 -493 -1256q-78 -90 -240 -129l-33 119q70 16 152 86q43 37 106 160zM217 1276q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5zM532 1276 q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="Œ" horiz-adv-x="1306" d="M113 653q0 102 23.5 208t70.5 205t133 161.5t199 62.5h665v-137h-499v-410h382v-141h-382v-461h520v-141h-686q-115 0 -202 64.5t-133 167t-68.5 209t-22.5 212.5zM270 633q0 -80 10.5 -155t36 -160t84 -137t140.5 -52v1030q-84 0 -142.5 -55t-84 -145.5t-35 -167 t-9.5 -158.5z" /> +<glyph unicode="œ" horiz-adv-x="1558" d="M100 500q0 92 25 183t72 171t125.5 130t177.5 50q197 0 297 -172q123 172 323 172q145 0 242.5 -114.5t105.5 -264.5q2 -16 2 -51t3 -53h-619v-39q0 -63 12.5 -131t40 -135.5t79.5 -110.5t122 -43q154 0 254 60l47 -101q-133 -82 -328 -82q-86 0 -161.5 44t-126.5 116 q-113 -160 -293 -160q-129 0 -223.5 85t-135.5 203t-41 243zM256 500q0 -68 10 -131.5t35 -130t76 -107.5t123 -41q160 0 219 209q-25 100 -25 229q0 78 27 168q-61 215 -221 215q-72 0 -123 -41t-76 -108.5t-35 -131.5t-10 -130zM872 668h437q0 94 -55.5 170.5t-147.5 76.5 q-88 0 -151 -75.5t-83 -171.5z" /> +<glyph unicode="Ÿ" horiz-adv-x="964" d="M27 1290h174l286 -643l289 643h164l-377 -817v-473h-162v473zM244 1542q0 45 35.5 83t80.5 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -38.5 -80t-83.5 -35q-33 0 -55.5 21.5t-22.5 54.5zM559 1542q0 45 36 83t81 38q35 0 59.5 -23.5t24.5 -58.5q0 -45 -39 -80t-84 -35 q-33 0 -55.5 21.5t-22.5 54.5z" /> +<glyph unicode="ˆ" horiz-adv-x="823" d="M123 1239q164 113 313 301q127 -164 314 -299l-66 -94q-117 61 -248 205q-133 -145 -248 -205z" /> +<glyph unicode="˜" horiz-adv-x="778" d="M2 1315q152 119 246 119q59 0 146 -45.5t134 -45.5q53 0 84 13.5t88 52.5l64 -78q-74 -68 -125 -91t-125 -23q-41 0 -128 45t-144 45q-18 0 -36 -4.5t-40 -15.5l-33 -16q-10 -5 -38.5 -23.5t-33.5 -20.5z" /> +<glyph unicode="–" horiz-adv-x="733" d="M0 461v123h733v-123h-733z" /> +<glyph unicode="—" horiz-adv-x="1245" d="M0 463v123h1245v-123h-1245z" /> +<glyph unicode="‘" horiz-adv-x="546" d="M94 977q84 178 264 377l93 -64q-98 -141 -123 -239q-10 -41 -11 -84l5 -49q-57 -35 -97 -35q-27 0 -59.5 23.5t-50.5 47.5z" /> +<glyph unicode="’" horiz-adv-x="552" d="M98 915q98 141 123 240q10 41 10 84l-4 49q57 35 97 35q27 0 59.5 -23.5t50.5 -46.5l21 -24q-84 -178 -265 -377z" /> +<glyph unicode="‚" horiz-adv-x="528" d="M59 -223q98 141 123 239q10 41 11 84l-5 50q57 35 97 34q27 0 59.5 -23.5t52.5 -45.5l19 -25q-84 -178 -264 -377z" /> +<glyph unicode="“" horiz-adv-x="913" d="M96 977q84 178 264 377l93 -64q-98 -141 -123 -239q-10 -41 -11 -84l5 -49q-57 -35 -97 -35q-27 0 -59.5 23.5t-52.5 47.5zM459 977q84 178 264 377l92 -64q-98 -141 -123 -239q-10 -41 -10 -84l4 -49q-57 -35 -96 -35q-27 0 -59.5 23.5t-51.5 47.5z" /> +<glyph unicode="”" horiz-adv-x="913" d="M96 915q98 141 123 240q10 41 10 84l-4 49q57 35 97 35q27 0 59.5 -23.5t52.5 -46.5l19 -24q-84 -178 -265 -377zM459 915q98 141 123 240q10 41 10 84l-4 49q57 35 96 35q27 0 59.5 -23.5t51.5 -46.5l20 -24q-84 -178 -264 -377z" /> +<glyph unicode="„" horiz-adv-x="888" d="M59 -223q98 141 123 239q10 41 11 84l-5 50q57 35 97 34q27 0 59.5 -23.5t52.5 -45.5l19 -25q-84 -178 -264 -377zM422 -223q98 141 123 239q10 41 10 84l-4 50q57 35 96 34q27 0 59.5 -23.5t51.5 -45.5l20 -25q-84 -178 -264 -377z" /> +<glyph unicode="•" horiz-adv-x="868" d="M80 645.5q0 145.5 103.5 248.5t249 103t249.5 -103t104 -248.5t-104 -249t-249.5 -103.5t-249 103.5t-103.5 249z" /> +<glyph unicode="…" horiz-adv-x="1236" d="M143 84q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-91 -38q-49 0 -84 33t-35 82zM496 84q0 51 39.5 88t91.5 37q49 0 83.5 -33t34.5 -82t-39.5 -87t-91.5 -38q-49 0 -83.5 33t-34.5 82zM844 84q0 51 40 88t91 37q49 0 84 -33t35 -82t-40 -87t-91 -38q-49 0 -84 33 t-35 82z" /> +<glyph unicode="‹" horiz-adv-x="614" d="M68 512l378 385l97 -78l-305 -307l309 -307l-96 -78z" /> +<glyph unicode="›" horiz-adv-x="614" d="M68 205l96 -78l383 385l-379 385l-96 -78l305 -307z" /> +<glyph unicode="™" horiz-adv-x="1642" d="M137 1331v90h498v-90h-199v-723h-100v723h-199zM731 608v813h137l240 -686l238 686h137v-813h-103v553l6 111l-233 -664h-90l-234 666q6 -68 7 -111v-555h-105z" /> +<glyph unicode="" horiz-adv-x="1004" d="M0 1005h1005v-1005h-1005v1005z" /> +<glyph unicode="fi" horiz-adv-x="1069" d="M47 903v101h135v112q0 168 115 249t291 81q43 0 96 -6t115.5 -21.5t104.5 -48.5t42 -78q0 -49 -35.5 -80.5t-87.5 -31.5q-43 0 -64.5 18.5t-28.5 43t-19.5 50t-51 44t-110.5 18.5q-106 0 -158.5 -78t-52.5 -191v-81h199v-119h-199v-885h-156v885zM760 0v1004h151v-1004 h-151z" /> +<glyph unicode="fl" horiz-adv-x="1069" d="M47 903v101h135v112q0 152 100.5 238t254 86t225.5 -78v57h151v-1016q0 -197 62 -403h-162q-51 137 -51 403v828q-72 111 -201 110q-63 0 -107 -20.5t-66.5 -47t-34 -78.5t-13.5 -88t-2 -103h199v-119h-199v-885h-156v885z" /> +<glyph unicode="ffi" horiz-adv-x="1658" d="M51 903v101h135v159q0 242 351 285l28 -121q-8 0 -26.5 -2t-54 -14.5t-66.5 -30.5t-53.5 -56t-22.5 -87v-133h199v-119h-199v-885h-156v885zM637 903v101h135v159q0 242 350 285l29 -121q-8 0 -26.5 -2t-54.5 -14.5t-66.5 -30.5t-53 -56t-22.5 -87v-133h198v-119h-198 v-885h-156v885zM1294 1284q0 47 35 78t82 31q45 0 79 -30t34 -75q0 -47 -35 -77.5t-82 -30.5q-45 0 -79 29.5t-34 74.5zM1339 0v1004h152v-1004h-152z" /> +<glyph unicode="ffl" horiz-adv-x="1654" d="M51 903v101h135v159q0 242 351 285l28 -121q-8 0 -26.5 -2t-54 -14.5t-66.5 -30.5t-53.5 -56t-22.5 -87v-133h199v-119h-199v-885h-156v885zM637 903v101h135v159q0 242 350 285l29 -121q-8 0 -26.5 -2t-54.5 -14.5t-66.5 -30.5t-53 -56t-22.5 -87v-133h198v-119h-198 v-885h-156v885zM1327 403v1018h152v-1018q0 -203 61 -403h-162q-51 139 -51 403z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-roman-webfont.ttf b/app/assets/fonts/delicious-roman-webfont.ttf Binary files differnew file mode 100755 index 000000000..cc27c5c1d --- /dev/null +++ b/app/assets/fonts/delicious-roman-webfont.ttf diff --git a/app/assets/fonts/delicious-roman-webfont.woff b/app/assets/fonts/delicious-roman-webfont.woff Binary files differnew file mode 100755 index 000000000..9acbdd4a0 --- /dev/null +++ b/app/assets/fonts/delicious-roman-webfont.woff diff --git a/app/assets/fonts/delicious-smallcaps-webfont.eot b/app/assets/fonts/delicious-smallcaps-webfont.eot Binary files differnew file mode 100755 index 000000000..358efc11f --- /dev/null +++ b/app/assets/fonts/delicious-smallcaps-webfont.eot diff --git a/app/assets/fonts/delicious-smallcaps-webfont.svg b/app/assets/fonts/delicious-smallcaps-webfont.svg new file mode 100755 index 000000000..d1235b7f6 --- /dev/null +++ b/app/assets/fonts/delicious-smallcaps-webfont.svg @@ -0,0 +1,222 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG webfont generated by Font Squirrel. +Copyright : 35 I1995 Jos Buivenga +</metadata> +<defs> +<font id="DeliciousSmallCapsRegular" horiz-adv-x="450" > +<font-face units-per-em="1000" ascent="800" descent="-200" /> +<missing-glyph horiz-adv-x="300" /> +<glyph unicode=" " horiz-adv-x="300" /> +<glyph unicode="	" horiz-adv-x="300" /> +<glyph unicode=" " horiz-adv-x="300" /> +<glyph unicode="!" horiz-adv-x="264" d="M207 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM195 699q-17 -99 -17 -193v-344h-60q-14 258 -14 368q0 88 15 159z" /> +<glyph unicode=""" horiz-adv-x="346" d="M126 708l14 -63l-22 -157h-33l-21 157l13 63h49zM264 708l14 -63l-22 -157h-33l-21 157l13 63h49z" /> +<glyph unicode="#" horiz-adv-x="556" d="M325 279l19 152h-114l-19 -152h114zM375 675h60l-22 -178h85l-6 -66h-88l-19 -152h75l-6 -66h-77l-26 -204h-60l26 204h-115l-23 -185h-60l23 185h-81l10 66h80l19 152h-71l10 66h69l25 197h60l-25 -197h115z" /> +<glyph unicode="$" horiz-adv-x="445" d="M263 256v-201q33 11 51 38.5t18 63.5q0 54 -69 99zM218 379v201q-37 -6 -57.5 -31t-20.5 -62q0 -27 12.5 -48t25 -31t40.5 -29zM409 156q0 -66 -40.5 -111.5t-105.5 -56.5v-61h-45v58q-80 2 -151 43l23 60q75 -37 128 -40v236l-22.5 14.5l-24 15.5l-21 14.5t-21.5 16 l-18 16.5t-17 19.5t-12.5 20t-11.5 24t-5.5 26t-2.5 29.5q0 72 42 115.5t114 48.5v55h45v-56q72 -9 122 -48l-27 -54q-40 29 -95 38v-228q77 -49 101 -74q45 -48 45 -121z" /> +<glyph unicode="%" horiz-adv-x="699" d="M253 512q0 31 -21.5 52.5t-52.5 21.5t-52.5 -21.5t-21.5 -52.5t21.5 -52.5t52.5 -21.5t52.5 21.5t21.5 52.5zM482 630l-222 -630h-66l211 584q-17 -9 -43 -13.5t-44 -4.5l-17 -1q11 -21 11 -53q0 -55 -39 -94t-94 -39t-94 39t-39 94t39 94t94 39q39 0 71 -20 q40 -11 70 -11q28 0 56 7.5t42 14.5l13 8zM651 118q0 -55 -39 -94t-94 -39t-94 39t-39 94t39 94t94 39t94 -39t39 -94zM592 118q0 31 -21.5 52.5t-52.5 21.5t-52.5 -21.5t-21.5 -52.5t21.5 -52.5t52.5 -21.5t52.5 21.5t21.5 52.5z" /> +<glyph unicode="&" horiz-adv-x="630" d="M413 490v47q0 46 -25 76t-70 30q-40 0 -71 -22.5t-31 -61.5q0 -37 32 -53t73 -16h92zM599 13q-56 -28 -102 -28q-47 0 -67.5 31.5t-20.5 80.5v335h-97q-99 0 -141.5 -47.5t-42.5 -147.5q0 -94 31 -153q20 -37 38 -37q11 0 27 16.5t38.5 33t50.5 16.5q25 0 49 -11l-15 -54 q-20 6 -31 6q-23 0 -55 -34.5t-65 -34.5q-41 0 -72.5 25.5t-48 65.5t-25 80.5t-8.5 77.5q0 85 44 158.5t123 76.5q-69 29 -69 90q0 60 52.5 97.5t115.5 37.5q69 0 122 -38t53 -104v-62h99v-58h-97v-331q0 -55 39 -55q26 0 58 14z" /> +<glyph unicode="'" horiz-adv-x="208" d="M126 708l14 -63l-22 -157h-33l-21 157l13 63h49z" /> +<glyph unicode="(" horiz-adv-x="336" d="M307 -84l-7 -62q-128 97 -190.5 210.5t-62.5 267.5q0 133 68.5 253t182.5 191l8 -57q-93 -61 -146.5 -167.5t-53.5 -221.5q0 -253 201 -414z" /> +<glyph unicode=")" horiz-adv-x="339" d="M40 776q114 -71 182.5 -191t68.5 -253q0 -154 -62.5 -267.5t-190.5 -210.5l-7 62q202 161 202 414q0 115 -54 221.5t-147 167.5z" /> +<glyph unicode="*" horiz-adv-x="424" d="M265 674l-34 -113l149 52v-62l-131 -42l106 -163h-70l-77 123l-76 -123h-65l98 163l-130 42v64l150 -54l-36 113h116z" /> +<glyph unicode="+" horiz-adv-x="471" d="M426 220h-162v-173h-58v173h-161v60h161v173h58v-173h162v-60z" /> +<glyph unicode="," horiz-adv-x="258" d="M139 87q13 0 29 -11.5t26 -23.5l9 -11q-41 -87 -129 -184l-45 31q48 69 60 117q5 20 5 41l-2 24q28 17 47 17z" /> +<glyph unicode="-" horiz-adv-x="302" d="M245 285v-60h-191v60h191z" /> +<glyph unicode="." horiz-adv-x="258" d="M194 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40z" /> +<glyph unicode="/" horiz-adv-x="356" d="M339 757l-259 -905h-63l259 905h63z" /> +<glyph unicode="0" d="M225 -10q-82 0 -139 94.5t-57 230.5t57 230.5t139 94.5t139 -94.5t57 -230.5t-57 -230.5t-139 -94.5zM225 571q-50 0 -88.5 -73.5t-38.5 -182.5t38.5 -182.5t88.5 -73.5t88.5 73.5t38.5 182.5t-38.5 182.5t-88.5 73.5z" /> +<glyph unicode="1" d="M224 0v553h-145v54q25 0 63 5.5t64 11.5l25 6h70v-630h-77z" /> +<glyph unicode="2" d="M227 645q82 0 129 -42q36 -33 44 -86t-7 -103.5t-45 -87.5l-145 -176q-14 -17 -26.5 -37.5t-17.5 -32.5l-5 -11h244v-69h-330l-6 62q17 48 69 111l100 120q149 178 61 261q-29 27 -70 27q-72 0 -146 -57l-14 58q7 7 20.5 17.5t56.5 28t88 17.5z" /> +<glyph unicode="3" d="M242 12q-48 -19 -104 -24q-30 -3 -61 -3h-5v57h14.5t33.5 3t49 9t51 19t48.5 31t33.5 47.5t14 66.5q0 92 -139 92q-35 0 -85 -6v60l44 6q52 28 125 84q43 32 43 73q0 13 -4 21q-18 35 -58 35q-29 0 -73.5 -19.5t-72.5 -39.5l-18 55q33 25 83 45.5t97 20.5q92 0 117 -79 q6 -18 6 -37q0 -69 -80 -123l-54 -34q41 0 81 -23q65 -37 65 -115q0 -165 -151 -222z" /> +<glyph unicode="4" d="M356 140v-140h-72v140h-249l-4 65q65 183 244 425h83v-424h64v-66h-66zM284 533q-114 -166 -177 -327h177v327z" /> +<glyph unicode="5" d="M250 13q-50 -22 -111 -26q-22 -2 -67 -2h-5v60q82 -6 152 28q92 44 92 147q0 49 -32 71t-80 22q-41 0 -115 -20l28 337h248v-62h-178l-16 -198q21 5 41 5h26q48 -2 87 -24q29 -15 49 -46t20 -69q0 -86 -39 -141t-100 -82z" /> +<glyph unicode="6" d="M337 221q0 95 -90 111q-15 3 -29 3q-34 0 -90 -25q-8 -38 -8 -74q0 -78 30.5 -134.5t82.5 -56.5q72 0 95 86q9 36 9 90zM330 643l27 -49l-62.5 -40t-44 -33.5t-42.5 -42.5q-47 -58 -67 -117q66 23 105 23h15q22 -1 46 -10t48.5 -27t40 -51.5t15.5 -77.5 q0 -102 -48 -167.5t-136 -65.5q-89 0 -134.5 78.5t-45.5 193.5q0 152 113 267q69 70 170 119z" /> +<glyph unicode="7" d="M387 630v-64q0 -135 -69 -318q-61 -165 -148 -275l-66 28q90 118 149 272q62 164 62 293h-256v64h328z" /> +<glyph unicode="8" d="M409 138q-10 -71 -62.5 -112t-128.5 -41q-74 0 -125.5 51t-51.5 124q0 59 40 116q25 36 78 61q-44 27 -68 55t-30 69q-2 18 -2 26q0 64 43.5 110.5t120.5 47.5h6q83 1 120.5 -41.5t36.5 -109.5v-7q0 -38 -28.5 -78t-82.5 -65q5 -3 25 -13q56 -29 84 -66t28 -91 q0 -16 -3 -36zM314 509q-6 79 -93 79q-38 0 -63.5 -29.5t-25.5 -75.5q0 -39 20.5 -64.5t61.5 -46.5l37 19q63 32 63 104v14zM272 273q-10 5 -24.5 13.5l-25 14.5t-12.5 7q-93 -49 -93 -137q0 -49 30.5 -87.5t79.5 -38.5q48 0 78 38.5t30 86.5q0 69 -63 103z" /> +<glyph unicode="9" d="M334 396q0 77 -30.5 133t-82.5 56q-104 0 -104 -176q0 -50 27 -79t63 -33q6 -1 18 -1q25 0 61 9q23 8 40 17q8 38 8 74zM294 106q-86 -88 -204 -135l-21 58q130 68 176 124q44 53 66 114l-6 -2q-3 -1 -16.5 -6t-22.5 -7q-24 -7 -60 -7q-24 0 -50.5 9t-52.5 27t-43 52.5 t-17 78.5q0 102 47 167.5t135 65.5q64 0 106 -42t61 -113q16 -57 16 -126q0 -142 -114 -258z" /> +<glyph unicode=":" horiz-adv-x="258" d="M194 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM194 353q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40z" /> +<glyph unicode=";" horiz-adv-x="258" d="M136 87q13 0 29 -11.5t25 -23.5l10 -11q-41 -87 -129 -184l-45 31q48 69 60 117q5 20 5 41l-2 24q28 17 47 17zM194 352q0 -25 -19.5 -43.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43.5t44.5 18.5q24 0 41 -16t17 -40z" /> +<glyph unicode="<" horiz-adv-x="300" d="M267 100l-47 -38l-187 188l185 188l47 -38l-149 -150z" /> +<glyph unicode="=" horiz-adv-x="480" d="M426 203v-60h-382v60h382zM426 352v-60h-382v60h382z" /> +<glyph unicode=">" horiz-adv-x="300" d="M33 100l47 -38l187 188l-185 188l-47 -38l149 -150z" /> +<glyph unicode="?" horiz-adv-x="358" d="M194 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM164 162h-61v51q0 42 1 62t10 50.5t27 54.5l82 110q34 47 34 79q0 28 -15 45t-43 17q-43 0 -84 -9v57q47 13 90 13q59 0 93 -31t34 -90q0 -48 -49 -112l-80 -106 q-22 -29 -30.5 -59t-8.5 -81v-51z" /> +<glyph unicode="@" horiz-adv-x="792" d="M497 457q-37 48 -100 48q-38 0 -64.5 -40.5t-36 -85t-9.5 -81.5q0 -29 10.5 -52t36.5 -23q9 0 21 3t20 6t23 11t20 11l21 12.5t17 10.5zM528 146q-35 0 -58 17t-23 51q0 6 2 16q-58 -50 -85 -62q-22 -10 -44 -10q-48 0 -74.5 39.5t-26.5 89.5q0 46 9 90.5t28.5 87 t56.5 69t86 26.5q71 0 110 -57l10 41h65l-69 -289q-6 -22 -6 -33q0 -27 27 -27q61 0 106.5 74t45.5 139q0 123 -79.5 192.5t-204.5 69.5q-124 0 -209.5 -95.5t-85.5 -221.5q0 -132 87.5 -223.5t219.5 -91.5q74 0 142.5 32t112.5 89l47 -25q-46 -68 -132 -110t-170 -42 q-156 0 -264.5 108.5t-108.5 264.5q0 146 109 258.5t254 112.5q144 0 239 -87.5t95 -230.5q0 -55 -27.5 -115t-78 -103.5t-106.5 -43.5z" /> +<glyph unicode="A" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86z" /> +<glyph unicode="B" horiz-adv-x="502" d="M382 184q0 36 -11.5 60t-34 35.5t-47 15.5t-59.5 4h-70v-233q45 -13 75 -13q66 0 106.5 33t40.5 98zM348 472q0 96 -114 96h-74v-204h74q51 0 82.5 29t31.5 79zM235 630q60 0 99 -15q93 -37 93 -144q0 -42 -28 -82.5t-67 -51.5q61 -4 95.5 -47.5t34.5 -106.5 q0 -95 -64 -146.5t-161 -51.5q-61 0 -157 25v620h155z" /> +<glyph unicode="C" horiz-adv-x="497" d="M472 41q-33 -26 -81 -41t-93 -15q-66 0 -115 29.5t-76 78.5t-39.5 104.5t-12.5 117.5q0 58 14.5 114t42.5 105.5t77 80t111 30.5q95 0 162 -43l-29 -52q-58 32 -133 32q-42 0 -75.5 -28.5t-52 -72t-28 -88t-9.5 -82.5q0 -44 8 -86t25.5 -83.5t52 -66.5t81.5 -25 q81 0 136 41z" /> +<glyph unicode="D" horiz-adv-x="547" d="M258 566h-99v-502h93q48 0 81 23t49 62.5t22.5 78.5t6.5 84q0 254 -153 254zM262 630q65 0 111.5 -27.5t71.5 -74.5t36 -100t11 -114t-13 -114.5t-40 -99.5t-75 -73t-112 -27h-172v630h182z" /> +<glyph unicode="E" horiz-adv-x="454" d="M404 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v630h324z" /> +<glyph unicode="F" horiz-adv-x="412" d="M376 630v-66h-216v-201h171v-69h-171v-294h-80v630h296z" /> +<glyph unicode="G" horiz-adv-x="524" d="M464 11q-63 -26 -164 -26q-66 0 -115.5 29t-76.5 78t-40 105t-13 118q0 58 14.5 114t42.5 105.5t77 80t111 30.5q47 0 82 -10t81 -33l-31 -53q-55 34 -132 34q-54 0 -93 -47t-55.5 -107t-16.5 -118q0 -44 7.5 -86t25 -83.5t52 -67t82.5 -25.5q53 0 88 14v206h74v-258z " /> +<glyph unicode="H" horiz-adv-x="548" d="M468 0h-80v295h-228v-295h-80v630h80v-267h228v267h80v-630z" /> +<glyph unicode="I" horiz-adv-x="238" d="M158 630v-630h-78v630h78z" /> +<glyph unicode="J" horiz-adv-x="245" d="M165 -28q0 -100 -142 -135l-17 54q81 27 81 87v652h78v-658z" /> +<glyph unicode="K" horiz-adv-x="485" d="M477 0h-80l-237 263v-263h-80v630h80v-288l220 288h85l-242 -324l254 -282v-24z" /> +<glyph unicode="L" horiz-adv-x="382" d="M156 630v-564h217v-66h-293v630h76z" /> +<glyph unicode="M" horiz-adv-x="742" d="M662 630v-630h-80v428l5 87l-181 -515h-70l-181 516q5 -52 5 -86v-430h-80v630h106l185 -532l185 532h106z" /> +<glyph unicode="N" horiz-adv-x="554" d="M474 0h-82l-237 514q5 -47 5 -86v-428h-80v630h106l213 -482q-5 48 -5 88v394h80v-630z" /> +<glyph unicode="O" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32z" /> +<glyph unicode="P" horiz-adv-x="489" d="M216 566h-56v-255q48 -21 81 -21q59 0 94 43.5t35 103.5q0 65 -43.5 97t-110.5 32zM216 630q103 0 168.5 -48t65.5 -146q0 -93 -55.5 -152t-148.5 -59q-49 0 -86 20v-245h-80v630h136z" /> +<glyph unicode="Q" horiz-adv-x="548" d="M335 -5q63 -67 128 -67q20 0 36 4l-21 -76q-16 -2 -24 -2q-25 0 -47 6.5t-43 22t-33 25.5t-34 32.5l-28 28.5l-18 17q-52 6 -92 40.5t-61.5 84t-32 101.5t-10.5 103q0 54 11.5 108.5t36 106t69 83.5t102.5 32t102.5 -32t69 -83.5t36 -106t11.5 -108.5q0 -102 -39 -196.5 t-119 -123.5zM274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29z" /> +<glyph unicode="R" horiz-adv-x="505" d="M370 437q0 65 -43.5 97t-110.5 32h-56v-255q46 -21 74 -21q63 0 99.5 41.5t36.5 105.5zM480 0h-78l-207 230q-19 6 -35 15v-245h-80v630h136q103 0 168.5 -48t65.5 -146q0 -77 -41 -134t-115 -72l186 -206v-24z" /> +<glyph unicode="S" horiz-adv-x="434" d="M220 645q43 0 84.5 -20t63.5 -54l-45 -38q-38 48 -103 48q-42 0 -69.5 -24t-27.5 -65q0 -40 28 -71.5t67.5 -53.5t79 -47t67.5 -66.5t28 -97.5q0 -77 -54 -124t-132 -47q-37 0 -78.5 16.5t-71.5 42.5l34 57q63 -52 117 -52q47 0 77.5 30.5t30.5 77.5q0 35 -28 64.5 t-67.5 52.5l-79.5 48.5t-68 65.5t-28 92q0 79 47.5 122t127.5 43z" /> +<glyph unicode="T" horiz-adv-x="405" d="M395 560h-154v-560h-78v560h-153v70h385v-70z" /> +<glyph unicode="U" horiz-adv-x="558" d="M479 241q0 -110 -48 -183t-152 -73t-152 74t-48 186v385h80v-386q0 -193 120 -193t120 194v385h80v-389z" /> +<glyph unicode="V" horiz-adv-x="510" d="M505 630l-206 -630h-88l-205 630h87l164 -534l165 534h83z" /> +<glyph unicode="W" horiz-adv-x="708" d="M700 630l-148 -630h-85l-112 509l-112 -509h-85l-148 630h85l97 -446q8 -35 8 -66l115 512h80l115 -512q0 31 8 66l101 446h81z" /> +<glyph unicode="X" horiz-adv-x="492" d="M480 0h-92l-148 250l-143 -250h-87l187 323l-182 307h87l139 -231l134 231h82l-173 -303z" /> +<glyph unicode="Y" horiz-adv-x="471" d="M459 630l-184 -399v-231h-79v231l-183 399h85l140 -314l141 314h80z" /> +<glyph unicode="Z" horiz-adv-x="458" d="M401 630v-69q-147 -242 -277 -491h304v-70h-392v68q138 273 272 493h-250v69h343z" /> +<glyph unicode="[" horiz-adv-x="248" d="M183 732v-58h-93v-772h93v-58h-155v888h155z" /> +<glyph unicode="\" horiz-adv-x="335" d="M333 -148h-62l-259 905h62z" /> +<glyph unicode="]" horiz-adv-x="249" d="M221 732v-888h-155v58h93v772h-93v58h155z" /> +<glyph unicode="^" horiz-adv-x="624" d="M551 320h-70l-170 307l-170 -307h-70l212 388h56q198 -364 212 -388z" /> +<glyph unicode="_" d="M450 -75v-60h-450v60h450z" /> +<glyph unicode="`" horiz-adv-x="338" d="M261 606l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="a" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117z" /> +<glyph unicode="b" horiz-adv-x="479" d="M227 -16q-49 0 -137 22v508h135q53 0 85 -12q81 -31 81 -120q0 -16 -7.5 -38t-18.5 -35q-15 -17 -39 -32q94 -26 94 -127q0 -76 -54 -121t-139 -45zM282 442q-22 12 -58 12h-55v-152l54 -1q39 -1 64.5 20.5t25.5 61.5q0 44 -31 59zM308 223q-23 16 -87 16h-52v-180 q34 -9 56 -9q53 0 84.5 25.5t31.5 74.5q0 51 -33 73z" /> +<glyph unicode="c" horiz-adv-x="465" d="M273 -16q-56 0 -97.5 24.5t-65 65.5t-34.5 87t-11 96q0 113 53 188q58 85 157 85q82 0 142 -38l-29 -52q-57 29 -113 29q-28 0 -57 -21.5t-46 -55.5q-28 -56 -28 -138q0 -84 24 -131q38 -76 108 -76q59 0 114 38l35 -51q-68 -50 -152 -50z" /> +<glyph unicode="d" horiz-adv-x="509" d="M239 0h-149v514h157q95 0 146 -67t51 -191q0 -126 -57 -191t-148 -65zM244 452h-76v-390h71q126 0 126 193q0 197 -121 197z" /> +<glyph unicode="e" horiz-adv-x="437" d="M90 0v514h282v-64h-203v-149h156v-66h-156v-169h212v-66h-291z" /> +<glyph unicode="f" horiz-adv-x="394" d="M169 450v-149h143v-66h-143v-235h-79v514h260v-64h-181z" /> +<glyph unicode="g" horiz-adv-x="498" d="M386 440q-46 29 -111 29q-28 0 -56.5 -21t-46.5 -56q-28 -56 -28 -138q0 -84 24 -131q38 -76 108 -76q34 0 67 10v169h74v-218q-60 -23 -142 -23q-45 0 -81.5 16t-60 43t-39.5 62.5t-22.5 73t-6.5 77.5q0 113 53 188q59 85 157 85q42 0 71 -8.5t72 -29.5z" /> +<glyph unicode="h" horiz-adv-x="514" d="M346 0v236h-177v-236h-79v514h79v-213h177v213h79v-514h-79z" /> +<glyph unicode="i" horiz-adv-x="261" d="M90 0v514h82v-514h-82z" /> +<glyph unicode="j" horiz-adv-x="255" d="M136 -87q-31 -28 -102 -47l-18 54q41 13 61 36q8 9 8 30v528h80v-533q0 -44 -29 -68z" /> +<glyph unicode="k" horiz-adv-x="457" d="M357 0l-188 201v-201h-79v514h79v-218l173 218h86l-205 -264l209 -219v-31h-75z" /> +<glyph unicode="l" horiz-adv-x="367" d="M90 0v514h76v-450h181v-64h-257z" /> +<glyph unicode="m" horiz-adv-x="673" d="M505 0v369l-127 -369h-66l-144 375v-26v-349h-78v514h98l134 -346q0 -1 1.5 -3.5t3 -7.5t4 -12l6 -17t6.5 -21l18 60l124 347h98v-514h-78z" /> +<glyph unicode="n" horiz-adv-x="527" d="M356 0l-185 384q0 -11 0.5 -19.5t1 -12t0.5 -5.5v-347h-79v515h98l164 -357q-1 26 -1 36v321h79v-515h-78z" /> +<glyph unicode="o" horiz-adv-x="505" d="M253 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM253 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5z" /> +<glyph unicode="p" horiz-adv-x="455" d="M354 221q-47 -41 -120 -41q-31 0 -65 17v-197h-79v514h119q103 0 152 -45.5t49 -114.5q0 -85 -56 -133zM209 452h-40v-194q31 -15 55 -15q50 0 78.5 31.5t28.5 80.5q0 44 -31 70.5t-91 26.5z" /> +<glyph unicode="q" horiz-adv-x="505" d="M427 -118q-12 -2 -25 -2q-21 0 -40 6t-38 19t-30.5 22.5t-32 30t-28.5 27.5q-57 7 -96.5 52t-55.5 102t-16 120q0 65 18.5 124.5t63 103t106.5 43.5q63 0 107.5 -44t62.5 -103.5t18 -126.5q0 -83 -31.5 -156.5t-95.5 -103.5q54 -45 96 -45q12 0 38 5zM253 466 q-31 0 -54 -20t-34.5 -53t-16.5 -68t-5 -74q0 -37 5 -70t16.5 -64.5t34.5 -50t54 -18.5q42 0 67.5 34.5t33.5 77.5t8 96q0 38 -5 72.5t-17 66.5t-34 51.5t-53 19.5z" /> +<glyph unicode="r" horiz-adv-x="481" d="M361 0l-173 184l-19 9v-193h-79v514h119q103 0 152 -46t49 -114q0 -60 -32 -105.5t-93 -62.5l150 -159v-27h-74zM209 452h-40v-194q1 -1 8.5 -4.5t21 -7t25.5 -3.5q50 0 78.5 31.5t28.5 80.5q0 44 -31 70.5t-91 26.5z" /> +<glyph unicode="s" horiz-adv-x="408" d="M196 -16q-60 0 -133 52l35 55q18 -13 37 -24q38 -20 62 -20q36 0 60 22t24 59q0 43 -52 76l-95 57q-79 49 -79 132q0 65 41.5 101t110.5 36q35 0 66 -14.5t44 -27t21 -24.5l-47 -38q-38 42 -84 42q-33 0 -54 -17.5t-21 -48.5q0 -51 56 -84l86 -53q83 -49 83 -138 q0 -64 -46.5 -103.5t-114.5 -39.5z" /> +<glyph unicode="t" horiz-adv-x="388" d="M232 447v-447h-77v447h-127v67h332v-67h-128z" /> +<glyph unicode="u" horiz-adv-x="522" d="M261 -16q-88 0 -130 61.5t-42 152.5v316h78v-316q0 -150 94 -150t94 150v316h78v-319q0 -92 -42 -151.5t-130 -59.5z" /> +<glyph unicode="v" horiz-adv-x="466" d="M274 0h-82l-173 514h84l132 -412l132 412h81z" /> +<glyph unicode="w" horiz-adv-x="627" d="M482 0h-77q-73 307 -89 383l-6 -28l-83 -355h-80l-125 514h83l82 -369l91 369h76l90 -369l83 369h80z" /> +<glyph unicode="x" horiz-adv-x="452" d="M341 0l-119 194l-115 -194h-86l158 264l-154 250h86l112 -178l107 178h82l-147 -247l166 -267h-90z" /> +<glyph unicode="y" horiz-adv-x="416" d="M247 189v-189h-78v189l-155 325h84l112 -242l113 242h80z" /> +<glyph unicode="z" horiz-adv-x="429" d="M49 0v61q117 224 211 370l11 17h-204v66h298v-62q-116 -182 -211 -355l-16 -30h249v-67h-338z" /> +<glyph unicode="{" horiz-adv-x="333" d="M290 -108l-11 -54h-57q-20 0 -35 4t-31.5 16t-25 39.5t-8.5 69.5v176q0 50 -25.5 85.5t-56.5 35.5v47q31 0 56.5 35t25.5 85v176q0 78 27 104t71 26h59l11 -54h-59q-28 0 -38 -17.5t-10 -72.5v-176q0 -40 -18.5 -73t-36.5 -46l-19 -13q3 -2 8 -5t18 -14.5t22.5 -25 t17.5 -36t8 -47.5v-176q0 -55 10.5 -72t43.5 -17h53z" /> +<glyph unicode="|" horiz-adv-x="222" d="M141 756v-774h-60v774h60z" /> +<glyph unicode="}" horiz-adv-x="333" d="M290 264q-31 0 -56.5 -35.5t-25.5 -85.5v-176q0 -42 -8.5 -69.5t-25 -39.5t-31.5 -16t-35 -4h-57l-11 54h53q33 0 43.5 17t10.5 72v176q0 25 7.5 47.5t18.5 36.5t22 24.5t19 14.5l7 5q-3 2 -8 5t-18 15.5t-22.5 26.5t-17.5 37t-8 48v176q0 55 -10 72.5t-38 17.5h-59 l11 54h59q20 0 35 -4t30.5 -16.5t24 -40t8.5 -69.5v-176q0 -50 25.5 -85t56.5 -35v-47z" /> +<glyph unicode="~" horiz-adv-x="666" d="M146 308q12 26 36 44t51 18q38 0 104 -34t102 -34q55 0 81 58l-1 -72q-12 -23 -36.5 -37.5t-50.5 -14.5q-37 0 -104 34t-102 34q-52 0 -80 -58v62z" /> +<glyph unicode="¡" horiz-adv-x="264" d="M58 442q0 25 19.5 43.5t44.5 18.5q24 0 41 -16.5t17 -40.5t-19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40zM70 -210q17 97 17 193v344h60q15 -276 15 -368q0 -83 -16 -159z" /> +<glyph unicode="¢" horiz-adv-x="412" d="M219 50v388q-49 -16 -71.5 -72t-22.5 -116q0 -61 23 -121.5t71 -78.5zM264 571v-68q67 -7 109 -48l-34 -45q-31 25 -75 32v-396q40 5 98 43l25 -47q-48 -45 -123 -56v-60h-45v60q-65 11 -110 64q-60 69 -60 190q0 116 49 182q47 65 121 80v69h45z" /> +<glyph unicode="£" horiz-adv-x="442" d="M398 37q-20 -22 -50 -37t-58 -15q-13 0 -35 3.5t-37 7.5l-15 4h-150q36 161 36 251v129l-59 6l20 50h39v132q0 63 47.5 93.5t113.5 30.5q71 0 104 -28l-17 -46q-31 18 -81 18q-91 0 -91 -83v-117h134v-60h-134v-127q0 -88 -23 -188h71q58 -19 88 -19q23 0 47 13t39 32z " /> +<glyph unicode="¤" horiz-adv-x="542" d="M550 620l-28 -60q-55 67 -147 67q-161 0 -185 -195h274l-30 -63h-252q-2 -26 -2 -33q0 -8 2 -30h223l-30 -63h-185q11 -93 58 -142.5t117 -49.5q42 0 83.5 21t71.5 58v-80q-62 -62 -160 -62q-95 0 -162 69t-78 186h-110l30 63h71q-1 13 -1 31q0 19 1 32h-101l30 63h80 q12 112 81.5 185t178.5 73q100 0 170 -70z" /> +<glyph unicode="¥" horiz-adv-x="476" d="M467 317h-148l-41 -89v-5h187v-60h-187v-163h-80v163h-183v60h183v6l-41 88h-140v60h112l-117 253h85l141 -314l141 314h84q-116 -253 -117 -253h121v-60z" /> +<glyph unicode="§" horiz-adv-x="420" d="M301 262q0 32 -25.5 51.5t-58.5 19.5q-46 0 -73.5 -23t-27.5 -68q0 -28 25.5 -45.5t54.5 -17.5q45 0 75 20t30 63zM211 647q77 0 144 -38l-27 -52q-68 32 -119 32q-39 0 -64 -20.5t-25 -58.5q0 -25 26 -45t63 -36.5t73.5 -35.5t62.5 -51.5t26 -74.5q0 -53 -29 -87.5 t-81 -45.5q31 -16 50.5 -29t37.5 -38t18 -54q0 -75 -48 -115t-124 -40q-91 0 -174 49l28 55q72 -44 150 -44q42 0 69 20.5t27 61.5q0 30 -26 53t-63.5 39.5l-75 34.5t-63.5 46.5t-26 65.5q0 61 36 95t98 43q-72 34 -94 54q-36 32 -36 76q0 68 48 104t118 36z" /> +<glyph unicode="¨" horiz-adv-x="384" d="M157 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM311 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="©" horiz-adv-x="879" d="M763 245q0 -133 -94.5 -227.5t-227.5 -94.5t-227 94.5t-94 227.5t94 227t227 94q134 0 228 -94t94 -227zM714 245q0 113 -80 193t-193 80t-192.5 -80t-79.5 -193q0 -115 80 -194t195 -79q112 0 191 80t79 193zM555 114q-20 -19 -51 -33.5t-56 -14.5q-74 0 -116 51 t-42 127q0 75 40 128t113 53q60 0 102 -39l-27 -34q-30 25 -75 25q-51 0 -73.5 -37.5t-22.5 -92.5q0 -57 24.5 -95.5t78.5 -38.5q37 0 84 34z" /> +<glyph unicode="ª" horiz-adv-x="378" d="M276 374l-41 123h-112l-40 -123h-63l128 391h62l129 -391h-63zM179 693l-44 -147h89z" /> +<glyph unicode="«" horiz-adv-x="500" d="M267 100l-47 -38l-187 188l185 188l47 -38l-149 -150zM445 100l-47 -38l-187 188l185 188l47 -38l-149 -150z" /> +<glyph unicode="¬" horiz-adv-x="535" d="M449 176l-59 -11v206h-344v60h403v-255z" /> +<glyph unicode="®" horiz-adv-x="879" d="M760 244q0 -134 -94 -228t-227 -94t-227 94.5t-94 227.5q0 134 93.5 227.5t227.5 93.5t227.5 -93.5t93.5 -227.5zM711 244q0 113 -80 193t-192 80q-113 0 -192.5 -80t-79.5 -193q0 -116 79.5 -194.5t195.5 -78.5q113 0 191 80t78 193zM582 71h-53l-120 121q-11 3 -22 9 v-130h-51v355h86q61 0 103.5 -29t42.5 -87q0 -41 -28 -77.5t-68 -40.5l110 -106v-15zM515 312q0 38 -26.5 55.5t-66.5 17.5h-35v-142q28 -13 46 -13q37 0 59.5 22t22.5 60z" /> +<glyph unicode="¯" horiz-adv-x="335" d="M304 632v-59h-274v59h274z" /> +<glyph unicode="°" horiz-adv-x="352" d="M172 601q-39 0 -66.5 -27.5t-27.5 -66.5t27.5 -66.5t66.5 -27.5t66.5 27.5t27.5 66.5t-27.5 66.5t-66.5 27.5zM172 646q57 0 98 -41t41 -98t-41 -98t-98 -41t-98 41t-41 98t41 98t98 41z" /> +<glyph unicode="±" horiz-adv-x="447" d="M416 59v-60h-361v60h361zM426 220h-162v-125h-58v125h-161v60h161v173h58v-173h162v-60z" /> +<glyph unicode="²" d="M227 645q82 0 129 -42q36 -33 44 -86t-7 -103.5t-45 -87.5l-145 -176q-14 -17 -26.5 -37.5t-17.5 -32.5l-5 -11h244v-69h-330l-6 62q17 48 69 111l100 120q149 178 61 261q-29 27 -70 27q-72 0 -146 -57l-14 58q7 7 20.5 17.5t56.5 28t88 17.5z" /> +<glyph unicode="³" d="M242 12q-48 -19 -104 -24q-30 -3 -61 -3h-5v57h14.5t33.5 3t49 9t51 19t48.5 31t33.5 47.5t14 66.5q0 92 -139 92q-35 0 -85 -6v60l44 6q52 28 125 84q43 32 43 73q0 13 -4 21q-18 35 -58 35q-29 0 -73.5 -19.5t-72.5 -39.5l-18 55q33 25 83 45.5t97 20.5q92 0 117 -79 q6 -18 6 -37q0 -69 -80 -123l-54 -34q41 0 81 -23q65 -37 65 -115q0 -165 -151 -222z" /> +<glyph unicode="´" horiz-adv-x="338" d="M213 752l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="µ" horiz-adv-x="519" d="M181 -88q0 -37 -19 -70l-100 -18q29 67 29 112q0 30 -5.5 91t-5.5 91v372h76v-372q0 -37 10.5 -54t45.5 -17q34 0 79 17.5t70 39.5v386h76v-415q0 -39 21 -40l-25 -50q-27 0 -46.5 15.5t-23.5 41.5q-31 -25 -74.5 -41t-83.5 -16q-15 0 -43 15q19 -57 19 -88z" /> +<glyph unicode="¶" horiz-adv-x="543" d="M497 639l-3 -51l-52 -12q0 -170 -34 -362t-98 -322l-64 20q69 125 104 310t35 354q-125 13 -143 13q-42 0 -59.5 -35.5t-17.5 -81.5q0 -25 3.5 -48.5t12.5 -48t27.5 -39.5t45.5 -15q43 0 81 30q-50 -99 -143 -99q-80 0 -122.5 59t-42.5 142q0 84 51.5 138t134.5 54 q26 0 55.5 -2.5t65 -6.5l54.5 -6l-1 43h56q0 -29 -1 -43z" /> +<glyph unicode="·" horiz-adv-x="258" d="M194 246q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40z" /> +<glyph unicode="¸" horiz-adv-x="402" d="M331 -63q0 -33 -33 -64t-70 -47l-38 31q81 44 81 75q0 13 -9 21t-23 8q-13 0 -49 -9v47h104q37 -27 37 -62z" /> +<glyph unicode="¹" d="M224 0v553h-145v54q25 0 63 5.5t64 11.5l25 6h70v-630h-77z" /> +<glyph unicode="º" horiz-adv-x="392" d="M203 361q-70 0 -106.5 64.5t-36.5 145.5q0 79 37 142t106 63q70 0 106.5 -63.5t36.5 -144.5q0 -80 -36.5 -143.5t-106.5 -63.5zM203 727q-31 0 -50.5 -27t-26 -61t-6.5 -74q0 -64 19 -109.5t64 -45.5q32 0 51.5 26.5t25.5 59t6 72.5t-6.5 73t-26 59.5t-50.5 26.5z" /> +<glyph unicode="»" horiz-adv-x="500" d="M242 100l47 -38l187 188l-185 188l-47 -38l149 -150zM64 100l47 -38l187 188l-185 188l-47 -38l149 -150z" /> +<glyph unicode="¼" horiz-adv-x="1239" d="M1145 140v-140h-72v140h-249l-4 65q65 183 244 425h83v-424h64v-66h-66zM1073 533q-114 -166 -177 -327h177v327zM870 745l-430 -876h-63l430 876h63zM224 0v553h-145v54q25 0 63 5.5t64 11.5l25 6h70v-630h-77z" /> +<glyph unicode="½" horiz-adv-x="1239" d="M1016 645q82 0 129 -42q36 -33 44 -86t-7 -103.5t-45 -87.5l-145 -176q-14 -17 -26.5 -37.5t-17.5 -32.5l-5 -11h244v-69h-330l-6 62q17 48 69 111l100 120q149 178 61 261q-29 27 -70 27q-72 0 -146 -57l-14 58q7 7 20.5 17.5t56.5 28t88 17.5zM870 745l-430 -876h-63 l430 876h63zM224 0v553h-145v54q25 0 63 5.5t64 11.5l25 6h70v-630h-77z" /> +<glyph unicode="¾" horiz-adv-x="1239" d="M1145 140v-140h-72v140h-249l-4 65q65 183 244 425h83v-424h64v-66h-66zM1073 533q-114 -166 -177 -327h177v327zM870 745l-430 -876h-63l430 876h63zM242 12q-48 -19 -104 -24q-30 -3 -61 -3h-5v57h14.5t33.5 3t49 9t51 19t48.5 31t33.5 47.5t14 66.5q0 92 -139 92 q-35 0 -85 -6v60l44 6q52 28 125 84q43 32 43 73q0 13 -4 21q-18 35 -58 35q-29 0 -73.5 -19.5t-72.5 -39.5l-18 55q33 25 83 45.5t97 20.5q92 0 117 -79q6 -18 6 -37q0 -69 -80 -123l-54 -34q41 0 81 -23q65 -37 65 -115q0 -165 -151 -222z" /> +<glyph unicode="¿" horiz-adv-x="358" d="M180 444q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40t-19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40zM210 325h61v-48q0 -42 -1 -62t-10 -50.5t-27 -54.5l-82 -110q-34 -45 -34 -78q0 -29 15 -46t43 -17q43 0 84 9v-57q-54 -13 -90 -13q-59 0 -93 31t-34 91q0 46 49 112 l80 105q23 30 31 59.5t8 80.5v48z" /> +<glyph unicode="À" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86zM366 736l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="Á" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86zM304 882l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="Â" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86zM405 736l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="Ã" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86zM188 830q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43q74 58 120 58z " /> +<glyph unicode="Ä" horiz-adv-x="510" d="M255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86zM224 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM378 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5 t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="Å" horiz-adv-x="510" d="M315 784q0 24 -17 42t-40 18q-25 0 -43.5 -18t-18.5 -42t18 -42t41 -18q24 0 42 18t18 42zM361 784q0 -44 -31.5 -75t-74.5 -31t-73.5 31t-30.5 75q0 43 31.5 74.5t74.5 31.5t73.5 -31t30.5 -75zM255 547l-75 -278h150zM498 0h-82l-67 203h-188l-67 -203h-82l200 630h86z " /> +<glyph unicode="Æ" horiz-adv-x="726" d="M255 547l-74 -278h149zM676 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v203h-190l-67 -203h-83l199 630h93l48 -211v211h324z" /> +<glyph unicode="Ç" horiz-adv-x="497" d="M376 -63q0 -33 -33 -64t-70 -47l-38 31q81 44 81 75q0 13 -9 21t-23 8q-13 0 -49 -9v40q-50 14 -86.5 48t-56 80.5t-28.5 95t-9 99.5q0 58 14.5 114t42.5 105.5t77 80t111 30.5q97 0 162 -43l-29 -52q-62 33 -133 33q-40 0 -75.5 -30.5t-54.5 -70.5q-35 -71 -35 -171 q0 -44 8 -86t25.5 -84t52 -67.5t81.5 -25.5q80 0 135 44l35 -51q-48 -39 -122 -52q26 -26 26 -52z" /> +<glyph unicode="È" horiz-adv-x="454" d="M404 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v630h324zM355 736l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="É" horiz-adv-x="454" d="M404 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v630h324zM295 882l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="Ê" horiz-adv-x="454" d="M404 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v630h324zM396 736l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="Ë" horiz-adv-x="454" d="M404 630v-67h-244v-200h187v-69h-187v-225h254v-69h-334v630h324zM215 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM369 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5 q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="Ì" horiz-adv-x="238" d="M158 630v-630h-78v630h78zM296 736l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="Í" horiz-adv-x="238" d="M158 630v-630h-78v630h78zM234 882l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="Î" horiz-adv-x="238" d="M158 630v-630h-78v630h78zM386 736l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="Ï" horiz-adv-x="238" d="M158 630v-630h-78v630h78zM178 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM332 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="Ñ" horiz-adv-x="554" d="M474 0h-82l-237 514q5 -47 5 -86v-428h-80v630h106l213 -482q-5 48 -5 88v394h80v-630zM215 830q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43 q74 58 120 58z" /> +<glyph unicode="Ò" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32zM380 736l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="Ó" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32zM321 882l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="Ô" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32zM426 739l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="Õ" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32zM206 830q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43 q74 58 120 58z" /> +<glyph unicode="Ö" horiz-adv-x="548" d="M274 579q-38 0 -67 -29t-43.5 -73.5t-21.5 -85.5t-7 -76t7 -76t21.5 -85.5t43.5 -73.5t67 -29t67 29t43.5 73.5t21.5 85.5t7 76t-7 76t-21.5 85.5t-43.5 73.5t-67 29zM274 645q58 0 102.5 -32t69 -83.5t36 -106t11.5 -108.5t-11.5 -108.5t-36 -106t-69 -83.5t-102.5 -32 t-102.5 32t-69 83.5t-36 106t-11.5 108.5t11.5 108.5t36 106t69 83.5t102.5 32zM244 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM398 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5 t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="Ø" horiz-adv-x="548" d="M363 531l-135 -471q22 -9 46 -9q44 0 73.5 27t42.5 72t18 84t5 81q0 145 -50 216zM188 93l136 474q-22 12 -50 12q-43 0 -73 -28t-43 -74.5t-18 -86t-5 -81.5q0 -150 53 -216zM171 -140h-50l46 161q-58 45 -85 128t-27 170q0 54 12 108t36 104.5t68.5 82t102.5 31.5 q38 0 68 -13l34 117h50l-41 -143q56 -45 82 -125.5t26 -165.5q0 -54 -11.5 -108.5t-35.5 -106t-68.5 -83.5t-103.5 -32q-31 0 -64 11z" /> +<glyph unicode="Ù" horiz-adv-x="558" d="M479 241q0 -110 -48 -183t-152 -73t-152 74t-48 186v385h80v-386q0 -193 120 -193t120 194v385h80v-389zM389 736l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="Ú" horiz-adv-x="558" d="M479 241q0 -110 -48 -183t-152 -73t-152 74t-48 186v385h80v-386q0 -193 120 -193t120 194v385h80v-389zM329 882l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="Û" horiz-adv-x="558" d="M479 241q0 -110 -48 -183t-152 -73t-152 74t-48 186v385h80v-386q0 -193 120 -193t120 194v385h80v-389zM430 736l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="Ü" horiz-adv-x="550" d="M479 241q0 -110 -48 -183t-152 -73t-152 74t-48 186v385h80v-386q0 -193 120 -193t120 194v385h80v-389zM250 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM404 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5 t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="Ý" horiz-adv-x="471" d="M358 896l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146zM459 630l-184 -399v-231h-79v231l-183 399h85l140 -314l141 314h80z" /> +<glyph unicode="ß" horiz-adv-x="572" d="M542 124q0 -69 -50.5 -104t-122.5 -35q-92 0 -174 50l28 55q72 -45 150 -45q41 0 68.5 22t27.5 61q0 29 -26 52t-63.5 39.5l-75 34.5t-63.5 46.5t-26 66.5q0 67 44 103t113 36q7 26 7 46q0 46 -30.5 71t-76.5 25q-43 0 -75 -26.5t-32 -68.5v-553h-76v432l-66 9v49h66v76 q0 65 54 103t122 38q76 0 127 -45t51 -120q0 -26 -20 -102q-17 9 -44 9q-39 0 -64 -20.5t-25 -58.5q0 -24 26 -44t63 -36.5t74 -36t63 -52.5t26 -77z" /> +<glyph unicode="à" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM335 631l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="á" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM272 777l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="â" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM382 631l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="ã" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM165 725q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43 q74 58 120 58z" /> +<glyph unicode="ä" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM204 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM358 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5 t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="å" horiz-adv-x="465" d="M359 0l-54 162h-147l-53 -162h-83l169 514h81l170 -514h-83zM232 419l-59 -193h117zM233 744q-25 0 -43.5 -17.5t-18.5 -42.5q0 -24 17.5 -42t41.5 -18t42 18t18 42t-16.5 42t-40.5 18zM232 790q43 0 73.5 -31.5t30.5 -74.5q0 -44 -31 -75t-75 -31q-43 0 -73.5 31.5 t-30.5 74.5t31.5 74.5t74.5 31.5z" /> +<glyph unicode="æ" horiz-adv-x="645" d="M302 0v160h-145l-53 -160h-82l167 515h86l27 -151v151h279v-66h-200v-150h153v-67h-153v-164h208v-68h-287zM232 411l-58 -186h108z" /> +<glyph unicode="ç" horiz-adv-x="465" d="M337 -43q4 -9 4 -20q0 -32 -33 -63.5t-70 -47.5l-38 31q81 44 81 75q0 5 -1 8q-7 21 -31 21q-11 0 -49 -9v45q-69 26 -102 99t-33 161q0 113 53 188q58 85 157 85q82 0 142 -38l-29 -52q-57 29 -113 29q-28 0 -57 -21.5t-46 -55.5q-28 -56 -28 -138q0 -84 24 -131 q38 -76 108 -76q59 0 114 38l35 -51q-47 -35 -109 -46q14 -14 21 -31z" /> +<glyph unicode="è" horiz-adv-x="437" d="M90 0v514h282v-64h-203v-149h156v-66h-156v-169h212v-66h-291zM340 631l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="é" horiz-adv-x="437" d="M90 0v514h282v-64h-203v-149h156v-66h-156v-169h212v-66h-291zM280 777l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="ê" horiz-adv-x="437" d="M386 631l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146zM102 0v514h282v-64h-203v-149h156v-66h-156v-169h212v-66h-291z" /> +<glyph unicode="ë" horiz-adv-x="437" d="M90 0v514h282v-64h-203v-149h156v-66h-156v-169h212v-66h-291zM204 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM358 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5 q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="ì" horiz-adv-x="261" d="M90 0v514h82v-514h-82zM306 631l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="í" horiz-adv-x="261" d="M90 0v514h82v-514h-82zM244 777l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="î" horiz-adv-x="261" d="M90 0v514h82v-514h-82zM396 631l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="ï" horiz-adv-x="261" d="M90 0v514h82v-514h-82zM188 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM342 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="ñ" horiz-adv-x="539" d="M366 0l-185 384q0 -11 0.5 -19.5t1 -12t0.5 -5.5v-347h-79v515h98l164 -357q-1 26 -1 36v321h79v-515h-78zM201 725q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8 t-19 -11.5t-16 -10l-29 43q74 58 120 58z" /> +<glyph unicode="ò" horiz-adv-x="505" d="M263 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM263 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5zM363 631l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="ó" horiz-adv-x="505" d="M263 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM263 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5zM310 777l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="ô" horiz-adv-x="505" d="M263 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM263 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5zM406 631l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="õ" horiz-adv-x="505" d="M263 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM263 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5zM188 725q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43q74 58 120 58z" /> +<glyph unicode="ö" horiz-adv-x="505" d="M263 -16q-48 0 -86 25.5t-59 67t-32 88t-11 95.5q0 65 18.5 124.5t63 102.5t106.5 43q63 0 107.5 -44t62.5 -103.5t18 -125.5t-18 -125.5t-62.5 -103.5t-107.5 -44zM263 466q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -37 5 -70.5t16.5 -65t34.5 -50t54 -18.5 q42 0 67.5 35t33.5 78t8 96q0 52 -8.5 96t-34.5 78.5t-66 34.5zM228 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM382 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5 q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="÷" horiz-adv-x="489" d="M308 404q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM308 107q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM434 280v-60h-380v60h380z" /> +<glyph unicode="ø" horiz-adv-x="505" d="M182 85l108 373q-18 8 -37 8q-31 0 -54 -19.5t-34.5 -52.5t-16.5 -68t-5 -74q0 -114 39 -167zM329 419l-106 -366q15 -5 30 -5q42 0 67.5 35t33.5 78t8 96q0 105 -33 162zM382 605l-31 -109q45 -36 67.5 -102t22.5 -137q0 -66 -18 -125.5t-62.5 -103.5t-107.5 -44 q-22 0 -48 7l-29 -102h-50l36 124q-48 36 -72.5 104.5t-24.5 142.5q0 65 18.5 124.5t63 102.5t106.5 43q29 0 55 -9l24 84h50z" /> +<glyph unicode="ù" horiz-adv-x="522" d="M271 -16q-88 0 -130 61.5t-42 152.5v316h78v-316q0 -150 94 -150t94 150v316h78v-319q0 -92 -42 -151.5t-130 -59.5zM373 631l-32 -45q-117 62 -184 128l62 63q52 -72 154 -146z" /> +<glyph unicode="ú" horiz-adv-x="522" d="M271 -16q-88 0 -130 61.5t-42 152.5v316h78v-316q0 -150 94 -150t94 150v316h78v-319q0 -92 -42 -151.5t-130 -59.5zM312 777l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146z" /> +<glyph unicode="û" horiz-adv-x="522" d="M413 631l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146zM271 -16q-88 0 -130 61.5t-42 152.5v316h78v-316q0 -150 94 -150t94 150v316h78v-319q0 -92 -42 -151.5t-130 -59.5z" /> +<glyph unicode="ü" horiz-adv-x="522" d="M271 -16q-88 0 -130 61.5t-42 152.5v316h78v-316q0 -150 94 -150t94 150v316h78v-319q0 -92 -42 -151.5t-130 -59.5zM233 667q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM387 667q0 -22 -19 -39t-41 -17 q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="ý" horiz-adv-x="416" d="M331 781l62 -63q-68 -67 -184 -128l-32 45q98 71 154 146zM247 189v-189h-78v189l-155 325h84l112 -242l113 242h80z" /> +<glyph unicode="ÿ" horiz-adv-x="416" d="M187 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM341 642q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM257 189v-189h-78v189l-155 325h84l112 -242 l113 242h80z" /> +<glyph unicode="Œ" horiz-adv-x="638" d="M264 566q-41 0 -69.5 -27t-41 -71t-17 -81.5t-4.5 -77.5q0 -39 5 -75.5t17.5 -78t41 -67t68.5 -25.5v503zM588 630v-67h-244v-200h187v-69h-187v-225h254v-69h-335q-56 0 -98.5 31.5t-65 81.5t-33.5 102t-11 104q0 50 11.5 101.5t34.5 100t65 79t97 30.5h325z" /> +<glyph unicode="œ" horiz-adv-x="583" d="M244 0q-61 0 -103 42t-59 98.5t-17 119.5q0 62 17.5 117.5t60 96t101.5 40.5h275v-66h-202v-146h155v-68h-155v-166h210v-68h-283zM238 450q-36 -5 -58 -37.5t-29.5 -72t-7.5 -88.5q0 -46 7.5 -84t29.5 -69t58 -35v386z" /> +<glyph unicode="Ÿ" horiz-adv-x="471" d="M459 630l-184 -399v-231h-79v231l-183 399h85l140 -314l141 314h80zM217 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5q17 0 29 -11.5t12 -28.5zM371 772q0 -22 -19 -39t-41 -17q-16 0 -27 10.5t-11 26.5q0 22 17.5 40.5t39.5 18.5 q17 0 29 -11.5t12 -28.5z" /> +<glyph unicode="ˆ" horiz-adv-x="402" d="M366 606l-32 -46q-57 30 -121 100q-65 -71 -121 -100l-32 45q80 55 153 147q62 -80 153 -146z" /> +<glyph unicode="˜" horiz-adv-x="380" d="M121 700q29 0 71.5 -22t65.5 -22q26 0 41 6.5t43 25.5l31 -38q-36 -33 -61 -44.5t-61 -11.5q-20 0 -62.5 22t-70.5 22q-9 0 -17.5 -2t-19.5 -7.5l-16 -8t-19 -11.5t-16 -10l-29 43q74 58 120 58z" /> +<glyph unicode="–" horiz-adv-x="358" d="M358 285v-60h-358v60h358z" /> +<glyph unicode="—" horiz-adv-x="608" d="M608 286v-60h-608v60h608z" /> +<glyph unicode="‘" horiz-adv-x="267" d="M110 431q-13 0 -29 11.5t-25 23.5l-10 11q41 87 129 184l45 -31q-48 -69 -60 -117q-5 -20 -5 -41l2 -24q-28 -17 -47 -17z" /> +<glyph unicode="’" horiz-adv-x="270" d="M158 646q13 0 29 -11.5t25 -22.5l10 -12q-41 -87 -129 -184l-45 31q48 69 60 117q5 20 5 41l-2 24q28 17 47 17z" /> +<glyph unicode="‚" horiz-adv-x="258" d="M139 90q13 0 29 -11.5t26 -22.5l9 -12q-41 -87 -129 -184l-45 31q48 69 60 117q5 20 5 41l-2 24q28 17 47 17z" /> +<glyph unicode="“" horiz-adv-x="446" d="M288 431l47 17q-2 9 -2 24q0 21 5 41q12 48 60 117l-45 31q-86 -95 -129 -184q34 -46 64 -46zM111 431l47 17q-2 9 -2 24q0 21 5 41q12 48 60 117l-45 31q-86 -95 -129 -184q34 -46 64 -46z" /> +<glyph unicode="”" horiz-adv-x="446" d="M157 646l-47 -17q2 -9 2 -24q0 -21 -5 -41q-12 -48 -60 -117l45 -31q86 95 129 184q-34 46 -64 46zM334 646l-47 -17q2 -9 2 -24q0 -21 -5 -41q-12 -48 -60 -117l45 -31q86 95 129 184q-34 46 -64 46z" /> +<glyph unicode="„" horiz-adv-x="434" d="M139 90l-47 -17q2 -9 2 -24q0 -21 -5 -41q-12 -48 -60 -117l45 -31q86 95 129 184q-34 46 -64 46zM316 90l-47 -17q2 -9 2 -24q0 -21 -5 -41q-12 -48 -60 -117l45 -31q86 95 129 184q-34 46 -64 46z" /> +<glyph unicode="•" horiz-adv-x="424" d="M211 487q71 0 122 -50.5t51 -121.5t-51 -121.5t-122 -50.5t-121.5 50.5t-50.5 121.5t50.5 121.5t121.5 50.5z" /> +<glyph unicode="…" horiz-adv-x="604" d="M192 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM364 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40zM534 46q0 -24 -19.5 -42.5t-44.5 -18.5q-24 0 -41 16 t-17 40q0 25 19.5 43t44.5 18q24 0 41 -16t17 -40z" /> +<glyph unicode="‹" horiz-adv-x="300" d="M267 100l-47 -38l-187 188l185 188l47 -38l-149 -150z" /> +<glyph unicode="›" horiz-adv-x="300" d="M33 100l47 -38l187 188l-185 188l-47 -38l149 -150z" /> +<glyph unicode="⁄" horiz-adv-x="339" d="M420 745l-430 -876h-63l430 876h63z" /> +<glyph unicode="™" horiz-adv-x="802" d="M310 650h-97v-353h-49v353h-97v44h243v-44zM724 694v-397h-50v270l3 54l-114 -324h-44l-114 325q3 -33 3 -54v-271h-51v397h67l117 -335l116 335h67z" /> +<glyph unicode="" horiz-adv-x="515" d="M0 515h515v-515h-515v515z" /> +<glyph unicode="fi" horiz-adv-x="661" d="M490 0v514h76v-514h-76zM173 450v-149h142v-66h-142v-235h-78v514h257v-64h-179z" /> +<glyph unicode="fl" horiz-adv-x="762" d="M488 0v514h75v-450h179v-64h-254zM173 450v-149h141v-66h-141v-235h-78v514h256v-64h-178z" /> +<glyph unicode="ffi" horiz-adv-x="1049" d="M878 0v514h82v-514h-82zM563 450v-149h143v-66h-143v-235h-79v514h260v-64h-181zM169 450v-149h143v-66h-143v-235h-79v514h260v-64h-181z" /> +<glyph unicode="ffl" horiz-adv-x="1155" d="M878 0v514h76v-450h181v-64h-257zM563 450v-149h143v-66h-143v-235h-79v514h260v-64h-181zM169 450v-149h143v-66h-143v-235h-79v514h260v-64h-181z" /> +</font> +</defs></svg>
\ No newline at end of file diff --git a/app/assets/fonts/delicious-smallcaps-webfont.ttf b/app/assets/fonts/delicious-smallcaps-webfont.ttf Binary files differnew file mode 100755 index 000000000..152f15f13 --- /dev/null +++ b/app/assets/fonts/delicious-smallcaps-webfont.ttf diff --git a/app/assets/fonts/delicious-smallcaps-webfont.woff b/app/assets/fonts/delicious-smallcaps-webfont.woff Binary files differnew file mode 100755 index 000000000..0d4d1f7d0 --- /dev/null +++ b/app/assets/fonts/delicious-smallcaps-webfont.woff diff --git a/app/assets/images/README.txt b/app/assets/images/README.txt new file mode 100644 index 000000000..0bf9f36d6 --- /dev/null +++ b/app/assets/images/README.txt @@ -0,0 +1,6 @@ +The mime type icons are copied from the following directory on an Ubuntu +desktop machine. Look there for new ones... + +/usr/share/icons/Rodent/48x48/mimetypes + +Install package xfce4-icon-theme if the directory isn't there. diff --git a/app/assets/images/arrow-left.png b/app/assets/images/arrow-left.png Binary files differnew file mode 100644 index 000000000..74d2fa253 --- /dev/null +++ b/app/assets/images/arrow-left.png diff --git a/app/assets/images/arrow-right.png b/app/assets/images/arrow-right.png Binary files differnew file mode 100644 index 000000000..34dd75ef8 --- /dev/null +++ b/app/assets/images/arrow-right.png diff --git a/app/assets/images/bighand.png b/app/assets/images/bighand.png Binary files differnew file mode 100644 index 000000000..92a4a6105 --- /dev/null +++ b/app/assets/images/bighand.png diff --git a/app/assets/images/button-gradient-large.png b/app/assets/images/button-gradient-large.png Binary files differnew file mode 100644 index 000000000..93ebc6cbc --- /dev/null +++ b/app/assets/images/button-gradient-large.png diff --git a/app/assets/images/button-gradient.png b/app/assets/images/button-gradient.png Binary files differnew file mode 100644 index 000000000..8b29e89ba --- /dev/null +++ b/app/assets/images/button-gradient.png diff --git a/app/assets/images/button-preview.png b/app/assets/images/button-preview.png Binary files differnew file mode 100644 index 000000000..79b4ccd04 --- /dev/null +++ b/app/assets/images/button-preview.png diff --git a/app/assets/images/button-search.png b/app/assets/images/button-search.png Binary files differnew file mode 100644 index 000000000..23945f4f0 --- /dev/null +++ b/app/assets/images/button-search.png diff --git a/app/assets/images/calendar.png b/app/assets/images/calendar.png Binary files differnew file mode 100644 index 000000000..44981a41c --- /dev/null +++ b/app/assets/images/calendar.png diff --git a/app/assets/images/defaultprofilepic.png b/app/assets/images/defaultprofilepic.png Binary files differnew file mode 100644 index 000000000..affdaad3d --- /dev/null +++ b/app/assets/images/defaultprofilepic.png diff --git a/app/assets/images/email-16.png b/app/assets/images/email-16.png Binary files differnew file mode 100644 index 000000000..8692748e2 --- /dev/null +++ b/app/assets/images/email-16.png diff --git a/app/assets/images/fancybox-x.png b/app/assets/images/fancybox-x.png Binary files differnew file mode 100755 index 000000000..c2130f869 --- /dev/null +++ b/app/assets/images/fancybox-x.png diff --git a/app/assets/images/fancybox-y.png b/app/assets/images/fancybox-y.png Binary files differnew file mode 100755 index 000000000..7ef399b99 --- /dev/null +++ b/app/assets/images/fancybox-y.png diff --git a/app/assets/images/fancybox.png b/app/assets/images/fancybox.png Binary files differnew file mode 100755 index 000000000..65e14f68f --- /dev/null +++ b/app/assets/images/fancybox.png diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico Binary files differnew file mode 100644 index 000000000..26127495c --- /dev/null +++ b/app/assets/images/favicon.ico diff --git a/app/assets/images/feed-14.png b/app/assets/images/feed-14.png Binary files differnew file mode 100644 index 000000000..b3c949d22 --- /dev/null +++ b/app/assets/images/feed-14.png diff --git a/app/assets/images/feed-16.png b/app/assets/images/feed-16.png Binary files differnew file mode 100644 index 000000000..1679ab05b --- /dev/null +++ b/app/assets/images/feed-16.png diff --git a/app/assets/images/feed-28.png b/app/assets/images/feed-28.png Binary files differnew file mode 100644 index 000000000..d64c669c7 --- /dev/null +++ b/app/assets/images/feed-28.png diff --git a/app/assets/images/flying-computer.png b/app/assets/images/flying-computer.png Binary files differnew file mode 100644 index 000000000..b1e1d59bb --- /dev/null +++ b/app/assets/images/flying-computer.png diff --git a/app/assets/images/glyphicons-halflings-white.png b/app/assets/images/glyphicons-halflings-white.png Binary files differnew file mode 100644 index 000000000..3bf6484a2 --- /dev/null +++ b/app/assets/images/glyphicons-halflings-white.png diff --git a/app/assets/images/glyphicons-halflings.png b/app/assets/images/glyphicons-halflings.png Binary files differnew file mode 100644 index 000000000..79bc568c2 --- /dev/null +++ b/app/assets/images/glyphicons-halflings.png diff --git a/app/assets/images/helpmeinvestigate.png b/app/assets/images/helpmeinvestigate.png Binary files differnew file mode 100644 index 000000000..e9aaf5aec --- /dev/null +++ b/app/assets/images/helpmeinvestigate.png diff --git a/app/assets/images/home-grad.png b/app/assets/images/home-grad.png Binary files differnew file mode 100644 index 000000000..ff9887a11 --- /dev/null +++ b/app/assets/images/home-grad.png diff --git a/app/assets/images/icon-foi.png b/app/assets/images/icon-foi.png Binary files differnew file mode 100644 index 000000000..138bf3b5c --- /dev/null +++ b/app/assets/images/icon-foi.png diff --git a/app/assets/images/icon-person.png b/app/assets/images/icon-person.png Binary files differnew file mode 100644 index 000000000..dfb35a849 --- /dev/null +++ b/app/assets/images/icon-person.png diff --git a/app/assets/images/icon-publicbody.png b/app/assets/images/icon-publicbody.png Binary files differnew file mode 100644 index 000000000..0f3848ccf --- /dev/null +++ b/app/assets/images/icon-publicbody.png diff --git a/app/assets/images/icon_application_octet-stream_large.png b/app/assets/images/icon_application_octet-stream_large.png Binary files differnew file mode 100644 index 000000000..a239862e1 --- /dev/null +++ b/app/assets/images/icon_application_octet-stream_large.png diff --git a/app/assets/images/icon_application_pdf_large.png b/app/assets/images/icon_application_pdf_large.png Binary files differnew file mode 100644 index 000000000..9a38ca33c --- /dev/null +++ b/app/assets/images/icon_application_pdf_large.png diff --git a/app/assets/images/icon_application_rtf_large.png b/app/assets/images/icon_application_rtf_large.png Binary files differnew file mode 100644 index 000000000..2ad990608 --- /dev/null +++ b/app/assets/images/icon_application_rtf_large.png diff --git a/app/assets/images/icon_application_vnd.ms-excel_large.png b/app/assets/images/icon_application_vnd.ms-excel_large.png Binary files differnew file mode 100644 index 000000000..3f346f5ef --- /dev/null +++ b/app/assets/images/icon_application_vnd.ms-excel_large.png diff --git a/app/assets/images/icon_application_vnd.ms-powerpoint_large.png b/app/assets/images/icon_application_vnd.ms-powerpoint_large.png Binary files differnew file mode 100644 index 000000000..82c225059 --- /dev/null +++ b/app/assets/images/icon_application_vnd.ms-powerpoint_large.png diff --git a/app/assets/images/icon_application_vnd.ms-word_large.png b/app/assets/images/icon_application_vnd.ms-word_large.png Binary files differnew file mode 100644 index 000000000..91a696ab5 --- /dev/null +++ b/app/assets/images/icon_application_vnd.ms-word_large.png diff --git a/app/assets/images/icon_application_vnd.openxmlformats-officedocument.presentationml.presentation_large.png b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.presentationml.presentation_large.png new file mode 120000 index 000000000..4e0dcebe7 --- /dev/null +++ b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.presentationml.presentation_large.png @@ -0,0 +1 @@ +icon_application_vnd.ms-powerpoint_large.png
\ No newline at end of file diff --git a/app/assets/images/icon_application_vnd.openxmlformats-officedocument.spreadsheetml.sheet_large.png b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.spreadsheetml.sheet_large.png new file mode 120000 index 000000000..c664a30f9 --- /dev/null +++ b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.spreadsheetml.sheet_large.png @@ -0,0 +1 @@ +icon_application_vnd.ms-excel_large.png
\ No newline at end of file diff --git a/app/assets/images/icon_application_vnd.openxmlformats-officedocument.wordprocessingml.document_large.png b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.wordprocessingml.document_large.png new file mode 120000 index 000000000..950105e3b --- /dev/null +++ b/app/assets/images/icon_application_vnd.openxmlformats-officedocument.wordprocessingml.document_large.png @@ -0,0 +1 @@ +icon_application_vnd.ms-word_large.png
\ No newline at end of file diff --git a/app/assets/images/icon_application_zip_large.png b/app/assets/images/icon_application_zip_large.png Binary files differnew file mode 100644 index 000000000..0a14e978e --- /dev/null +++ b/app/assets/images/icon_application_zip_large.png diff --git a/app/assets/images/icon_image_bmp_large.png b/app/assets/images/icon_image_bmp_large.png Binary files differnew file mode 100644 index 000000000..f6e8dbaed --- /dev/null +++ b/app/assets/images/icon_image_bmp_large.png diff --git a/app/assets/images/icon_image_gif_large.png b/app/assets/images/icon_image_gif_large.png Binary files differnew file mode 100644 index 000000000..424d1e0fd --- /dev/null +++ b/app/assets/images/icon_image_gif_large.png diff --git a/app/assets/images/icon_image_jpeg_large.png b/app/assets/images/icon_image_jpeg_large.png Binary files differnew file mode 100644 index 000000000..fd50a889d --- /dev/null +++ b/app/assets/images/icon_image_jpeg_large.png diff --git a/app/assets/images/icon_image_png_large.png b/app/assets/images/icon_image_png_large.png Binary files differnew file mode 100644 index 000000000..f16edb08e --- /dev/null +++ b/app/assets/images/icon_image_png_large.png diff --git a/app/assets/images/icon_image_tiff_large.png b/app/assets/images/icon_image_tiff_large.png Binary files differnew file mode 100644 index 000000000..356f63478 --- /dev/null +++ b/app/assets/images/icon_image_tiff_large.png diff --git a/app/assets/images/icon_message_delivery-status_large.png b/app/assets/images/icon_message_delivery-status_large.png Binary files differnew file mode 100644 index 000000000..a239862e1 --- /dev/null +++ b/app/assets/images/icon_message_delivery-status_large.png diff --git a/app/assets/images/icon_text_html_large.png b/app/assets/images/icon_text_html_large.png Binary files differnew file mode 100644 index 000000000..914502cf4 --- /dev/null +++ b/app/assets/images/icon_text_html_large.png diff --git a/app/assets/images/icon_text_plain_large.png b/app/assets/images/icon_text_plain_large.png Binary files differnew file mode 100644 index 000000000..f74a997ba --- /dev/null +++ b/app/assets/images/icon_text_plain_large.png diff --git a/app/assets/images/icon_text_x-vcard_large.png b/app/assets/images/icon_text_x-vcard_large.png Binary files differnew file mode 100644 index 000000000..cc44d3edc --- /dev/null +++ b/app/assets/images/icon_text_x-vcard_large.png diff --git a/app/assets/images/icon_unknown.png b/app/assets/images/icon_unknown.png Binary files differnew file mode 100644 index 000000000..992c646c0 --- /dev/null +++ b/app/assets/images/icon_unknown.png diff --git a/app/assets/images/link-icon.png b/app/assets/images/link-icon.png Binary files differnew file mode 100644 index 000000000..7d9237fcd --- /dev/null +++ b/app/assets/images/link-icon.png diff --git a/app/assets/images/littlehand.png b/app/assets/images/littlehand.png Binary files differnew file mode 100644 index 000000000..f7ec82045 --- /dev/null +++ b/app/assets/images/littlehand.png diff --git a/app/assets/images/logo.png b/app/assets/images/logo.png Binary files differnew file mode 100644 index 000000000..1e44ced7d --- /dev/null +++ b/app/assets/images/logo.png diff --git a/app/assets/images/navimg/alaveteli-logo-header.png b/app/assets/images/navimg/alaveteli-logo-header.png Binary files differnew file mode 100644 index 000000000..49b529718 --- /dev/null +++ b/app/assets/images/navimg/alaveteli-logo-header.png diff --git a/app/assets/images/navimg/alaveteli-logo.png b/app/assets/images/navimg/alaveteli-logo.png Binary files differnew file mode 100644 index 000000000..ce9abdd3d --- /dev/null +++ b/app/assets/images/navimg/alaveteli-logo.png diff --git a/app/assets/images/navimg/auth-icon.png b/app/assets/images/navimg/auth-icon.png Binary files differnew file mode 100644 index 000000000..6eaa229bf --- /dev/null +++ b/app/assets/images/navimg/auth-icon.png diff --git a/app/assets/images/navimg/bnnr-ifyoudontask-24bit.png b/app/assets/images/navimg/bnnr-ifyoudontask-24bit.png Binary files differnew file mode 100644 index 000000000..5c87bdab4 --- /dev/null +++ b/app/assets/images/navimg/bnnr-ifyoudontask-24bit.png diff --git a/app/assets/images/navimg/bnnr-ifyoudontask.png b/app/assets/images/navimg/bnnr-ifyoudontask.png Binary files differnew file mode 100644 index 000000000..f577778e1 --- /dev/null +++ b/app/assets/images/navimg/bnnr-ifyoudontask.png diff --git a/app/assets/images/navimg/bnnr-temp-100px.jpg b/app/assets/images/navimg/bnnr-temp-100px.jpg Binary files differnew file mode 100644 index 000000000..baf03585e --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp-100px.jpg diff --git a/app/assets/images/navimg/bnnr-temp-100pxa.jpg b/app/assets/images/navimg/bnnr-temp-100pxa.jpg Binary files differnew file mode 100644 index 000000000..f95a4d9f5 --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp-100pxa.jpg diff --git a/app/assets/images/navimg/bnnr-temp-100pxb.jpg b/app/assets/images/navimg/bnnr-temp-100pxb.jpg Binary files differnew file mode 100644 index 000000000..f09f5c838 --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp-100pxb.jpg diff --git a/app/assets/images/navimg/bnnr-temp-100pxc.jpg b/app/assets/images/navimg/bnnr-temp-100pxc.jpg Binary files differnew file mode 100755 index 000000000..e83ec90ed --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp-100pxc.jpg diff --git a/app/assets/images/navimg/bnnr-temp-100pxd.jpg b/app/assets/images/navimg/bnnr-temp-100pxd.jpg Binary files differnew file mode 100755 index 000000000..105b97a19 --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp-100pxd.jpg diff --git a/app/assets/images/navimg/bnnr-temp.jpg b/app/assets/images/navimg/bnnr-temp.jpg Binary files differnew file mode 100644 index 000000000..abcc0bcfc --- /dev/null +++ b/app/assets/images/navimg/bnnr-temp.jpg diff --git a/app/assets/images/navimg/infobug20px.png b/app/assets/images/navimg/infobug20px.png Binary files differnew file mode 100644 index 000000000..f9cc70694 --- /dev/null +++ b/app/assets/images/navimg/infobug20px.png diff --git a/app/assets/images/navimg/logo-trans-small.png b/app/assets/images/navimg/logo-trans-small.png Binary files differnew file mode 100644 index 000000000..fde1d1e51 --- /dev/null +++ b/app/assets/images/navimg/logo-trans-small.png diff --git a/app/assets/images/navimg/logo-trans.png b/app/assets/images/navimg/logo-trans.png Binary files differnew file mode 100644 index 000000000..320a52efd --- /dev/null +++ b/app/assets/images/navimg/logo-trans.png diff --git a/app/assets/images/navimg/mysoc-logo-header.png b/app/assets/images/navimg/mysoc-logo-header.png Binary files differnew file mode 100755 index 000000000..ad813926a --- /dev/null +++ b/app/assets/images/navimg/mysoc-logo-header.png diff --git a/app/assets/images/navimg/mysoc-logo-small.png b/app/assets/images/navimg/mysoc-logo-small.png Binary files differnew file mode 100644 index 000000000..1d644924c --- /dev/null +++ b/app/assets/images/navimg/mysoc-logo-small.png diff --git a/app/assets/images/navimg/quote-open-small.png b/app/assets/images/navimg/quote-open-small.png Binary files differnew file mode 100755 index 000000000..46edaa850 --- /dev/null +++ b/app/assets/images/navimg/quote-open-small.png diff --git a/app/assets/images/navimg/quote-open.png b/app/assets/images/navimg/quote-open.png Binary files differnew file mode 100644 index 000000000..7e8858265 --- /dev/null +++ b/app/assets/images/navimg/quote-open.png diff --git a/app/assets/images/navimg/request-icon.png b/app/assets/images/navimg/request-icon.png Binary files differnew file mode 100644 index 000000000..9f2854d0e --- /dev/null +++ b/app/assets/images/navimg/request-icon.png diff --git a/app/assets/images/navimg/status-icons-attn.png b/app/assets/images/navimg/status-icons-attn.png Binary files differnew file mode 100644 index 000000000..6a543c5da --- /dev/null +++ b/app/assets/images/navimg/status-icons-attn.png diff --git a/app/assets/images/navimg/status-icons-error-message.png b/app/assets/images/navimg/status-icons-error-message.png Binary files differnew file mode 100644 index 000000000..03ddd60b7 --- /dev/null +++ b/app/assets/images/navimg/status-icons-error-message.png diff --git a/app/assets/images/navimg/status-icons-fail.png b/app/assets/images/navimg/status-icons-fail.png Binary files differnew file mode 100644 index 000000000..57529237c --- /dev/null +++ b/app/assets/images/navimg/status-icons-fail.png diff --git a/app/assets/images/navimg/status-icons-internal-review.png b/app/assets/images/navimg/status-icons-internal-review.png Binary files differnew file mode 100644 index 000000000..44fe2fdb3 --- /dev/null +++ b/app/assets/images/navimg/status-icons-internal-review.png diff --git a/app/assets/images/navimg/status-icons-not-held.png b/app/assets/images/navimg/status-icons-not-held.png Binary files differnew file mode 100644 index 000000000..1225a3db3 --- /dev/null +++ b/app/assets/images/navimg/status-icons-not-held.png diff --git a/app/assets/images/navimg/status-icons-post.png b/app/assets/images/navimg/status-icons-post.png Binary files differnew file mode 100755 index 000000000..56b9b9fa9 --- /dev/null +++ b/app/assets/images/navimg/status-icons-post.png diff --git a/app/assets/images/navimg/status-icons-succeed.png b/app/assets/images/navimg/status-icons-succeed.png Binary files differnew file mode 100644 index 000000000..8f3bba985 --- /dev/null +++ b/app/assets/images/navimg/status-icons-succeed.png diff --git a/app/assets/images/navimg/status-icons-user-withdrawn.png b/app/assets/images/navimg/status-icons-user-withdrawn.png Binary files differnew file mode 100644 index 000000000..fa4e278ff --- /dev/null +++ b/app/assets/images/navimg/status-icons-user-withdrawn.png diff --git a/app/assets/images/navimg/status-icons-wait.png b/app/assets/images/navimg/status-icons-wait.png Binary files differnew file mode 100644 index 000000000..a264f06f5 --- /dev/null +++ b/app/assets/images/navimg/status-icons-wait.png diff --git a/app/assets/images/navimg/user-icon.png b/app/assets/images/navimg/user-icon.png Binary files differnew file mode 100644 index 000000000..dfaf5525d --- /dev/null +++ b/app/assets/images/navimg/user-icon.png diff --git a/app/assets/images/petitions.png b/app/assets/images/petitions.png Binary files differnew file mode 100644 index 000000000..204e12b77 --- /dev/null +++ b/app/assets/images/petitions.png diff --git a/app/assets/images/pledgebank.png b/app/assets/images/pledgebank.png Binary files differnew file mode 100644 index 000000000..0f0a8f235 --- /dev/null +++ b/app/assets/images/pledgebank.png diff --git a/app/assets/images/quote-marks.png b/app/assets/images/quote-marks.png Binary files differnew file mode 100644 index 000000000..e2bdfb06d --- /dev/null +++ b/app/assets/images/quote-marks.png diff --git a/app/assets/images/rails.png b/app/assets/images/rails.png Binary files differnew file mode 100644 index 000000000..b8441f182 --- /dev/null +++ b/app/assets/images/rails.png diff --git a/app/assets/images/rss-16.png b/app/assets/images/rss-16.png Binary files differnew file mode 100644 index 000000000..d61986a56 --- /dev/null +++ b/app/assets/images/rss-16.png diff --git a/app/assets/images/rss-blue.png b/app/assets/images/rss-blue.png Binary files differnew file mode 100644 index 000000000..abc1b1859 --- /dev/null +++ b/app/assets/images/rss-blue.png diff --git a/app/assets/images/rss-orange.png b/app/assets/images/rss-orange.png Binary files differnew file mode 100644 index 000000000..d0e6a949e --- /dev/null +++ b/app/assets/images/rss-orange.png diff --git a/app/assets/images/rss.png b/app/assets/images/rss.png Binary files differnew file mode 100644 index 000000000..a88ac93f5 --- /dev/null +++ b/app/assets/images/rss.png diff --git a/app/assets/images/search-button.png b/app/assets/images/search-button.png Binary files differnew file mode 100644 index 000000000..f5d41d4f1 --- /dev/null +++ b/app/assets/images/search-button.png diff --git a/app/assets/images/small-green-cross.png b/app/assets/images/small-green-cross.png Binary files differnew file mode 100644 index 000000000..5868f5775 --- /dev/null +++ b/app/assets/images/small-green-cross.png diff --git a/app/assets/images/small-white-cross.png b/app/assets/images/small-white-cross.png Binary files differnew file mode 100644 index 000000000..3f78064d2 --- /dev/null +++ b/app/assets/images/small-white-cross.png diff --git a/app/assets/images/start-button.png b/app/assets/images/start-button.png Binary files differnew file mode 100644 index 000000000..585931c35 --- /dev/null +++ b/app/assets/images/start-button.png diff --git a/app/assets/images/status-complete.png b/app/assets/images/status-complete.png Binary files differnew file mode 100644 index 000000000..2ff49770a --- /dev/null +++ b/app/assets/images/status-complete.png diff --git a/app/assets/images/status-denied.png b/app/assets/images/status-denied.png Binary files differnew file mode 100644 index 000000000..1f768af5e --- /dev/null +++ b/app/assets/images/status-denied.png diff --git a/app/assets/images/status-error.png b/app/assets/images/status-error.png Binary files differnew file mode 100644 index 000000000..5865dd1e1 --- /dev/null +++ b/app/assets/images/status-error.png diff --git a/app/assets/images/status-gone-postal.png b/app/assets/images/status-gone-postal.png Binary files differnew file mode 100644 index 000000000..00df771ac --- /dev/null +++ b/app/assets/images/status-gone-postal.png diff --git a/app/assets/images/status-internal-review.png b/app/assets/images/status-internal-review.png Binary files differnew file mode 100644 index 000000000..639f72797 --- /dev/null +++ b/app/assets/images/status-internal-review.png diff --git a/app/assets/images/status-not-held.png b/app/assets/images/status-not-held.png Binary files differnew file mode 100644 index 000000000..9d20ac2e7 --- /dev/null +++ b/app/assets/images/status-not-held.png diff --git a/app/assets/images/status-overdue.png b/app/assets/images/status-overdue.png Binary files differnew file mode 100644 index 000000000..637fed8d9 --- /dev/null +++ b/app/assets/images/status-overdue.png diff --git a/app/assets/images/status-pending.png b/app/assets/images/status-pending.png Binary files differnew file mode 100644 index 000000000..dcf4009f3 --- /dev/null +++ b/app/assets/images/status-pending.png diff --git a/app/assets/images/status-withdrawn.png b/app/assets/images/status-withdrawn.png Binary files differnew file mode 100644 index 000000000..d56aa213f --- /dev/null +++ b/app/assets/images/status-withdrawn.png diff --git a/app/assets/images/stripes-70-light.png b/app/assets/images/stripes-70-light.png Binary files differnew file mode 100644 index 000000000..7a0ee575d --- /dev/null +++ b/app/assets/images/stripes-70-light.png diff --git a/app/assets/images/stripes-70-light2.png b/app/assets/images/stripes-70-light2.png Binary files differnew file mode 100644 index 000000000..443442a48 --- /dev/null +++ b/app/assets/images/stripes-70-light2.png diff --git a/app/assets/images/stripes-70.png b/app/assets/images/stripes-70.png Binary files differnew file mode 100644 index 000000000..fda1c339d --- /dev/null +++ b/app/assets/images/stripes-70.png diff --git a/app/assets/images/stripes.png b/app/assets/images/stripes.png Binary files differnew file mode 100644 index 000000000..fda1c339d --- /dev/null +++ b/app/assets/images/stripes.png diff --git a/app/assets/images/twitter-16.png b/app/assets/images/twitter-16.png Binary files differnew file mode 100644 index 000000000..e848b8f2e --- /dev/null +++ b/app/assets/images/twitter-16.png diff --git a/app/assets/images/twitter.png b/app/assets/images/twitter.png Binary files differnew file mode 100644 index 000000000..6589824af --- /dev/null +++ b/app/assets/images/twitter.png diff --git a/app/assets/images/ui-icons-theme.png b/app/assets/images/ui-icons-theme.png Binary files differnew file mode 100644 index 000000000..f619648da --- /dev/null +++ b/app/assets/images/ui-icons-theme.png diff --git a/app/assets/images/wordpress.png b/app/assets/images/wordpress.png Binary files differnew file mode 100644 index 000000000..a0e254373 --- /dev/null +++ b/app/assets/images/wordpress.png diff --git a/app/assets/images/writetothem.png b/app/assets/images/writetothem.png Binary files differnew file mode 100644 index 000000000..f5648df65 --- /dev/null +++ b/app/assets/images/writetothem.png diff --git a/app/assets/javascripts/admin.js b/app/assets/javascripts/admin.js new file mode 100644 index 000000000..0b5d56525 --- /dev/null +++ b/app/assets/javascripts/admin.js @@ -0,0 +1,7 @@ +// ... +//= require jquery +//= require jquery.ui.tabs +//= require admin/bootstrap-collapse +//= require admin/bootstrap-tab +//= require admin/admin +//= require jquery_ujs diff --git a/app/assets/javascripts/admin/admin.js.coffee b/app/assets/javascripts/admin/admin.js.coffee new file mode 100644 index 000000000..3d39369a4 --- /dev/null +++ b/app/assets/javascripts/admin/admin.js.coffee @@ -0,0 +1,24 @@ +jQuery -> + $('.locales a:first').tab('show') + $('.accordion-body').on('hidden', -> + $(@).prev().find('i').first().removeClass().addClass('icon-chevron-right') + ) + $('.accordion-body').on('shown', -> + $(@).prev().find('i').first().removeClass().addClass('icon-chevron-down')) + $('.toggle-hidden').live('click', -> + $(@).parents('td').find('div:hidden').show() + false) + $('#request_hidden_user_explanation_reasons input').live('click', -> + $('#request_hidden_user_subject, #request_hidden_user_explanation, #request_hide_button').show() + info_request_id = $('#hide_request_form').attr('data-info-request-id') + reason = $(this).val() + $('#request_hidden_user_explanation_field').attr("value", "[loading default text...]") + $.ajax "/hidden_user_explanation?reason=" + reason + "&info_request_id=" + info_request_id, + type: "GET" + dataType: "text" + error: (data, textStatus, jqXHR) -> + $('#request_hidden_user_explanation_field').attr("value", "Error: #{textStatus}") + success: (data, textStatus, jqXHR) -> + $('#request_hidden_user_explanation_field').attr("value", data) + ) + diff --git a/app/assets/javascripts/admin/bootstrap-collapse.js b/app/assets/javascripts/admin/bootstrap-collapse.js new file mode 100644 index 000000000..9a364468b --- /dev/null +++ b/app/assets/javascripts/admin/bootstrap-collapse.js @@ -0,0 +1,138 @@ +/* ============================================================= + * bootstrap-collapse.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#collapse + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + var Collapse = function ( element, options ) { + this.$element = $(element) + this.options = $.extend({}, $.fn.collapse.defaults, options) + + if (this.options["parent"]) { + this.$parent = $(this.options["parent"]) + } + + this.options.toggle && this.toggle() + } + + Collapse.prototype = { + + constructor: Collapse + + , dimension: function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + , show: function () { + var dimension = this.dimension() + , scroll = $.camelCase(['scroll', dimension].join('-')) + , actives = this.$parent && this.$parent.find('.in') + , hasData + + if (actives && actives.length) { + hasData = actives.data('collapse') + actives.collapse('hide') + hasData || actives.data('collapse', null) + } + + this.$element[dimension](0) + this.transition('addClass', 'show', 'shown') + this.$element[dimension](this.$element[0][scroll]) + + } + + , hide: function () { + var dimension = this.dimension() + this.reset(this.$element[dimension]()) + this.transition('removeClass', 'hide', 'hidden') + this.$element[dimension](0) + } + + , reset: function ( size ) { + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + [dimension](size || 'auto') + [0].offsetWidth + + this.$element[size ? 'addClass' : 'removeClass']('collapse') + + return this + } + + , transition: function ( method, startEvent, completeEvent ) { + var that = this + , complete = function () { + if (startEvent == 'show') that.reset() + that.$element.trigger(completeEvent) + } + + this.$element + .trigger(startEvent) + [method]('in') + + $.support.transition && this.$element.hasClass('collapse') ? + this.$element.one($.support.transition.end, complete) : + complete() + } + + , toggle: function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + } + + /* COLLAPSIBLE PLUGIN DEFINITION + * ============================== */ + + $.fn.collapse = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('collapse') + , options = typeof option == 'object' && option + if (!data) $this.data('collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.defaults = { + toggle: true + } + + $.fn.collapse.Constructor = Collapse + + + /* COLLAPSIBLE DATA-API + * ==================== */ + + $(function () { + $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $(target).collapse(option) + }) + }) + +}( window.jQuery );
\ No newline at end of file diff --git a/app/assets/javascripts/admin/bootstrap-tab.js b/app/assets/javascripts/admin/bootstrap-tab.js new file mode 100644 index 000000000..26c9ece75 --- /dev/null +++ b/app/assets/javascripts/admin/bootstrap-tab.js @@ -0,0 +1,130 @@ +/* ======================================================== + * bootstrap-tab.js v2.0.1 + * http://twitter.github.com/bootstrap/javascript.html#tabs + * ======================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================== */ + + +!function( $ ){ + + "use strict" + + /* TAB CLASS DEFINITION + * ==================== */ + + var Tab = function ( element ) { + this.element = $(element) + } + + Tab.prototype = { + + constructor: Tab + + , show: function () { + var $this = this.element + , $ul = $this.closest('ul:not(.dropdown-menu)') + , selector = $this.attr('data-target') + , previous + , $target + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + if ( $this.parent('li').hasClass('active') ) return + + previous = $ul.find('.active a').last()[0] + + $this.trigger({ + type: 'show' + , relatedTarget: previous + }) + + $target = $(selector) + + this.activate($this.parent('li'), $ul) + this.activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown' + , relatedTarget: previous + }) + }) + } + + , activate: function ( element, container, callback) { + var $active = container.find('> .active') + , transition = callback + && $.support.transition + && $active.hasClass('fade') + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if ( element.parent('.dropdown-menu') ) { + element.closest('li.dropdown').addClass('active') + } + + callback && callback() + } + + transition ? + $active.one($.support.transition.end, next) : + next() + + $active.removeClass('in') + } + } + + + /* TAB PLUGIN DEFINITION + * ===================== */ + + $.fn.tab = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('tab') + if (!data) $this.data('tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.tab.Constructor = Tab + + + /* TAB DATA-API + * ============ */ + + $(function () { + $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + e.preventDefault() + $(this).tab('show') + }) + }) + +}( window.jQuery );
\ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 000000000..d8aed6346 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,6 @@ +// ... +//= require jquery +//= require jquery.ui.datepicker +//= require jquery.cookie +//= require general +//= require ba-throttle-debounce diff --git a/app/assets/javascripts/ba-throttle-debounce.js b/app/assets/javascripts/ba-throttle-debounce.js new file mode 100644 index 000000000..07205508e --- /dev/null +++ b/app/assets/javascripts/ba-throttle-debounce.js @@ -0,0 +1,9 @@ +/* + * jQuery throttle / debounce - v1.1 - 3/7/2010 + * http://benalman.com/projects/jquery-throttle-debounce-plugin/ + * + * Copyright (c) 2010 "Cowboy" Ben Alman + * Dual licensed under the MIT and GPL licenses. + * http://benalman.com/about/license/ + */ +(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
\ No newline at end of file diff --git a/app/assets/javascripts/bootstrap-collapse.js b/app/assets/javascripts/bootstrap-collapse.js new file mode 100644 index 000000000..9a364468b --- /dev/null +++ b/app/assets/javascripts/bootstrap-collapse.js @@ -0,0 +1,138 @@ +/* ============================================================= + * bootstrap-collapse.js v2.0.2 + * http://twitter.github.com/bootstrap/javascript.html#collapse + * ============================================================= + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================ */ + +!function( $ ){ + + "use strict" + + var Collapse = function ( element, options ) { + this.$element = $(element) + this.options = $.extend({}, $.fn.collapse.defaults, options) + + if (this.options["parent"]) { + this.$parent = $(this.options["parent"]) + } + + this.options.toggle && this.toggle() + } + + Collapse.prototype = { + + constructor: Collapse + + , dimension: function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + , show: function () { + var dimension = this.dimension() + , scroll = $.camelCase(['scroll', dimension].join('-')) + , actives = this.$parent && this.$parent.find('.in') + , hasData + + if (actives && actives.length) { + hasData = actives.data('collapse') + actives.collapse('hide') + hasData || actives.data('collapse', null) + } + + this.$element[dimension](0) + this.transition('addClass', 'show', 'shown') + this.$element[dimension](this.$element[0][scroll]) + + } + + , hide: function () { + var dimension = this.dimension() + this.reset(this.$element[dimension]()) + this.transition('removeClass', 'hide', 'hidden') + this.$element[dimension](0) + } + + , reset: function ( size ) { + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + [dimension](size || 'auto') + [0].offsetWidth + + this.$element[size ? 'addClass' : 'removeClass']('collapse') + + return this + } + + , transition: function ( method, startEvent, completeEvent ) { + var that = this + , complete = function () { + if (startEvent == 'show') that.reset() + that.$element.trigger(completeEvent) + } + + this.$element + .trigger(startEvent) + [method]('in') + + $.support.transition && this.$element.hasClass('collapse') ? + this.$element.one($.support.transition.end, complete) : + complete() + } + + , toggle: function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + } + + /* COLLAPSIBLE PLUGIN DEFINITION + * ============================== */ + + $.fn.collapse = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('collapse') + , options = typeof option == 'object' && option + if (!data) $this.data('collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.collapse.defaults = { + toggle: true + } + + $.fn.collapse.Constructor = Collapse + + + /* COLLAPSIBLE DATA-API + * ==================== */ + + $(function () { + $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $(target).collapse(option) + }) + }) + +}( window.jQuery );
\ No newline at end of file diff --git a/app/assets/javascripts/bootstrap-tab.js b/app/assets/javascripts/bootstrap-tab.js new file mode 100644 index 000000000..26c9ece75 --- /dev/null +++ b/app/assets/javascripts/bootstrap-tab.js @@ -0,0 +1,130 @@ +/* ======================================================== + * bootstrap-tab.js v2.0.1 + * http://twitter.github.com/bootstrap/javascript.html#tabs + * ======================================================== + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ======================================================== */ + + +!function( $ ){ + + "use strict" + + /* TAB CLASS DEFINITION + * ==================== */ + + var Tab = function ( element ) { + this.element = $(element) + } + + Tab.prototype = { + + constructor: Tab + + , show: function () { + var $this = this.element + , $ul = $this.closest('ul:not(.dropdown-menu)') + , selector = $this.attr('data-target') + , previous + , $target + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } + + if ( $this.parent('li').hasClass('active') ) return + + previous = $ul.find('.active a').last()[0] + + $this.trigger({ + type: 'show' + , relatedTarget: previous + }) + + $target = $(selector) + + this.activate($this.parent('li'), $ul) + this.activate($target, $target.parent(), function () { + $this.trigger({ + type: 'shown' + , relatedTarget: previous + }) + }) + } + + , activate: function ( element, container, callback) { + var $active = container.find('> .active') + , transition = callback + && $.support.transition + && $active.hasClass('fade') + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + + element.addClass('active') + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if ( element.parent('.dropdown-menu') ) { + element.closest('li.dropdown').addClass('active') + } + + callback && callback() + } + + transition ? + $active.one($.support.transition.end, next) : + next() + + $active.removeClass('in') + } + } + + + /* TAB PLUGIN DEFINITION + * ===================== */ + + $.fn.tab = function ( option ) { + return this.each(function () { + var $this = $(this) + , data = $this.data('tab') + if (!data) $this.data('tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + $.fn.tab.Constructor = Tab + + + /* TAB DATA-API + * ============ */ + + $(function () { + $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + e.preventDefault() + $(this).tab('show') + }) + }) + +}( window.jQuery );
\ No newline at end of file diff --git a/app/assets/javascripts/excanvas.min.js b/app/assets/javascripts/excanvas.min.js new file mode 100644 index 000000000..fcf876c74 --- /dev/null +++ b/app/assets/javascripts/excanvas.min.js @@ -0,0 +1 @@ +if(!document.createElement("canvas").getContext){(function(){var ab=Math;var n=ab.round;var l=ab.sin;var A=ab.cos;var H=ab.abs;var N=ab.sqrt;var d=10;var f=d/2;var z=+navigator.userAgent.match(/MSIE ([\d.]+)?/)[1];function y(){return this.context_||(this.context_=new D(this))}var t=Array.prototype.slice;function g(j,m,p){var i=t.call(arguments,2);return function(){return j.apply(m,i.concat(t.call(arguments)))}}function af(i){return String(i).replace(/&/g,"&").replace(/"/g,""")}function Y(m,j,i){if(!m.namespaces[j]){m.namespaces.add(j,i,"#default#VML")}}function R(j){Y(j,"g_vml_","urn:schemas-microsoft-com:vml");Y(j,"g_o_","urn:schemas-microsoft-com:office:office");if(!j.styleSheets.ex_canvas_){var i=j.createStyleSheet();i.owningElement.id="ex_canvas_";i.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}R(document);var e={init:function(i){var j=i||document;j.createElement("canvas");j.attachEvent("onreadystatechange",g(this.init_,this,j))},init_:function(p){var m=p.getElementsByTagName("canvas");for(var j=0;j<m.length;j++){this.initElement(m[j])}},initElement:function(j){if(!j.getContext){j.getContext=y;R(j.ownerDocument);j.innerHTML="";j.attachEvent("onpropertychange",x);j.attachEvent("onresize",W);var i=j.attributes;if(i.width&&i.width.specified){j.style.width=i.width.nodeValue+"px"}else{j.width=j.clientWidth}if(i.height&&i.height.specified){j.style.height=i.height.nodeValue+"px"}else{j.height=j.clientHeight}}return j}};function x(j){var i=j.srcElement;switch(j.propertyName){case"width":i.getContext().clearRect();i.style.width=i.attributes.width.nodeValue+"px";i.firstChild.style.width=i.clientWidth+"px";break;case"height":i.getContext().clearRect();i.style.height=i.attributes.height.nodeValue+"px";i.firstChild.style.height=i.clientHeight+"px";break}}function W(j){var i=j.srcElement;if(i.firstChild){i.firstChild.style.width=i.clientWidth+"px";i.firstChild.style.height=i.clientHeight+"px"}}e.init();var k=[];for(var ae=0;ae<16;ae++){for(var ad=0;ad<16;ad++){k[ae*16+ad]=ae.toString(16)+ad.toString(16)}}function B(){return[[1,0,0],[0,1,0],[0,0,1]]}function J(p,m){var j=B();for(var i=0;i<3;i++){for(var ah=0;ah<3;ah++){var Z=0;for(var ag=0;ag<3;ag++){Z+=p[i][ag]*m[ag][ah]}j[i][ah]=Z}}return j}function v(j,i){i.fillStyle=j.fillStyle;i.lineCap=j.lineCap;i.lineJoin=j.lineJoin;i.lineWidth=j.lineWidth;i.miterLimit=j.miterLimit;i.shadowBlur=j.shadowBlur;i.shadowColor=j.shadowColor;i.shadowOffsetX=j.shadowOffsetX;i.shadowOffsetY=j.shadowOffsetY;i.strokeStyle=j.strokeStyle;i.globalAlpha=j.globalAlpha;i.font=j.font;i.textAlign=j.textAlign;i.textBaseline=j.textBaseline;i.arcScaleX_=j.arcScaleX_;i.arcScaleY_=j.arcScaleY_;i.lineScale_=j.lineScale_}var b={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"};function M(j){var p=j.indexOf("(",3);var i=j.indexOf(")",p+1);var m=j.substring(p+1,i).split(",");if(m.length!=4||j.charAt(3)!="a"){m[3]=1}return m}function c(i){return parseFloat(i)/100}function r(j,m,i){return Math.min(i,Math.max(m,j))}function I(ag){var i,ai,aj,ah,ak,Z;ah=parseFloat(ag[0])/360%360;if(ah<0){ah++}ak=r(c(ag[1]),0,1);Z=r(c(ag[2]),0,1);if(ak==0){i=ai=aj=Z}else{var j=Z<0.5?Z*(1+ak):Z+ak-Z*ak;var m=2*Z-j;i=a(m,j,ah+1/3);ai=a(m,j,ah);aj=a(m,j,ah-1/3)}return"#"+k[Math.floor(i*255)]+k[Math.floor(ai*255)]+k[Math.floor(aj*255)]}function a(j,i,m){if(m<0){m++}if(m>1){m--}if(6*m<1){return j+(i-j)*6*m}else{if(2*m<1){return i}else{if(3*m<2){return j+(i-j)*(2/3-m)*6}else{return j}}}}var C={};function F(j){if(j in C){return C[j]}var ag,Z=1;j=String(j);if(j.charAt(0)=="#"){ag=j}else{if(/^rgb/.test(j)){var p=M(j);var ag="#",ah;for(var m=0;m<3;m++){if(p[m].indexOf("%")!=-1){ah=Math.floor(c(p[m])*255)}else{ah=+p[m]}ag+=k[r(ah,0,255)]}Z=+p[3]}else{if(/^hsl/.test(j)){var p=M(j);ag=I(p);Z=p[3]}else{ag=b[j]||j}}}return C[j]={color:ag,alpha:Z}}var o={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var L={};function E(i){if(L[i]){return L[i]}var p=document.createElement("div");var m=p.style;try{m.font=i}catch(j){}return L[i]={style:m.fontStyle||o.style,variant:m.fontVariant||o.variant,weight:m.fontWeight||o.weight,size:m.fontSize||o.size,family:m.fontFamily||o.family}}function u(m,j){var i={};for(var ah in m){i[ah]=m[ah]}var ag=parseFloat(j.currentStyle.fontSize),Z=parseFloat(m.size);if(typeof m.size=="number"){i.size=m.size}else{if(m.size.indexOf("px")!=-1){i.size=Z}else{if(m.size.indexOf("em")!=-1){i.size=ag*Z}else{if(m.size.indexOf("%")!=-1){i.size=(ag/100)*Z}else{if(m.size.indexOf("pt")!=-1){i.size=Z/0.75}else{i.size=ag}}}}}i.size*=0.981;return i}function ac(i){return i.style+" "+i.variant+" "+i.weight+" "+i.size+"px "+i.family}var s={butt:"flat",round:"round"};function S(i){return s[i]||"square"}function D(i){this.m_=B();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=d*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var m="width:"+i.clientWidth+"px;height:"+i.clientHeight+"px;overflow:hidden;position:absolute";var j=i.ownerDocument.createElement("div");j.style.cssText=m;i.appendChild(j);var p=j.cloneNode(false);p.style.backgroundColor="red";p.style.filter="alpha(opacity=0)";i.appendChild(p);this.element_=j;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var q=D.prototype;q.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};q.beginPath=function(){this.currentPath_=[]};q.moveTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"moveTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.lineTo=function(j,i){var m=V(this,j,i);this.currentPath_.push({type:"lineTo",x:m.x,y:m.y});this.currentX_=m.x;this.currentY_=m.y};q.bezierCurveTo=function(m,j,ak,aj,ai,ag){var i=V(this,ai,ag);var ah=V(this,m,j);var Z=V(this,ak,aj);K(this,ah,Z,i)};function K(i,Z,m,j){i.currentPath_.push({type:"bezierCurveTo",cp1x:Z.x,cp1y:Z.y,cp2x:m.x,cp2y:m.y,x:j.x,y:j.y});i.currentX_=j.x;i.currentY_=j.y}q.quadraticCurveTo=function(ai,m,j,i){var ah=V(this,ai,m);var ag=V(this,j,i);var aj={x:this.currentX_+2/3*(ah.x-this.currentX_),y:this.currentY_+2/3*(ah.y-this.currentY_)};var Z={x:aj.x+(ag.x-this.currentX_)/3,y:aj.y+(ag.y-this.currentY_)/3};K(this,aj,Z,ag)};q.arc=function(al,aj,ak,ag,j,m){ak*=d;var ap=m?"at":"wa";var am=al+A(ag)*ak-f;var ao=aj+l(ag)*ak-f;var i=al+A(j)*ak-f;var an=aj+l(j)*ak-f;if(am==i&&!m){am+=0.125}var Z=V(this,al,aj);var ai=V(this,am,ao);var ah=V(this,i,an);this.currentPath_.push({type:ap,x:Z.x,y:Z.y,radius:ak,xStart:ai.x,yStart:ai.y,xEnd:ah.x,yEnd:ah.y})};q.rect=function(m,j,i,p){this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath()};q.strokeRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.stroke();this.currentPath_=Z};q.fillRect=function(m,j,i,p){var Z=this.currentPath_;this.beginPath();this.moveTo(m,j);this.lineTo(m+i,j);this.lineTo(m+i,j+p);this.lineTo(m,j+p);this.closePath();this.fill();this.currentPath_=Z};q.createLinearGradient=function(j,p,i,m){var Z=new U("gradient");Z.x0_=j;Z.y0_=p;Z.x1_=i;Z.y1_=m;return Z};q.createRadialGradient=function(p,ag,m,j,Z,i){var ah=new U("gradientradial");ah.x0_=p;ah.y0_=ag;ah.r0_=m;ah.x1_=j;ah.y1_=Z;ah.r1_=i;return ah};q.drawImage=function(aq,m){var aj,ah,al,ay,ao,am,at,aA;var ak=aq.runtimeStyle.width;var ap=aq.runtimeStyle.height;aq.runtimeStyle.width="auto";aq.runtimeStyle.height="auto";var ai=aq.width;var aw=aq.height;aq.runtimeStyle.width=ak;aq.runtimeStyle.height=ap;if(arguments.length==3){aj=arguments[1];ah=arguments[2];ao=am=0;at=al=ai;aA=ay=aw}else{if(arguments.length==5){aj=arguments[1];ah=arguments[2];al=arguments[3];ay=arguments[4];ao=am=0;at=ai;aA=aw}else{if(arguments.length==9){ao=arguments[1];am=arguments[2];at=arguments[3];aA=arguments[4];aj=arguments[5];ah=arguments[6];al=arguments[7];ay=arguments[8]}else{throw Error("Invalid number of arguments")}}}var az=V(this,aj,ah);var p=at/2;var j=aA/2;var ax=[];var i=10;var ag=10;ax.push(" <g_vml_:group",' coordsize="',d*i,",",d*ag,'"',' coordorigin="0,0"',' style="width:',i,"px;height:",ag,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]||this.m_[1][1]!=1||this.m_[1][0]){var Z=[];Z.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",n(az.x/d),",","Dy=",n(az.y/d),"");var av=az;var au=V(this,aj+al,ah);var ar=V(this,aj,ah+ay);var an=V(this,aj+al,ah+ay);av.x=ab.max(av.x,au.x,ar.x,an.x);av.y=ab.max(av.y,au.y,ar.y,an.y);ax.push("padding:0 ",n(av.x/d),"px ",n(av.y/d),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",Z.join(""),", sizingmethod='clip');")}else{ax.push("top:",n(az.y/d),"px;left:",n(az.x/d),"px;")}ax.push(' ">','<g_vml_:image src="',aq.src,'"',' style="width:',d*al,"px;"," height:",d*ay,'px"',' cropleft="',ao/ai,'"',' croptop="',am/aw,'"',' cropright="',(ai-ao-at)/ai,'"',' cropbottom="',(aw-am-aA)/aw,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",ax.join(""))};q.stroke=function(ao){var Z=10;var ap=10;var ag=5000;var ai={x:null,y:null};var an={x:null,y:null};for(var aj=0;aj<this.currentPath_.length;aj+=ag){var am=[];var ah=false;am.push("<g_vml_:shape",' filled="',!!ao,'"',' style="position:absolute;width:',Z,"px;height:",ap,'px;"',' coordorigin="0,0"',' coordsize="',d*Z,",",d*ap,'"',' stroked="',!ao,'"',' path="');var aq=false;for(var ak=aj;ak<Math.min(aj+ag,this.currentPath_.length);ak++){if(ak%ag==0&&ak>0){am.push(" m ",n(this.currentPath_[ak-1].x),",",n(this.currentPath_[ak-1].y))}var m=this.currentPath_[ak];var al;switch(m.type){case"moveTo":al=m;am.push(" m ",n(m.x),",",n(m.y));break;case"lineTo":am.push(" l ",n(m.x),",",n(m.y));break;case"close":am.push(" x ");m=null;break;case"bezierCurveTo":am.push(" c ",n(m.cp1x),",",n(m.cp1y),",",n(m.cp2x),",",n(m.cp2y),",",n(m.x),",",n(m.y));break;case"at":case"wa":am.push(" ",m.type," ",n(m.x-this.arcScaleX_*m.radius),",",n(m.y-this.arcScaleY_*m.radius)," ",n(m.x+this.arcScaleX_*m.radius),",",n(m.y+this.arcScaleY_*m.radius)," ",n(m.xStart),",",n(m.yStart)," ",n(m.xEnd),",",n(m.yEnd));break}if(m){if(ai.x==null||m.x<ai.x){ai.x=m.x}if(an.x==null||m.x>an.x){an.x=m.x}if(ai.y==null||m.y<ai.y){ai.y=m.y}if(an.y==null||m.y>an.y){an.y=m.y}}}am.push(' ">');if(!ao){w(this,am)}else{G(this,am,ai,an)}am.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",am.join(""))}};function w(m,ag){var j=F(m.strokeStyle);var p=j.color;var Z=j.alpha*m.globalAlpha;var i=m.lineScale_*m.lineWidth;if(i<1){Z*=i}ag.push("<g_vml_:stroke",' opacity="',Z,'"',' joinstyle="',m.lineJoin,'"',' miterlimit="',m.miterLimit,'"',' endcap="',S(m.lineCap),'"',' weight="',i,'px"',' color="',p,'" />')}function G(aq,ai,aK,ar){var aj=aq.fillStyle;var aB=aq.arcScaleX_;var aA=aq.arcScaleY_;var j=ar.x-aK.x;var p=ar.y-aK.y;if(aj instanceof U){var an=0;var aF={x:0,y:0};var ax=0;var am=1;if(aj.type_=="gradient"){var al=aj.x0_/aB;var m=aj.y0_/aA;var ak=aj.x1_/aB;var aM=aj.y1_/aA;var aJ=V(aq,al,m);var aI=V(aq,ak,aM);var ag=aI.x-aJ.x;var Z=aI.y-aJ.y;an=Math.atan2(ag,Z)*180/Math.PI;if(an<0){an+=360}if(an<0.000001){an=0}}else{var aJ=V(aq,aj.x0_,aj.y0_);aF={x:(aJ.x-aK.x)/j,y:(aJ.y-aK.y)/p};j/=aB*d;p/=aA*d;var aD=ab.max(j,p);ax=2*aj.r0_/aD;am=2*aj.r1_/aD-ax}var av=aj.colors_;av.sort(function(aN,i){return aN.offset-i.offset});var ap=av.length;var au=av[0].color;var at=av[ap-1].color;var az=av[0].alpha*aq.globalAlpha;var ay=av[ap-1].alpha*aq.globalAlpha;var aE=[];for(var aH=0;aH<ap;aH++){var ao=av[aH];aE.push(ao.offset*am+ax+" "+ao.color)}ai.push('<g_vml_:fill type="',aj.type_,'"',' method="none" focus="100%"',' color="',au,'"',' color2="',at,'"',' colors="',aE.join(","),'"',' opacity="',ay,'"',' g_o_:opacity2="',az,'"',' angle="',an,'"',' focusposition="',aF.x,",",aF.y,'" />')}else{if(aj instanceof T){if(j&&p){var ah=-aK.x;var aC=-aK.y;ai.push("<g_vml_:fill",' position="',ah/j*aB*aB,",",aC/p*aA*aA,'"',' type="tile"',' src="',aj.src_,'" />')}}else{var aL=F(aq.fillStyle);var aw=aL.color;var aG=aL.alpha*aq.globalAlpha;ai.push('<g_vml_:fill color="',aw,'" opacity="',aG,'" />')}}}q.fill=function(){this.stroke(true)};q.closePath=function(){this.currentPath_.push({type:"close"})};function V(j,Z,p){var i=j.m_;return{x:d*(Z*i[0][0]+p*i[1][0]+i[2][0])-f,y:d*(Z*i[0][1]+p*i[1][1]+i[2][1])-f}}q.save=function(){var i={};v(this,i);this.aStack_.push(i);this.mStack_.push(this.m_);this.m_=J(B(),this.m_)};q.restore=function(){if(this.aStack_.length){v(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function h(i){return isFinite(i[0][0])&&isFinite(i[0][1])&&isFinite(i[1][0])&&isFinite(i[1][1])&&isFinite(i[2][0])&&isFinite(i[2][1])}function aa(j,i,p){if(!h(i)){return}j.m_=i;if(p){var Z=i[0][0]*i[1][1]-i[0][1]*i[1][0];j.lineScale_=N(H(Z))}}q.translate=function(m,j){var i=[[1,0,0],[0,1,0],[m,j,1]];aa(this,J(i,this.m_),false)};q.rotate=function(j){var p=A(j);var m=l(j);var i=[[p,m,0],[-m,p,0],[0,0,1]];aa(this,J(i,this.m_),false)};q.scale=function(m,j){this.arcScaleX_*=m;this.arcScaleY_*=j;var i=[[m,0,0],[0,j,0],[0,0,1]];aa(this,J(i,this.m_),true)};q.transform=function(Z,p,ah,ag,j,i){var m=[[Z,p,0],[ah,ag,0],[j,i,1]];aa(this,J(m,this.m_),true)};q.setTransform=function(ag,Z,ai,ah,p,j){var i=[[ag,Z,0],[ai,ah,0],[p,j,1]];aa(this,i,true)};q.drawText_=function(am,ak,aj,ap,ai){var ao=this.m_,at=1000,j=0,ar=at,ah={x:0,y:0},ag=[];var i=u(E(this.font),this.element_);var p=ac(i);var au=this.element_.currentStyle;var Z=this.textAlign.toLowerCase();switch(Z){case"left":case"center":case"right":break;case"end":Z=au.direction=="ltr"?"right":"left";break;case"start":Z=au.direction=="rtl"?"right":"left";break;default:Z="left"}switch(this.textBaseline){case"hanging":case"top":ah.y=i.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":ah.y=-i.size/2.25;break}switch(Z){case"right":j=at;ar=0.05;break;case"center":j=ar=at/2;break}var aq=V(this,ak+ah.x,aj+ah.y);ag.push('<g_vml_:line from="',-j,' 0" to="',ar,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!ai,'" stroked="',!!ai,'" style="position:absolute;width:1px;height:1px;">');if(ai){w(this,ag)}else{G(this,ag,{x:-j,y:0},{x:ar,y:i.size})}var an=ao[0][0].toFixed(3)+","+ao[1][0].toFixed(3)+","+ao[0][1].toFixed(3)+","+ao[1][1].toFixed(3)+",0,0";var al=n(aq.x/d)+","+n(aq.y/d);ag.push('<g_vml_:skew on="t" matrix="',an,'" ',' offset="',al,'" origin="',j,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',af(am),'" style="v-text-align:',Z,";font:",af(p),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",ag.join(""))};q.fillText=function(m,i,p,j){this.drawText_(m,i,p,j,false)};q.strokeText=function(m,i,p,j){this.drawText_(m,i,p,j,true)};q.measureText=function(m){if(!this.textMeasureEl_){var i='<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>';this.element_.insertAdjacentHTML("beforeEnd",i);this.textMeasureEl_=this.element_.lastChild}var j=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(j.createTextNode(m));return{width:this.textMeasureEl_.offsetWidth}};q.clip=function(){};q.arcTo=function(){};q.createPattern=function(j,i){return new T(j,i)};function U(i){this.type_=i;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}U.prototype.addColorStop=function(j,i){i=F(i);this.colors_.push({offset:j,color:i.color,alpha:i.alpha})};function T(j,i){Q(j);switch(i){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=i;break;default:O("SYNTAX_ERR")}this.src_=j.src;this.width_=j.width;this.height_=j.height}function O(i){throw new P(i)}function Q(i){if(!i||i.nodeType!=1||i.tagName!="IMG"){O("TYPE_MISMATCH_ERR")}if(i.readyState!="complete"){O("INVALID_STATE_ERR")}}function P(i){this.code=this[i];this.message=i+": DOM Exception "+this.code}var X=P.prototype=new Error;X.INDEX_SIZE_ERR=1;X.DOMSTRING_SIZE_ERR=2;X.HIERARCHY_REQUEST_ERR=3;X.WRONG_DOCUMENT_ERR=4;X.INVALID_CHARACTER_ERR=5;X.NO_DATA_ALLOWED_ERR=6;X.NO_MODIFICATION_ALLOWED_ERR=7;X.NOT_FOUND_ERR=8;X.NOT_SUPPORTED_ERR=9;X.INUSE_ATTRIBUTE_ERR=10;X.INVALID_STATE_ERR=11;X.SYNTAX_ERR=12;X.INVALID_MODIFICATION_ERR=13;X.NAMESPACE_ERR=14;X.INVALID_ACCESS_ERR=15;X.VALIDATION_ERR=16;X.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=e;CanvasRenderingContext2D=D;CanvasGradient=U;CanvasPattern=T;DOMException=P})()};
\ No newline at end of file diff --git a/app/assets/javascripts/general.js b/app/assets/javascripts/general.js new file mode 100644 index 000000000..529bbeb04 --- /dev/null +++ b/app/assets/javascripts/general.js @@ -0,0 +1,60 @@ +$(document).ready(function() { + // flash message for people coming from other countries + if(window.location.search.substring(1).search("country_name") == -1) { + if (!$.cookie('has_seen_country_message')) { + $.ajax({ + url: "/country_message", + dataType: 'html', + success: function(country_message){ + if (country_message != ''){ + $('#other-country-notice .popup-content').html(country_message); + $('body:not(.front) #other-country-notice').show() + } + } + }) + + } + } + + // popup messages + $('#other-country-notice .popup-close').click(function() { + $('#other-country-notice').hide('slow'); + $.cookie('has_seen_country_message', 1, {expires: 365, path: '/'}); + }); + $('#everypage .popup-close').click(function() { + $('#everypage').hide('slow'); + $.cookie('seen_foi2', 1, { expires: 7, path: '/' }); + return false; + }); + + // "link to this" widget + $('a.link_to_this').click(function() { + var box = $('div#link_box'); + var location = window.location.protocol + "//" + window.location.hostname + $(this).attr('href'); + box.width(location.length + " em"); + box.find('input').val(location).attr('size', location.length + " em"); + box.show(); + box.find('input').select(); + box.position({ + my: "left top", + at: "left bottom", + of: this, + collision: "fit" }); + return false; + }); + + $('.close-button').click(function() { $(this).parent().hide() }); + $('div#variety-filter a').each(function() { + $(this).click(function() { + var form = $('form#search_form'); + form.attr('action', $(this).attr('href')); + form.submit(); + return false; + }) + }) + + if($.cookie('seen_foi2') == 1) { + $('#everypage').hide(); + } + +}) diff --git a/app/assets/javascripts/jquery.Jcrop.js b/app/assets/javascripts/jquery.Jcrop.js new file mode 100644 index 000000000..9002b9787 --- /dev/null +++ b/app/assets/javascripts/jquery.Jcrop.js @@ -0,0 +1,163 @@ +/** + * Jcrop v.0.9.8 (minimized) + * (c) 2008 Kelly Hallman and DeepLiquid.com + * More information: http://deepliquid.com/content/Jcrop.html + * Released under MIT License - this header must remain with code + */ + + +(function($){$.Jcrop=function(obj,opt) +{var obj=obj,opt=opt;if(typeof(obj)!=='object')obj=$(obj)[0];if(typeof(opt)!=='object')opt={};if(!('trackDocument'in opt)) +{opt.trackDocument=$.browser.msie?false:true;if($.browser.msie&&$.browser.version.split('.')[0]=='8') +opt.trackDocument=true;} +if(!('keySupport'in opt)) +opt.keySupport=$.browser.msie?false:true;var defaults={trackDocument:false,baseClass:'jcrop',addClass:null,bgColor:'black',bgOpacity:.6,borderOpacity:.4,handleOpacity:.5,handlePad:5,handleSize:9,handleOffset:5,edgeMargin:14,aspectRatio:0,keySupport:true,cornerHandles:true,sideHandles:true,drawBorders:true,dragEdges:true,boxWidth:0,boxHeight:0,boundary:8,animationDelay:20,swingSpeed:3,allowSelect:true,allowMove:true,allowResize:true,minSelect:[0,0],maxSize:[0,0],minSize:[0,0],onChange:function(){},onSelect:function(){}};var options=defaults;setOptions(opt);var $origimg=$(obj);var $img=$origimg.clone().removeAttr('id').css({position:'absolute'});$img.width($origimg.width());$img.height($origimg.height());$origimg.after($img).hide();presize($img,options.boxWidth,options.boxHeight);var boundx=$img.width(),boundy=$img.height(),$div=$('<div />').width(boundx).height(boundy).addClass(cssClass('holder')).css({position:'relative',backgroundColor:options.bgColor}).insertAfter($origimg).append($img);;if(options.addClass)$div.addClass(options.addClass);var $img2=$('<img />').attr('src',$img.attr('src')).css('position','absolute').width(boundx).height(boundy);var $img_holder=$('<div />').width(pct(100)).height(pct(100)).css({zIndex:310,position:'absolute',overflow:'hidden'}).append($img2);var $hdl_holder=$('<div />').width(pct(100)).height(pct(100)).css('zIndex',320);var $sel=$('<div />').css({position:'absolute',zIndex:300}).insertBefore($img).append($img_holder,$hdl_holder);var bound=options.boundary;var $trk=newTracker().width(boundx+(bound*2)).height(boundy+(bound*2)).css({position:'absolute',top:px(-bound),left:px(-bound),zIndex:290}).mousedown(newSelection);var xlimit,ylimit,xmin,ymin;var xscale,yscale,enabled=true;var docOffset=getPos($img),btndown,lastcurs,dimmed,animating,shift_down;var Coords=function() +{var x1=0,y1=0,x2=0,y2=0,ox,oy;function setPressed(pos) +{var pos=rebound(pos);x2=x1=pos[0];y2=y1=pos[1];};function setCurrent(pos) +{var pos=rebound(pos);ox=pos[0]-x2;oy=pos[1]-y2;x2=pos[0];y2=pos[1];};function getOffset() +{return[ox,oy];};function moveOffset(offset) +{var ox=offset[0],oy=offset[1];if(0>x1+ox)ox-=ox+x1;if(0>y1+oy)oy-=oy+y1;if(boundy<y2+oy)oy+=boundy-(y2+oy);if(boundx<x2+ox)ox+=boundx-(x2+ox);x1+=ox;x2+=ox;y1+=oy;y2+=oy;};function getCorner(ord) +{var c=getFixed();switch(ord) +{case'ne':return[c.x2,c.y];case'nw':return[c.x,c.y];case'se':return[c.x2,c.y2];case'sw':return[c.x,c.y2];}};function getFixed() +{if(!options.aspectRatio)return getRect();var aspect=options.aspectRatio,min_x=options.minSize[0]/xscale,min_y=options.minSize[1]/yscale,max_x=options.maxSize[0]/xscale,max_y=options.maxSize[1]/yscale,rw=x2-x1,rh=y2-y1,rwa=Math.abs(rw),rha=Math.abs(rh),real_ratio=rwa/rha,xx,yy;if(max_x==0){max_x=boundx*10} +if(max_y==0){max_y=boundy*10} +if(real_ratio<aspect) +{yy=y2;w=rha*aspect;xx=rw<0?x1-w:w+x1;if(xx<0) +{xx=0;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;} +else if(xx>boundx) +{xx=boundx;h=Math.abs((xx-x1)/aspect);yy=rh<0?y1-h:h+y1;}} +else +{xx=x2;h=rwa/aspect;yy=rh<0?y1-h:y1+h;if(yy<0) +{yy=0;w=Math.abs((yy-y1)*aspect);xx=rw<0?x1-w:w+x1;} +else if(yy>boundy) +{yy=boundy;w=Math.abs(yy-y1)*aspect;xx=rw<0?x1-w:w+x1;}} +if(xx>x1){if(xx-x1<min_x){xx=x1+min_x;}else if(xx-x1>max_x){xx=x1+max_x;} +if(yy>y1){yy=y1+(xx-x1)/aspect;}else{yy=y1-(xx-x1)/aspect;}}else if(xx<x1){if(x1-xx<min_x){xx=x1-min_x}else if(x1-xx>max_x){xx=x1-max_x;} +if(yy>y1){yy=y1+(x1-xx)/aspect;}else{yy=y1-(x1-xx)/aspect;}} +if(xx<0){x1-=xx;xx=0;}else if(xx>boundx){x1-=xx-boundx;xx=boundx;} +if(yy<0){y1-=yy;yy=0;}else if(yy>boundy){y1-=yy-boundy;yy=boundy;} +return last=makeObj(flipCoords(x1,y1,xx,yy));};function rebound(p) +{if(p[0]<0)p[0]=0;if(p[1]<0)p[1]=0;if(p[0]>boundx)p[0]=boundx;if(p[1]>boundy)p[1]=boundy;return[p[0],p[1]];};function flipCoords(x1,y1,x2,y2) +{var xa=x1,xb=x2,ya=y1,yb=y2;if(x2<x1) +{xa=x2;xb=x1;} +if(y2<y1) +{ya=y2;yb=y1;} +return[Math.round(xa),Math.round(ya),Math.round(xb),Math.round(yb)];};function getRect() +{var xsize=x2-x1;var ysize=y2-y1;if(xlimit&&(Math.abs(xsize)>xlimit)) +x2=(xsize>0)?(x1+xlimit):(x1-xlimit);if(ylimit&&(Math.abs(ysize)>ylimit)) +y2=(ysize>0)?(y1+ylimit):(y1-ylimit);if(ymin&&(Math.abs(ysize)<ymin)) +y2=(ysize>0)?(y1+ymin):(y1-ymin);if(xmin&&(Math.abs(xsize)<xmin)) +x2=(xsize>0)?(x1+xmin):(x1-xmin);if(x1<0){x2-=x1;x1-=x1;} +if(y1<0){y2-=y1;y1-=y1;} +if(x2<0){x1-=x2;x2-=x2;} +if(y2<0){y1-=y2;y2-=y2;} +if(x2>boundx){var delta=x2-boundx;x1-=delta;x2-=delta;} +if(y2>boundy){var delta=y2-boundy;y1-=delta;y2-=delta;} +if(x1>boundx){var delta=x1-boundy;y2-=delta;y1-=delta;} +if(y1>boundy){var delta=y1-boundy;y2-=delta;y1-=delta;} +return makeObj(flipCoords(x1,y1,x2,y2));};function makeObj(a) +{return{x:a[0],y:a[1],x2:a[2],y2:a[3],w:a[2]-a[0],h:a[3]-a[1]};};return{flipCoords:flipCoords,setPressed:setPressed,setCurrent:setCurrent,getOffset:getOffset,moveOffset:moveOffset,getCorner:getCorner,getFixed:getFixed};}();var Selection=function() +{var start,end,dragmode,awake,hdep=370;var borders={};var handle={};var seehandles=false;var hhs=options.handleOffset;if(options.drawBorders){borders={top:insertBorder('hline').css('top',$.browser.msie?px(-1):px(0)),bottom:insertBorder('hline'),left:insertBorder('vline'),right:insertBorder('vline')};} +if(options.dragEdges){handle.t=insertDragbar('n');handle.b=insertDragbar('s');handle.r=insertDragbar('e');handle.l=insertDragbar('w');} +options.sideHandles&&createHandles(['n','s','e','w']);options.cornerHandles&&createHandles(['sw','nw','ne','se']);function insertBorder(type) +{var jq=$('<div />').css({position:'absolute',opacity:options.borderOpacity}).addClass(cssClass(type));$img_holder.append(jq);return jq;};function dragDiv(ord,zi) +{var jq=$('<div />').mousedown(createDragger(ord)).css({cursor:ord+'-resize',position:'absolute',zIndex:zi});$hdl_holder.append(jq);return jq;};function insertHandle(ord) +{return dragDiv(ord,hdep++).css({top:px(-hhs+1),left:px(-hhs+1),opacity:options.handleOpacity}).addClass(cssClass('handle'));};function insertDragbar(ord) +{var s=options.handleSize,o=hhs,h=s,w=s,t=o,l=o;switch(ord) +{case'n':case's':w=pct(100);break;case'e':case'w':h=pct(100);break;} +return dragDiv(ord,hdep++).width(w).height(h).css({top:px(-t+1),left:px(-l+1)});};function createHandles(li) +{for(i in li)handle[li[i]]=insertHandle(li[i]);};function moveHandles(c) +{var midvert=Math.round((c.h/2)-hhs),midhoriz=Math.round((c.w/2)-hhs),north=west=-hhs+1,east=c.w-hhs,south=c.h-hhs,x,y;'e'in handle&&handle.e.css({top:px(midvert),left:px(east)})&&handle.w.css({top:px(midvert)})&&handle.s.css({top:px(south),left:px(midhoriz)})&&handle.n.css({left:px(midhoriz)});'ne'in handle&&handle.ne.css({left:px(east)})&&handle.se.css({top:px(south),left:px(east)})&&handle.sw.css({top:px(south)});'b'in handle&&handle.b.css({top:px(south)})&&handle.r.css({left:px(east)});};function moveto(x,y) +{$img2.css({top:px(-y),left:px(-x)});$sel.css({top:px(y),left:px(x)});};function resize(w,h) +{$sel.width(w).height(h);};function refresh() +{var c=Coords.getFixed();Coords.setPressed([c.x,c.y]);Coords.setCurrent([c.x2,c.y2]);updateVisible();};function updateVisible() +{if(awake)return update();};function update() +{var c=Coords.getFixed();resize(c.w,c.h);moveto(c.x,c.y);options.drawBorders&&borders['right'].css({left:px(c.w-1)})&&borders['bottom'].css({top:px(c.h-1)});seehandles&&moveHandles(c);awake||show();options.onChange(unscale(c));};function show() +{$sel.show();$img.css('opacity',options.bgOpacity);awake=true;};function release() +{disableHandles();$sel.hide();$img.css('opacity',1);awake=false;};function showHandles() +{if(seehandles) +{moveHandles(Coords.getFixed());$hdl_holder.show();}};function enableHandles() +{seehandles=true;if(options.allowResize) +{moveHandles(Coords.getFixed());$hdl_holder.show();return true;}};function disableHandles() +{seehandles=false;$hdl_holder.hide();};function animMode(v) +{(animating=v)?disableHandles():enableHandles();};function done() +{animMode(false);refresh();};var $track=newTracker().mousedown(createDragger('move')).css({cursor:'move',position:'absolute',zIndex:360}) +$img_holder.append($track);disableHandles();return{updateVisible:updateVisible,update:update,release:release,refresh:refresh,setCursor:function(cursor){$track.css('cursor',cursor);},enableHandles:enableHandles,enableOnly:function(){seehandles=true;},showHandles:showHandles,disableHandles:disableHandles,animMode:animMode,done:done};}();var Tracker=function() +{var onMove=function(){},onDone=function(){},trackDoc=options.trackDocument;if(!trackDoc) +{$trk.mousemove(trackMove).mouseup(trackUp).mouseout(trackUp);} +function toFront() +{$trk.css({zIndex:450});if(trackDoc) +{$(document).mousemove(trackMove).mouseup(trackUp);}} +function toBack() +{$trk.css({zIndex:290});if(trackDoc) +{$(document).unbind('mousemove',trackMove).unbind('mouseup',trackUp);}} +function trackMove(e) +{onMove(mouseAbs(e));};function trackUp(e) +{e.preventDefault();e.stopPropagation();if(btndown) +{btndown=false;onDone(mouseAbs(e));options.onSelect(unscale(Coords.getFixed()));toBack();onMove=function(){};onDone=function(){};} +return false;};function activateHandlers(move,done) +{btndown=true;onMove=move;onDone=done;toFront();return false;};function setCursor(t){$trk.css('cursor',t);};$img.before($trk);return{activateHandlers:activateHandlers,setCursor:setCursor};}();var KeyManager=function() +{var $keymgr=$('<input type="radio" />').css({position:'absolute',left:'-30px'}).keypress(parseKey).blur(onBlur),$keywrap=$('<div />').css({position:'absolute',overflow:'hidden'}).append($keymgr);function watchKeys() +{if(options.keySupport) +{$keymgr.show();$keymgr.focus();}};function onBlur(e) +{$keymgr.hide();};function doNudge(e,x,y) +{if(options.allowMove){Coords.moveOffset([x,y]);Selection.updateVisible();};e.preventDefault();e.stopPropagation();};function parseKey(e) +{if(e.ctrlKey)return true;shift_down=e.shiftKey?true:false;var nudge=shift_down?10:1;switch(e.keyCode) +{case 37:doNudge(e,-nudge,0);break;case 39:doNudge(e,nudge,0);break;case 38:doNudge(e,0,-nudge);break;case 40:doNudge(e,0,nudge);break;case 27:Selection.release();break;case 9:return true;} +return nothing(e);};if(options.keySupport)$keywrap.insertBefore($img);return{watchKeys:watchKeys};}();function px(n){return''+parseInt(n)+'px';};function pct(n){return''+parseInt(n)+'%';};function cssClass(cl){return options.baseClass+'-'+cl;};function getPos(obj) +{var pos=$(obj).offset();return[pos.left,pos.top];};function mouseAbs(e) +{return[(e.pageX-docOffset[0]),(e.pageY-docOffset[1])];};function myCursor(type) +{if(type!=lastcurs) +{Tracker.setCursor(type);lastcurs=type;}};function startDragMode(mode,pos) +{docOffset=getPos($img);Tracker.setCursor(mode=='move'?mode:mode+'-resize');if(mode=='move') +return Tracker.activateHandlers(createMover(pos),doneSelect);var fc=Coords.getFixed();var opp=oppLockCorner(mode);var opc=Coords.getCorner(oppLockCorner(opp));Coords.setPressed(Coords.getCorner(opp));Coords.setCurrent(opc);Tracker.activateHandlers(dragmodeHandler(mode,fc),doneSelect);};function dragmodeHandler(mode,f) +{return function(pos){if(!options.aspectRatio)switch(mode) +{case'e':pos[1]=f.y2;break;case'w':pos[1]=f.y2;break;case'n':pos[0]=f.x2;break;case's':pos[0]=f.x2;break;} +else switch(mode) +{case'e':pos[1]=f.y+1;break;case'w':pos[1]=f.y+1;break;case'n':pos[0]=f.x+1;break;case's':pos[0]=f.x+1;break;} +Coords.setCurrent(pos);Selection.update();};};function createMover(pos) +{var lloc=pos;KeyManager.watchKeys();return function(pos) +{Coords.moveOffset([pos[0]-lloc[0],pos[1]-lloc[1]]);lloc=pos;Selection.update();};};function oppLockCorner(ord) +{switch(ord) +{case'n':return'sw';case's':return'nw';case'e':return'nw';case'w':return'ne';case'ne':return'sw';case'nw':return'se';case'se':return'nw';case'sw':return'ne';};};function createDragger(ord) +{return function(e){if(options.disabled)return false;if((ord=='move')&&!options.allowMove)return false;btndown=true;startDragMode(ord,mouseAbs(e));e.stopPropagation();e.preventDefault();return false;};};function presize($obj,w,h) +{var nw=$obj.width(),nh=$obj.height();if((nw>w)&&w>0) +{nw=w;nh=(w/$obj.width())*$obj.height();} +if((nh>h)&&h>0) +{nh=h;nw=(h/$obj.height())*$obj.width();} +xscale=$obj.width()/nw;yscale=$obj.height()/nh;$obj.width(nw).height(nh);};function unscale(c) +{return{x:parseInt(c.x*xscale),y:parseInt(c.y*yscale),x2:parseInt(c.x2*xscale),y2:parseInt(c.y2*yscale),w:parseInt(c.w*xscale),h:parseInt(c.h*yscale)};};function doneSelect(pos) +{var c=Coords.getFixed();if(c.w>options.minSelect[0]&&c.h>options.minSelect[1]) +{Selection.enableHandles();Selection.done();} +else +{Selection.release();} +Tracker.setCursor(options.allowSelect?'crosshair':'default');};function newSelection(e) +{if(options.disabled)return false;if(!options.allowSelect)return false;btndown=true;docOffset=getPos($img);Selection.disableHandles();myCursor('crosshair');var pos=mouseAbs(e);Coords.setPressed(pos);Tracker.activateHandlers(selectDrag,doneSelect);KeyManager.watchKeys();Selection.update();e.stopPropagation();e.preventDefault();return false;};function selectDrag(pos) +{Coords.setCurrent(pos);Selection.update();};function newTracker() +{var trk=$('<div></div>').addClass(cssClass('tracker'));$.browser.msie&&trk.css({opacity:0,backgroundColor:'white'});return trk;};function animateTo(a) +{var x1=a[0]/xscale,y1=a[1]/yscale,x2=a[2]/xscale,y2=a[3]/yscale;if(animating)return;var animto=Coords.flipCoords(x1,y1,x2,y2);var c=Coords.getFixed();var animat=initcr=[c.x,c.y,c.x2,c.y2];var interv=options.animationDelay;var x=animat[0];var y=animat[1];var x2=animat[2];var y2=animat[3];var ix1=animto[0]-initcr[0];var iy1=animto[1]-initcr[1];var ix2=animto[2]-initcr[2];var iy2=animto[3]-initcr[3];var pcent=0;var velocity=options.swingSpeed;Selection.animMode(true);var animator=function() +{return function() +{pcent+=(100-pcent)/velocity;animat[0]=x+((pcent/100)*ix1);animat[1]=y+((pcent/100)*iy1);animat[2]=x2+((pcent/100)*ix2);animat[3]=y2+((pcent/100)*iy2);if(pcent<100)animateStart();else Selection.done();if(pcent>=99.8)pcent=100;setSelectRaw(animat);};}();function animateStart() +{window.setTimeout(animator,interv);};animateStart();};function setSelect(rect) +{setSelectRaw([rect[0]/xscale,rect[1]/yscale,rect[2]/xscale,rect[3]/yscale]);};function setSelectRaw(l) +{Coords.setPressed([l[0],l[1]]);Coords.setCurrent([l[2],l[3]]);Selection.update();};function setOptions(opt) +{if(typeof(opt)!='object')opt={};options=$.extend(options,opt);if(typeof(options.onChange)!=='function') +options.onChange=function(){};if(typeof(options.onSelect)!=='function') +options.onSelect=function(){};};function tellSelect() +{return unscale(Coords.getFixed());};function tellScaled() +{return Coords.getFixed();};function setOptionsNew(opt) +{setOptions(opt);interfaceUpdate();};function disableCrop() +{options.disabled=true;Selection.disableHandles();Selection.setCursor('default');Tracker.setCursor('default');};function enableCrop() +{options.disabled=false;interfaceUpdate();};function cancelCrop() +{Selection.done();Tracker.activateHandlers(null,null);};function destroy() +{$div.remove();$origimg.show();};function interfaceUpdate(alt) +{options.allowResize?alt?Selection.enableOnly():Selection.enableHandles():Selection.disableHandles();Tracker.setCursor(options.allowSelect?'crosshair':'default');Selection.setCursor(options.allowMove?'move':'default');$div.css('backgroundColor',options.bgColor);if('setSelect'in options){setSelect(opt.setSelect);Selection.done();delete(options.setSelect);} +if('trueSize'in options){xscale=options.trueSize[0]/boundx;yscale=options.trueSize[1]/boundy;} +xlimit=options.maxSize[0]||0;ylimit=options.maxSize[1]||0;xmin=options.minSize[0]||0;ymin=options.minSize[1]||0;if('outerImage'in options) +{$img.attr('src',options.outerImage);delete(options.outerImage);} +Selection.refresh();};$hdl_holder.hide();interfaceUpdate(true);var api={animateTo:animateTo,setSelect:setSelect,setOptions:setOptionsNew,tellSelect:tellSelect,tellScaled:tellScaled,disable:disableCrop,enable:enableCrop,cancel:cancelCrop,focus:KeyManager.watchKeys,getBounds:function(){return[boundx*xscale,boundy*yscale];},getWidgetSize:function(){return[boundx,boundy];},release:Selection.release,destroy:destroy};$origimg.data('Jcrop',api);return api;};$.fn.Jcrop=function(options) +{function attachWhenDone(from) +{var loadsrc=options.useImg||from.src;var img=new Image();img.onload=function(){$.Jcrop(from,options);};img.src=loadsrc;};if(typeof(options)!=='object')options={};this.each(function() +{if($(this).data('Jcrop')) +{if(options=='api')return $(this).data('Jcrop');else $(this).data('Jcrop').setOptions(options);} +else attachWhenDone(this);});return this;};})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.cookie.js b/app/assets/javascripts/jquery.cookie.js new file mode 100644 index 000000000..6a3e394b4 --- /dev/null +++ b/app/assets/javascripts/jquery.cookie.js @@ -0,0 +1,41 @@ +/** + * jQuery Cookie plugin + * + * Copyright (c) 2010 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ +jQuery.cookie = function (key, value, options) { + + // key and at least value given, set cookie... + if (arguments.length > 1 && String(value) !== "[object Object]") { + options = jQuery.extend({}, options); + + if (value === null || value === undefined) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', + options.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // key and possibly options given, get cookie... + options = value || {}; + var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent; + return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null; +}; diff --git a/app/assets/javascripts/jquery.fancybox-1.3.4.pack.js b/app/assets/javascripts/jquery.fancybox-1.3.4.pack.js new file mode 100755 index 000000000..1373ed083 --- /dev/null +++ b/app/assets/javascripts/jquery.fancybox-1.3.4.pack.js @@ -0,0 +1,46 @@ +/* + * FancyBox - jQuery Plugin + * Simple and fancy lightbox alternative + * + * Examples and documentation at: http://fancybox.net + * + * Copyright (c) 2008 - 2010 Janis Skarnelis + * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated. + * + * Version: 1.3.4 (11/11/2010) + * Requires: jQuery v1.3+ + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +;(function(b){var m,t,u,f,D,j,E,n,z,A,q=0,e={},o=[],p=0,d={},l=[],G=null,v=new Image,J=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,W=/[^\.]\.(swf)\s*$/i,K,L=1,y=0,s="",r,i,h=false,B=b.extend(b("<div/>")[0],{prop:0}),M=b.browser.msie&&b.browser.version<7&&!window.XMLHttpRequest,N=function(){t.hide();v.onerror=v.onload=null;G&&G.abort();m.empty()},O=function(){if(false===e.onError(o,q,e)){t.hide();h=false}else{e.titleShow=false;e.width="auto";e.height="auto";m.html('<p id="fancybox-error">The requested content cannot be loaded.<br />Please try again later.</p>'); +F()}},I=function(){var a=o[q],c,g,k,C,P,w;N();e=b.extend({},b.fn.fancybox.defaults,typeof b(a).data("fancybox")=="undefined"?e:b(a).data("fancybox"));w=e.onStart(o,q,e);if(w===false)h=false;else{if(typeof w=="object")e=b.extend(e,w);k=e.title||(a.nodeName?b(a).attr("title"):a.title)||"";if(a.nodeName&&!e.orig)e.orig=b(a).children("img:first").length?b(a).children("img:first"):b(a);if(k===""&&e.orig&&e.titleFromAlt)k=e.orig.attr("alt");c=e.href||(a.nodeName?b(a).attr("href"):a.href)||null;if(/^(?:javascript)/i.test(c)|| +c=="#")c=null;if(e.type){g=e.type;if(!c)c=e.content}else if(e.content)g="html";else if(c)g=c.match(J)?"image":c.match(W)?"swf":b(a).hasClass("iframe")?"iframe":c.indexOf("#")===0?"inline":"ajax";if(g){if(g=="inline"){a=c.substr(c.indexOf("#"));g=b(a).length>0?"inline":"ajax"}e.type=g;e.href=c;e.title=k;if(e.autoDimensions)if(e.type=="html"||e.type=="inline"||e.type=="ajax"){e.width="auto";e.height="auto"}else e.autoDimensions=false;if(e.modal){e.overlayShow=true;e.hideOnOverlayClick=false;e.hideOnContentClick= +false;e.enableEscapeButton=false;e.showCloseButton=false}e.padding=parseInt(e.padding,10);e.margin=parseInt(e.margin,10);m.css("padding",e.padding+e.margin);b(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){b(this).replaceWith(j.children())});switch(g){case "html":m.html(e.content);F();break;case "inline":if(b(a).parent().is("#fancybox-content")===true){h=false;break}b('<div class="fancybox-inline-tmp" />').hide().insertBefore(b(a)).bind("fancybox-cleanup",function(){b(this).replaceWith(j.children())}).bind("fancybox-cancel", +function(){b(this).replaceWith(m.children())});b(a).appendTo(m);F();break;case "image":h=false;b.fancybox.showActivity();v=new Image;v.onerror=function(){O()};v.onload=function(){h=true;v.onerror=v.onload=null;e.width=v.width;e.height=v.height;b("<img />").attr({id:"fancybox-img",src:v.src,alt:e.title}).appendTo(m);Q()};v.src=c;break;case "swf":e.scrolling="no";C='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+e.width+'" height="'+e.height+'"><param name="movie" value="'+c+ +'"></param>';P="";b.each(e.swf,function(x,H){C+='<param name="'+x+'" value="'+H+'"></param>';P+=" "+x+'="'+H+'"'});C+='<embed src="'+c+'" type="application/x-shockwave-flash" width="'+e.width+'" height="'+e.height+'"'+P+"></embed></object>";m.html(C);F();break;case "ajax":h=false;b.fancybox.showActivity();e.ajax.win=e.ajax.success;G=b.ajax(b.extend({},e.ajax,{url:c,data:e.ajax.data||{},error:function(x){x.status>0&&O()},success:function(x,H,R){if((typeof R=="object"?R:G).status==200){if(typeof e.ajax.win== +"function"){w=e.ajax.win(c,x,H,R);if(w===false){t.hide();return}else if(typeof w=="string"||typeof w=="object")x=w}m.html(x);F()}}}));break;case "iframe":Q()}}else O()}},F=function(){var a=e.width,c=e.height;a=a.toString().indexOf("%")>-1?parseInt((b(window).width()-e.margin*2)*parseFloat(a)/100,10)+"px":a=="auto"?"auto":a+"px";c=c.toString().indexOf("%")>-1?parseInt((b(window).height()-e.margin*2)*parseFloat(c)/100,10)+"px":c=="auto"?"auto":c+"px";m.wrapInner('<div style="width:'+a+";height:"+c+ +";overflow: "+(e.scrolling=="auto"?"auto":e.scrolling=="yes"?"scroll":"hidden")+';position:relative;"></div>');e.width=m.width();e.height=m.height();Q()},Q=function(){var a,c;t.hide();if(f.is(":visible")&&false===d.onCleanup(l,p,d)){b.event.trigger("fancybox-cancel");h=false}else{h=true;b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");f.is(":visible")&&d.titlePosition!=="outside"&&f.css("height",f.height());l=o;p=q;d=e;if(d.overlayShow){u.css({"background-color":d.overlayColor, +opacity:d.overlayOpacity,cursor:d.hideOnOverlayClick?"pointer":"auto",height:b(document).height()});if(!u.is(":visible")){M&&b("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"});u.show()}}else u.hide();i=X();s=d.title||"";y=0;n.empty().removeAttr("style").removeClass();if(d.titleShow!==false){if(b.isFunction(d.titleFormat))a=d.titleFormat(s,l,p,d);else a=s&&s.length? +d.titlePosition=="float"?'<table id="fancybox-title-float-wrap" cellpadding="0" cellspacing="0"><tr><td id="fancybox-title-float-left"></td><td id="fancybox-title-float-main">'+s+'</td><td id="fancybox-title-float-right"></td></tr></table>':'<div id="fancybox-title-'+d.titlePosition+'">'+s+"</div>":false;s=a;if(!(!s||s==="")){n.addClass("fancybox-title-"+d.titlePosition).html(s).appendTo("body").show();switch(d.titlePosition){case "inside":n.css({width:i.width-d.padding*2,marginLeft:d.padding,marginRight:d.padding}); +y=n.outerHeight(true);n.appendTo(D);i.height+=y;break;case "over":n.css({marginLeft:d.padding,width:i.width-d.padding*2,bottom:d.padding}).appendTo(D);break;case "float":n.css("left",parseInt((n.width()-i.width-40)/2,10)*-1).appendTo(f);break;default:n.css({width:i.width-d.padding*2,paddingLeft:d.padding,paddingRight:d.padding}).appendTo(f)}}}n.hide();if(f.is(":visible")){b(E.add(z).add(A)).hide();a=f.position();r={top:a.top,left:a.left,width:f.width(),height:f.height()};c=r.width==i.width&&r.height== +i.height;j.fadeTo(d.changeFade,0.3,function(){var g=function(){j.html(m.contents()).fadeTo(d.changeFade,1,S)};b.event.trigger("fancybox-change");j.empty().removeAttr("filter").css({"border-width":d.padding,width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2});if(c)g();else{B.prop=0;b(B).animate({prop:1},{duration:d.changeSpeed,easing:d.easingChange,step:T,complete:g})}})}else{f.removeAttr("style");j.css("border-width",d.padding);if(d.transitionIn=="elastic"){r=V();j.html(m.contents()); +f.show();if(d.opacity)i.opacity=0;B.prop=0;b(B).animate({prop:1},{duration:d.speedIn,easing:d.easingIn,step:T,complete:S})}else{d.titlePosition=="inside"&&y>0&&n.show();j.css({width:i.width-d.padding*2,height:e.autoDimensions?"auto":i.height-y-d.padding*2}).html(m.contents());f.css(i).fadeIn(d.transitionIn=="none"?0:d.speedIn,S)}}}},Y=function(){if(d.enableEscapeButton||d.enableKeyboardNav)b(document).bind("keydown.fb",function(a){if(a.keyCode==27&&d.enableEscapeButton){a.preventDefault();b.fancybox.close()}else if((a.keyCode== +37||a.keyCode==39)&&d.enableKeyboardNav&&a.target.tagName!=="INPUT"&&a.target.tagName!=="TEXTAREA"&&a.target.tagName!=="SELECT"){a.preventDefault();b.fancybox[a.keyCode==37?"prev":"next"]()}});if(d.showNavArrows){if(d.cyclic&&l.length>1||p!==0)z.show();if(d.cyclic&&l.length>1||p!=l.length-1)A.show()}else{z.hide();A.hide()}},S=function(){if(!b.support.opacity){j.get(0).style.removeAttribute("filter");f.get(0).style.removeAttribute("filter")}e.autoDimensions&&j.css("height","auto");f.css("height","auto"); +s&&s.length&&n.show();d.showCloseButton&&E.show();Y();d.hideOnContentClick&&j.bind("click",b.fancybox.close);d.hideOnOverlayClick&&u.bind("click",b.fancybox.close);b(window).bind("resize.fb",b.fancybox.resize);d.centerOnScroll&&b(window).bind("scroll.fb",b.fancybox.center);if(d.type=="iframe")b('<iframe id="fancybox-frame" name="fancybox-frame'+(new Date).getTime()+'" frameborder="0" hspace="0" '+(b.browser.msie?'allowtransparency="true""':"")+' scrolling="'+e.scrolling+'" src="'+d.href+'"></iframe>').appendTo(j); +f.show();h=false;b.fancybox.center();d.onComplete(l,p,d);var a,c;if(l.length-1>p){a=l[p+1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}if(p>0){a=l[p-1].href;if(typeof a!=="undefined"&&a.match(J)){c=new Image;c.src=a}}},T=function(a){var c={width:parseInt(r.width+(i.width-r.width)*a,10),height:parseInt(r.height+(i.height-r.height)*a,10),top:parseInt(r.top+(i.top-r.top)*a,10),left:parseInt(r.left+(i.left-r.left)*a,10)};if(typeof i.opacity!=="undefined")c.opacity=a<0.5?0.5:a;f.css(c); +j.css({width:c.width-d.padding*2,height:c.height-y*a-d.padding*2})},U=function(){return[b(window).width()-d.margin*2,b(window).height()-d.margin*2,b(document).scrollLeft()+d.margin,b(document).scrollTop()+d.margin]},X=function(){var a=U(),c={},g=d.autoScale,k=d.padding*2;c.width=d.width.toString().indexOf("%")>-1?parseInt(a[0]*parseFloat(d.width)/100,10):d.width+k;c.height=d.height.toString().indexOf("%")>-1?parseInt(a[1]*parseFloat(d.height)/100,10):d.height+k;if(g&&(c.width>a[0]||c.height>a[1]))if(e.type== +"image"||e.type=="swf"){g=d.width/d.height;if(c.width>a[0]){c.width=a[0];c.height=parseInt((c.width-k)/g+k,10)}if(c.height>a[1]){c.height=a[1];c.width=parseInt((c.height-k)*g+k,10)}}else{c.width=Math.min(c.width,a[0]);c.height=Math.min(c.height,a[1])}c.top=parseInt(Math.max(a[3]-20,a[3]+(a[1]-c.height-40)*0.5),10);c.left=parseInt(Math.max(a[2]-20,a[2]+(a[0]-c.width-40)*0.5),10);return c},V=function(){var a=e.orig?b(e.orig):false,c={};if(a&&a.length){c=a.offset();c.top+=parseInt(a.css("paddingTop"), +10)||0;c.left+=parseInt(a.css("paddingLeft"),10)||0;c.top+=parseInt(a.css("border-top-width"),10)||0;c.left+=parseInt(a.css("border-left-width"),10)||0;c.width=a.width();c.height=a.height();c={width:c.width+d.padding*2,height:c.height+d.padding*2,top:c.top-d.padding-20,left:c.left-d.padding-20}}else{a=U();c={width:d.padding*2,height:d.padding*2,top:parseInt(a[3]+a[1]*0.5,10),left:parseInt(a[2]+a[0]*0.5,10)}}return c},Z=function(){if(t.is(":visible")){b("div",t).css("top",L*-40+"px");L=(L+1)%12}else clearInterval(K)}; +b.fn.fancybox=function(a){if(!b(this).length)return this;b(this).data("fancybox",b.extend({},a,b.metadata?b(this).metadata():{})).unbind("click.fb").bind("click.fb",function(c){c.preventDefault();if(!h){h=true;b(this).blur();o=[];q=0;c=b(this).attr("rel")||"";if(!c||c==""||c==="nofollow")o.push(this);else{o=b("a[rel="+c+"], area[rel="+c+"]");q=o.index(this)}I()}});return this};b.fancybox=function(a,c){var g;if(!h){h=true;g=typeof c!=="undefined"?c:{};o=[];q=parseInt(g.index,10)||0;if(b.isArray(a)){for(var k= +0,C=a.length;k<C;k++)if(typeof a[k]=="object")b(a[k]).data("fancybox",b.extend({},g,a[k]));else a[k]=b({}).data("fancybox",b.extend({content:a[k]},g));o=jQuery.merge(o,a)}else{if(typeof a=="object")b(a).data("fancybox",b.extend({},g,a));else a=b({}).data("fancybox",b.extend({content:a},g));o.push(a)}if(q>o.length||q<0)q=0;I()}};b.fancybox.showActivity=function(){clearInterval(K);t.show();K=setInterval(Z,66)};b.fancybox.hideActivity=function(){t.hide()};b.fancybox.next=function(){return b.fancybox.pos(p+ +1)};b.fancybox.prev=function(){return b.fancybox.pos(p-1)};b.fancybox.pos=function(a){if(!h){a=parseInt(a);o=l;if(a>-1&&a<l.length){q=a;I()}else if(d.cyclic&&l.length>1){q=a>=l.length?0:l.length-1;I()}}};b.fancybox.cancel=function(){if(!h){h=true;b.event.trigger("fancybox-cancel");N();e.onCancel(o,q,e);h=false}};b.fancybox.close=function(){function a(){u.fadeOut("fast");n.empty().hide();f.hide();b.event.trigger("fancybox-cleanup");j.empty();d.onClosed(l,p,d);l=e=[];p=q=0;d=e={};h=false}if(!(h||f.is(":hidden"))){h= +true;if(d&&false===d.onCleanup(l,p,d))h=false;else{N();b(E.add(z).add(A)).hide();b(j.add(u)).unbind();b(window).unbind("resize.fb scroll.fb");b(document).unbind("keydown.fb");j.find("iframe").attr("src",M&&/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank");d.titlePosition!=="inside"&&n.empty();f.stop();if(d.transitionOut=="elastic"){r=V();var c=f.position();i={top:c.top,left:c.left,width:f.width(),height:f.height()};if(d.opacity)i.opacity=1;n.empty().hide();B.prop=1; +b(B).animate({prop:0},{duration:d.speedOut,easing:d.easingOut,step:T,complete:a})}else f.fadeOut(d.transitionOut=="none"?0:d.speedOut,a)}}};b.fancybox.resize=function(){u.is(":visible")&&u.css("height",b(document).height());b.fancybox.center(true)};b.fancybox.center=function(a){var c,g;if(!h){g=a===true?1:0;c=U();!g&&(f.width()>c[0]||f.height()>c[1])||f.stop().animate({top:parseInt(Math.max(c[3]-20,c[3]+(c[1]-j.height()-40)*0.5-d.padding)),left:parseInt(Math.max(c[2]-20,c[2]+(c[0]-j.width()-40)*0.5- +d.padding))},typeof a=="number"?a:200)}};b.fancybox.init=function(){if(!b("#fancybox-wrap").length){b("body").append(m=b('<div id="fancybox-tmp"></div>'),t=b('<div id="fancybox-loading"><div></div></div>'),u=b('<div id="fancybox-overlay"></div>'),f=b('<div id="fancybox-wrap"></div>'));D=b('<div id="fancybox-outer"></div>').append('<div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div>').appendTo(f); +D.append(j=b('<div id="fancybox-content"></div>'),E=b('<a id="fancybox-close"></a>'),n=b('<div id="fancybox-title"></div>'),z=b('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),A=b('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));E.click(b.fancybox.close);t.click(b.fancybox.cancel);z.click(function(a){a.preventDefault();b.fancybox.prev()});A.click(function(a){a.preventDefault();b.fancybox.next()}); +b.fn.mousewheel&&f.bind("mousewheel.fb",function(a,c){if(h)a.preventDefault();else if(b(a.target).get(0).clientHeight==0||b(a.target).get(0).scrollHeight===b(a.target).get(0).clientHeight){a.preventDefault();b.fancybox[c>0?"prev":"next"]()}});b.support.opacity||f.addClass("fancybox-ie");if(M){t.addClass("fancybox-ie6");f.addClass("fancybox-ie6");b('<iframe id="fancybox-hide-sel-frame" src="'+(/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank")+'" scrolling="no" border="0" frameborder="0" tabindex="-1"></iframe>').prependTo(D)}}}; +b.fn.fancybox.defaults={padding:10,margin:40,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.7,overlayColor:"#777",titleShow:true,titlePosition:"float",titleFormat:null,titleFromAlt:false,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing", +easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,enableKeyboardNav:true,onStart:function(){},onCancel:function(){},onComplete:function(){},onCleanup:function(){},onClosed:function(){},onError:function(){}};b(document).ready(function(){b.fancybox.init()})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.flot.axislabels.js b/app/assets/javascripts/jquery.flot.axislabels.js new file mode 100644 index 000000000..c6f77aad4 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.axislabels.js @@ -0,0 +1,451 @@ +/* +Axis Labels Plugin for flot. +http://github.com/markrcote/flot-axislabels + +Original code is Copyright (c) 2010 Xuan Luo. +Original code was released under the GPLv3 license by Xuan Luo, September 2010. +Original code was rereleased under the MIT license by Xuan Luo, April 2012. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +(function ($) { + var options = { }; + + function canvasSupported() { + return !!document.createElement('canvas').getContext; + } + + function canvasTextSupported() { + if (!canvasSupported()) { + return false; + } + var dummy_canvas = document.createElement('canvas'); + var context = dummy_canvas.getContext('2d'); + return typeof context.fillText == 'function'; + } + + function css3TransitionSupported() { + var div = document.createElement('div'); + return typeof div.style.MozTransition != 'undefined' // Gecko + || typeof div.style.OTransition != 'undefined' // Opera + || typeof div.style.webkitTransition != 'undefined' // WebKit + || typeof div.style.transition != 'undefined'; + } + + + function AxisLabel(axisName, position, padding, plot, opts) { + this.axisName = axisName; + this.position = position; + this.padding = padding; + this.plot = plot; + this.opts = opts; + this.width = 0; + this.height = 0; + } + + AxisLabel.prototype['delete'] = function() { + }; + + + CanvasAxisLabel.prototype = new AxisLabel(); + CanvasAxisLabel.prototype.constructor = CanvasAxisLabel; + function CanvasAxisLabel(axisName, position, padding, plot, opts) { + AxisLabel.prototype.constructor.call(this, axisName, position, padding, + plot, opts); + } + + CanvasAxisLabel.prototype.calculateSize = function() { + if (!this.opts.axisLabelFontSizePixels) + this.opts.axisLabelFontSizePixels = 14; + if (!this.opts.axisLabelFontFamily) + this.opts.axisLabelFontFamily = 'sans-serif'; + + var textWidth = this.opts.axisLabelFontSizePixels + this.padding; + var textHeight = this.opts.axisLabelFontSizePixels + this.padding; + if (this.position == 'left' || this.position == 'right') { + this.width = this.opts.axisLabelFontSizePixels + this.padding; + this.height = 0; + } else { + this.width = 0; + this.height = this.opts.axisLabelFontSizePixels + this.padding; + } + }; + + CanvasAxisLabel.prototype.draw = function(box) { + var ctx = this.plot.getCanvas().getContext('2d'); + ctx.save(); + ctx.font = this.opts.axisLabelFontSizePixels + 'px ' + + this.opts.axisLabelFontFamily; + var width = ctx.measureText(this.opts.axisLabel).width; + var height = this.opts.axisLabelFontSizePixels; + var x, y, angle = 0; + if (this.position == 'top') { + x = box.left + box.width/2 - width/2; + y = box.top + height*0.72; + } else if (this.position == 'bottom') { + x = box.left + box.width/2 - width/2; + y = box.top + box.height - height*0.72; + } else if (this.position == 'left') { + x = box.left + height*0.72; + y = box.height/2 + box.top + width/2; + angle = -Math.PI/2; + } else if (this.position == 'right') { + x = box.left + box.width - height*0.72; + y = box.height/2 + box.top - width/2; + angle = Math.PI/2; + } + ctx.translate(x, y); + ctx.rotate(angle); + ctx.fillText(this.opts.axisLabel, 0, 0); + ctx.restore(); + }; + + + HtmlAxisLabel.prototype = new AxisLabel(); + HtmlAxisLabel.prototype.constructor = HtmlAxisLabel; + function HtmlAxisLabel(axisName, position, padding, plot, opts) { + AxisLabel.prototype.constructor.call(this, axisName, position, + padding, plot, opts); + this.elem = null; + } + + HtmlAxisLabel.prototype.calculateSize = function() { + var elem = $('<div class="axisLabels" style="position:absolute;">' + + this.opts.axisLabel + '</div>'); + this.plot.getPlaceholder().append(elem); + // store height and width of label itself, for use in draw() + this.labelWidth = elem.outerWidth(true); + this.labelHeight = elem.outerHeight(true); + elem.remove(); + + this.width = this.height = 0; + if (this.position == 'left' || this.position == 'right') { + this.width = this.labelWidth + this.padding; + } else { + this.height = this.labelHeight + this.padding; + } + }; + + HtmlAxisLabel.prototype['delete'] = function() { + if (this.elem) { + this.elem.remove(); + } + }; + + HtmlAxisLabel.prototype.draw = function(box) { + this.plot.getPlaceholder().find('#' + this.axisName + 'Label').remove(); + this.elem = $('<div id="' + this.axisName + + 'Label" " class="axisLabels" style="position:absolute;">' + + this.opts.axisLabel + '</div>'); + this.plot.getPlaceholder().append(this.elem); + if (this.position == 'top') { + this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + + 'px'); + this.elem.css('top', box.top + 'px'); + } else if (this.position == 'bottom') { + this.elem.css('left', box.left + box.width/2 - this.labelWidth/2 + + 'px'); + this.elem.css('top', box.top + box.height - this.labelHeight + + 'px'); + } else if (this.position == 'left') { + this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + + 'px'); + this.elem.css('left', box.left + 'px'); + } else if (this.position == 'right') { + this.elem.css('top', box.top + box.height/2 - this.labelHeight/2 + + 'px'); + this.elem.css('left', box.left + box.width - this.labelWidth + + 'px'); + } + }; + + + CssTransformAxisLabel.prototype = new HtmlAxisLabel(); + CssTransformAxisLabel.prototype.constructor = CssTransformAxisLabel; + function CssTransformAxisLabel(axisName, position, padding, plot, opts) { + HtmlAxisLabel.prototype.constructor.call(this, axisName, position, + padding, plot, opts); + } + + CssTransformAxisLabel.prototype.calculateSize = function() { + HtmlAxisLabel.prototype.calculateSize.call(this); + this.width = this.height = 0; + if (this.position == 'left' || this.position == 'right') { + this.width = this.labelHeight + this.padding; + } else { + this.height = this.labelHeight + this.padding; + } + }; + + CssTransformAxisLabel.prototype.transforms = function(degrees, x, y) { + var stransforms = { + '-moz-transform': '', + '-webkit-transform': '', + '-o-transform': '', + '-ms-transform': '' + }; + if (x != 0 || y != 0) { + var stdTranslate = ' translate(' + x + 'px, ' + y + 'px)'; + stransforms['-moz-transform'] += stdTranslate; + stransforms['-webkit-transform'] += stdTranslate; + stransforms['-o-transform'] += stdTranslate; + stransforms['-ms-transform'] += stdTranslate; + } + if (degrees != 0) { + var rotation = degrees / 90; + var stdRotate = ' rotate(' + degrees + 'deg)'; + stransforms['-moz-transform'] += stdRotate; + stransforms['-webkit-transform'] += stdRotate; + stransforms['-o-transform'] += stdRotate; + stransforms['-ms-transform'] += stdRotate; + } + var s = 'top: 0; left: 0; '; + for (var prop in stransforms) { + if (stransforms[prop]) { + s += prop + ':' + stransforms[prop] + ';'; + } + } + s += ';'; + return s; + }; + + CssTransformAxisLabel.prototype.calculateOffsets = function(box) { + var offsets = { x: 0, y: 0, degrees: 0 }; + if (this.position == 'bottom') { + offsets.x = box.left + box.width/2 - this.labelWidth/2; + offsets.y = box.top + box.height - this.labelHeight; + } else if (this.position == 'top') { + offsets.x = box.left + box.width/2 - this.labelWidth/2; + offsets.y = box.top; + } else if (this.position == 'left') { + offsets.degrees = -90; + offsets.x = box.left - this.labelWidth/2 + this.labelHeight/2; + offsets.y = box.height/2 + box.top; + } else if (this.position == 'right') { + offsets.degrees = 90; + offsets.x = box.left + box.width - this.labelWidth/2 + - this.labelHeight/2; + offsets.y = box.height/2 + box.top; + } + return offsets; + }; + + CssTransformAxisLabel.prototype.draw = function(box) { + this.plot.getPlaceholder().find("." + this.axisName + "Label").remove(); + var offsets = this.calculateOffsets(box); + this.elem = $('<div class="axisLabels ' + this.axisName + + 'Label" style="position:absolute; ' + + 'color: ' + this.opts.color + '; ' + + this.transforms(offsets.degrees, offsets.x, offsets.y) + + '">' + this.opts.axisLabel + '</div>'); + this.plot.getPlaceholder().append(this.elem); + }; + + + IeTransformAxisLabel.prototype = new CssTransformAxisLabel(); + IeTransformAxisLabel.prototype.constructor = IeTransformAxisLabel; + function IeTransformAxisLabel(axisName, position, padding, plot, opts) { + CssTransformAxisLabel.prototype.constructor.call(this, axisName, + position, padding, + plot, opts); + this.requiresResize = false; + } + + IeTransformAxisLabel.prototype.transforms = function(degrees, x, y) { + // I didn't feel like learning the crazy Matrix stuff, so this uses + // a combination of the rotation transform and CSS positioning. + var s = ''; + if (degrees != 0) { + var rotation = degrees/90; + while (rotation < 0) { + rotation += 4; + } + s += ' filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=' + rotation + '); '; + // see below + this.requiresResize = (this.position == 'right'); + } + if (x != 0) { + s += 'left: ' + x + 'px; '; + } + if (y != 0) { + s += 'top: ' + y + 'px; '; + } + return s; + }; + + IeTransformAxisLabel.prototype.calculateOffsets = function(box) { + var offsets = CssTransformAxisLabel.prototype.calculateOffsets.call( + this, box); + // adjust some values to take into account differences between + // CSS and IE rotations. + if (this.position == 'top') { + // FIXME: not sure why, but placing this exactly at the top causes + // the top axis label to flip to the bottom... + offsets.y = box.top + 1; + } else if (this.position == 'left') { + offsets.x = box.left; + offsets.y = box.height/2 + box.top - this.labelWidth/2; + } else if (this.position == 'right') { + offsets.x = box.left + box.width - this.labelHeight; + offsets.y = box.height/2 + box.top - this.labelWidth/2; + } + return offsets; + }; + + IeTransformAxisLabel.prototype.draw = function(box) { + CssTransformAxisLabel.prototype.draw.call(this, box); + if (this.requiresResize) { + this.elem = this.plot.getPlaceholder().find("." + this.axisName + + "Label"); + // Since we used CSS positioning instead of transforms for + // translating the element, and since the positioning is done + // before any rotations, we have to reset the width and height + // in case the browser wrapped the text (specifically for the + // y2axis). + this.elem.css('width', this.labelWidth); + this.elem.css('height', this.labelHeight); + } + }; + + + function init(plot) { + // This is kind of a hack. There are no hooks in Flot between + // the creation and measuring of the ticks (setTicks, measureTickLabels + // in setupGrid() ) and the drawing of the ticks and plot box + // (insertAxisLabels in setupGrid() ). + // + // Therefore, we use a trick where we run the draw routine twice: + // the first time to get the tick measurements, so that we can change + // them, and then have it draw it again. + var secondPass = false; + + var axisLabels = {}; + var axisOffsetCounts = { left: 0, right: 0, top: 0, bottom: 0 }; + + var defaultPadding = 2; // padding between axis and tick labels + plot.hooks.draw.push(function (plot, ctx) { + var hasAxisLabels = false; + if (!secondPass) { + // MEASURE AND SET OPTIONS + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options // Flot 0.7 + || plot.getOptions()[axisName]; // Flot 0.6 + + // Handle redraws initiated outside of this plug-in. + if (axisName in axisLabels) { + axis.labelHeight = axis.labelHeight - + axisLabels[axisName].height; + axis.labelWidth = axis.labelWidth - + axisLabels[axisName].width; + opts.labelHeight = axis.labelHeight; + opts.labelWidth = axis.labelWidth; + axisLabels[axisName]['delete'](); + delete axisLabels[axisName]; + } + + if (!opts || !opts.axisLabel || !axis.show) + return; + + hasAxisLabels = true; + var renderer = null; + + if (!opts.axisLabelUseHtml && + navigator.appName == 'Microsoft Internet Explorer') { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) { + rv = parseFloat(RegExp.$1); + } + if (rv >= 9 && !opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { + renderer = CssTransformAxisLabel; + } else if (!opts.axisLabelUseCanvas && !opts.axisLabelUseHtml) { + renderer = IeTransformAxisLabel; + } else if (opts.axisLabelUseCanvas) { + renderer = CanvasAxisLabel; + } else { + renderer = HtmlAxisLabel; + } + } else { + if (opts.axisLabelUseHtml || (!css3TransitionSupported() && !canvasTextSupported()) && !opts.axisLabelUseCanvas) { + renderer = HtmlAxisLabel; + } else if (opts.axisLabelUseCanvas || !css3TransitionSupported()) { + renderer = CanvasAxisLabel; + } else { + renderer = CssTransformAxisLabel; + } + } + + var padding = opts.axisLabelPadding === undefined ? + defaultPadding : opts.axisLabelPadding; + + axisLabels[axisName] = new renderer(axisName, + axis.position, padding, + plot, opts); + + // flot interprets axis.labelHeight and .labelWidth as + // the height and width of the tick labels. We increase + // these values to make room for the axis label and + // padding. + + axisLabels[axisName].calculateSize(); + + // AxisLabel.height and .width are the size of the + // axis label and padding. + // Just set opts here because axis will be sorted out on + // the redraw. + + opts.labelHeight = axis.labelHeight + + axisLabels[axisName].height; + opts.labelWidth = axis.labelWidth + + axisLabels[axisName].width; + }); + + // If there are axis labels, re-draw with new label widths and + // heights. + + if (hasAxisLabels) { + secondPass = true; + plot.setupGrid(); + plot.draw(); + } + } else { + secondPass = false; + // DRAW + $.each(plot.getAxes(), function(axisName, axis) { + var opts = axis.options // Flot 0.7 + || plot.getOptions()[axisName]; // Flot 0.6 + if (!opts || !opts.axisLabel || !axis.show) + return; + + axisLabels[axisName].draw(axis.box); + }); + } + }); + } + + + $.plot.plugins.push({ + init: init, + options: options, + name: 'axisLabels', + version: '2.0b0' + }); +})(jQuery); diff --git a/app/assets/javascripts/jquery.flot.axislabels.min.js b/app/assets/javascripts/jquery.flot.axislabels.min.js new file mode 100644 index 000000000..684d6173a --- /dev/null +++ b/app/assets/javascripts/jquery.flot.axislabels.min.js @@ -0,0 +1 @@ +(function($){var options={};function canvasSupported(){return !!document.createElement("canvas").getContext}function canvasTextSupported(){if(!canvasSupported()){return false}var dummy_canvas=document.createElement("canvas");var context=dummy_canvas.getContext("2d");return typeof context.fillText=="function"}function css3TransitionSupported(){var div=document.createElement("div");return typeof div.style.MozTransition!="undefined"||typeof div.style.OTransition!="undefined"||typeof div.style.webkitTransition!="undefined"||typeof div.style.transition!="undefined"}function AxisLabel(axisName,position,padding,plot,opts){this.axisName=axisName;this.position=position;this.padding=padding;this.plot=plot;this.opts=opts;this.width=0;this.height=0}AxisLabel.prototype["delete"]=function(){};CanvasAxisLabel.prototype=new AxisLabel();CanvasAxisLabel.prototype.constructor=CanvasAxisLabel;function CanvasAxisLabel(axisName,position,padding,plot,opts){AxisLabel.prototype.constructor.call(this,axisName,position,padding,plot,opts)}CanvasAxisLabel.prototype.calculateSize=function(){if(!this.opts.axisLabelFontSizePixels){this.opts.axisLabelFontSizePixels=14}if(!this.opts.axisLabelFontFamily){this.opts.axisLabelFontFamily="sans-serif"}var textWidth=this.opts.axisLabelFontSizePixels+this.padding;var textHeight=this.opts.axisLabelFontSizePixels+this.padding;if(this.position=="left"||this.position=="right"){this.width=this.opts.axisLabelFontSizePixels+this.padding;this.height=0}else{this.width=0;this.height=this.opts.axisLabelFontSizePixels+this.padding}};CanvasAxisLabel.prototype.draw=function(box){var ctx=this.plot.getCanvas().getContext("2d");ctx.save();ctx.font=this.opts.axisLabelFontSizePixels+"px "+this.opts.axisLabelFontFamily;var width=ctx.measureText(this.opts.axisLabel).width;var height=this.opts.axisLabelFontSizePixels;var x,y,angle=0;if(this.position=="top"){x=box.left+box.width/2-width/2;y=box.top+height*0.72}else{if(this.position=="bottom"){x=box.left+box.width/2-width/2;y=box.top+box.height-height*0.72}else{if(this.position=="left"){x=box.left+height*0.72;y=box.height/2+box.top+width/2;angle=-Math.PI/2}else{if(this.position=="right"){x=box.left+box.width-height*0.72;y=box.height/2+box.top-width/2;angle=Math.PI/2}}}}ctx.translate(x,y);ctx.rotate(angle);ctx.fillText(this.opts.axisLabel,0,0);ctx.restore()};HtmlAxisLabel.prototype=new AxisLabel();HtmlAxisLabel.prototype.constructor=HtmlAxisLabel;function HtmlAxisLabel(axisName,position,padding,plot,opts){AxisLabel.prototype.constructor.call(this,axisName,position,padding,plot,opts);this.elem=null}HtmlAxisLabel.prototype.calculateSize=function(){var elem=$('<div class="axisLabels" style="position:absolute;">'+this.opts.axisLabel+"</div>");this.plot.getPlaceholder().append(elem);this.labelWidth=elem.outerWidth(true);this.labelHeight=elem.outerHeight(true);elem.remove();this.width=this.height=0;if(this.position=="left"||this.position=="right"){this.width=this.labelWidth+this.padding}else{this.height=this.labelHeight+this.padding}};HtmlAxisLabel.prototype["delete"]=function(){if(this.elem){this.elem.remove()}};HtmlAxisLabel.prototype.draw=function(box){this.plot.getPlaceholder().find("#"+this.axisName+"Label").remove();this.elem=$('<div id="'+this.axisName+'Label" " class="axisLabels" style="position:absolute;">'+this.opts.axisLabel+"</div>");this.plot.getPlaceholder().append(this.elem);if(this.position=="top"){this.elem.css("left",box.left+box.width/2-this.labelWidth/2+"px");this.elem.css("top",box.top+"px")}else{if(this.position=="bottom"){this.elem.css("left",box.left+box.width/2-this.labelWidth/2+"px");this.elem.css("top",box.top+box.height-this.labelHeight+"px")}else{if(this.position=="left"){this.elem.css("top",box.top+box.height/2-this.labelHeight/2+"px");this.elem.css("left",box.left+"px")}else{if(this.position=="right"){this.elem.css("top",box.top+box.height/2-this.labelHeight/2+"px");this.elem.css("left",box.left+box.width-this.labelWidth+"px")}}}}};CssTransformAxisLabel.prototype=new HtmlAxisLabel();CssTransformAxisLabel.prototype.constructor=CssTransformAxisLabel;function CssTransformAxisLabel(axisName,position,padding,plot,opts){HtmlAxisLabel.prototype.constructor.call(this,axisName,position,padding,plot,opts)}CssTransformAxisLabel.prototype.calculateSize=function(){HtmlAxisLabel.prototype.calculateSize.call(this);this.width=this.height=0;if(this.position=="left"||this.position=="right"){this.width=this.labelHeight+this.padding}else{this.height=this.labelHeight+this.padding}};CssTransformAxisLabel.prototype.transforms=function(degrees,x,y){var stransforms={"-moz-transform":"","-webkit-transform":"","-o-transform":"","-ms-transform":""};if(x!=0||y!=0){var stdTranslate=" translate("+x+"px, "+y+"px)";stransforms["-moz-transform"]+=stdTranslate;stransforms["-webkit-transform"]+=stdTranslate;stransforms["-o-transform"]+=stdTranslate;stransforms["-ms-transform"]+=stdTranslate}if(degrees!=0){var rotation=degrees/90;var stdRotate=" rotate("+degrees+"deg)";stransforms["-moz-transform"]+=stdRotate;stransforms["-webkit-transform"]+=stdRotate;stransforms["-o-transform"]+=stdRotate;stransforms["-ms-transform"]+=stdRotate}var s="top: 0; left: 0; ";for(var prop in stransforms){if(stransforms[prop]){s+=prop+":"+stransforms[prop]+";"}}s+=";";return s};CssTransformAxisLabel.prototype.calculateOffsets=function(box){var offsets={x:0,y:0,degrees:0};if(this.position=="bottom"){offsets.x=box.left+box.width/2-this.labelWidth/2;offsets.y=box.top+box.height-this.labelHeight}else{if(this.position=="top"){offsets.x=box.left+box.width/2-this.labelWidth/2;offsets.y=box.top}else{if(this.position=="left"){offsets.degrees=-90;offsets.x=box.left-this.labelWidth/2+this.labelHeight/2;offsets.y=box.height/2+box.top}else{if(this.position=="right"){offsets.degrees=90;offsets.x=box.left+box.width-this.labelWidth/2-this.labelHeight/2;offsets.y=box.height/2+box.top}}}}return offsets};CssTransformAxisLabel.prototype.draw=function(box){this.plot.getPlaceholder().find("."+this.axisName+"Label").remove();var offsets=this.calculateOffsets(box);this.elem=$('<div class="axisLabels '+this.axisName+'Label" style="position:absolute; color: '+this.opts.color+"; "+this.transforms(offsets.degrees,offsets.x,offsets.y)+'">'+this.opts.axisLabel+"</div>");this.plot.getPlaceholder().append(this.elem)};IeTransformAxisLabel.prototype=new CssTransformAxisLabel();IeTransformAxisLabel.prototype.constructor=IeTransformAxisLabel;function IeTransformAxisLabel(axisName,position,padding,plot,opts){CssTransformAxisLabel.prototype.constructor.call(this,axisName,position,padding,plot,opts);this.requiresResize=false}IeTransformAxisLabel.prototype.transforms=function(degrees,x,y){var s="";if(degrees!=0){var rotation=degrees/90;while(rotation<0){rotation+=4}s+=" filter: progid:DXImageTransform.Microsoft.BasicImage(rotation="+rotation+"); ";this.requiresResize=(this.position=="right")}if(x!=0){s+="left: "+x+"px; "}if(y!=0){s+="top: "+y+"px; "}return s};IeTransformAxisLabel.prototype.calculateOffsets=function(box){var offsets=CssTransformAxisLabel.prototype.calculateOffsets.call(this,box);if(this.position=="top"){offsets.y=box.top+1}else{if(this.position=="left"){offsets.x=box.left;offsets.y=box.height/2+box.top-this.labelWidth/2}else{if(this.position=="right"){offsets.x=box.left+box.width-this.labelHeight;offsets.y=box.height/2+box.top-this.labelWidth/2}}}return offsets};IeTransformAxisLabel.prototype.draw=function(box){CssTransformAxisLabel.prototype.draw.call(this,box);if(this.requiresResize){this.elem=this.plot.getPlaceholder().find("."+this.axisName+"Label");this.elem.css("width",this.labelWidth);this.elem.css("height",this.labelHeight)}};function init(plot){var secondPass=false;var axisLabels={};var axisOffsetCounts={left:0,right:0,top:0,bottom:0};var defaultPadding=2;plot.hooks.draw.push(function(plot,ctx){var hasAxisLabels=false;if(!secondPass){$.each(plot.getAxes(),function(axisName,axis){var opts=axis.options||plot.getOptions()[axisName];if(axisName in axisLabels){axis.labelHeight=axis.labelHeight-axisLabels[axisName].height;axis.labelWidth=axis.labelWidth-axisLabels[axisName].width;opts.labelHeight=axis.labelHeight;opts.labelWidth=axis.labelWidth;axisLabels[axisName]["delete"]();delete axisLabels[axisName]}if(!opts||!opts.axisLabel||!axis.show){return}hasAxisLabels=true;var renderer=null;if(!opts.axisLabelUseHtml&&navigator.appName=="Microsoft Internet Explorer"){var ua=navigator.userAgent;var re=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");if(re.exec(ua)!=null){rv=parseFloat(RegExp.$1)}if(rv>=9&&!opts.axisLabelUseCanvas&&!opts.axisLabelUseHtml){renderer=CssTransformAxisLabel}else{if(!opts.axisLabelUseCanvas&&!opts.axisLabelUseHtml){renderer=IeTransformAxisLabel}else{if(opts.axisLabelUseCanvas){renderer=CanvasAxisLabel}else{renderer=HtmlAxisLabel}}}}else{if(opts.axisLabelUseHtml||(!css3TransitionSupported()&&!canvasTextSupported())&&!opts.axisLabelUseCanvas){renderer=HtmlAxisLabel}else{if(opts.axisLabelUseCanvas||!css3TransitionSupported()){renderer=CanvasAxisLabel}else{renderer=CssTransformAxisLabel}}}var padding=opts.axisLabelPadding===undefined?defaultPadding:opts.axisLabelPadding;axisLabels[axisName]=new renderer(axisName,axis.position,padding,plot,opts);axisLabels[axisName].calculateSize();opts.labelHeight=axis.labelHeight+axisLabels[axisName].height;opts.labelWidth=axis.labelWidth+axisLabels[axisName].width});if(hasAxisLabels){secondPass=true;plot.setupGrid();plot.draw()}}else{secondPass=false;$.each(plot.getAxes(),function(axisName,axis){var opts=axis.options||plot.getOptions()[axisName];if(!opts||!opts.axisLabel||!axis.show){return}axisLabels[axisName].draw(axis.box)})}})}$.plot.plugins.push({init:init,options:options,name:"axisLabels",version:"2.0b0"})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.flot.errorbars.js b/app/assets/javascripts/jquery.flot.errorbars.js new file mode 100644 index 000000000..729843678 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.errorbars.js @@ -0,0 +1,353 @@ +/* Flot plugin for plotting error bars. + +Copyright (c) 2007-2013 IOLA and Ole Laursen. +Licensed under the MIT license. + +Error bars are used to show standard deviation and other statistical +properties in a plot. + +* Created by Rui Pereira - rui (dot) pereira (at) gmail (dot) com + +This plugin allows you to plot error-bars over points. Set "errorbars" inside +the points series to the axis name over which there will be error values in +your data array (*even* if you do not intend to plot them later, by setting +"show: null" on xerr/yerr). + +The plugin supports these options: + + series: { + points: { + errorbars: "x" or "y" or "xy", + xerr: { + show: null/false or true, + asymmetric: null/false or true, + upperCap: null or "-" or function, + lowerCap: null or "-" or function, + color: null or color, + radius: null or number + }, + yerr: { same options as xerr } + } + } + +Each data point array is expected to be of the type: + + "x" [ x, y, xerr ] + "y" [ x, y, yerr ] + "xy" [ x, y, xerr, yerr ] + +Where xerr becomes xerr_lower,xerr_upper for the asymmetric error case, and +equivalently for yerr. Eg., a datapoint for the "xy" case with symmetric +error-bars on X and asymmetric on Y would be: + + [ x, y, xerr, yerr_lower, yerr_upper ] + +By default no end caps are drawn. Setting upperCap and/or lowerCap to "-" will +draw a small cap perpendicular to the error bar. They can also be set to a +user-defined drawing function, with (ctx, x, y, radius) as parameters, as eg. + + function drawSemiCircle( ctx, x, y, radius ) { + ctx.beginPath(); + ctx.arc( x, y, radius, 0, Math.PI, false ); + ctx.moveTo( x - radius, y ); + ctx.lineTo( x + radius, y ); + ctx.stroke(); + } + +Color and radius both default to the same ones of the points series if not +set. The independent radius parameter on xerr/yerr is useful for the case when +we may want to add error-bars to a line, without showing the interconnecting +points (with radius: 0), and still showing end caps on the error-bars. +shadowSize and lineWidth are derived as well from the points series. + +*/ + +(function ($) { + var options = { + series: { + points: { + errorbars: null, //should be 'x', 'y' or 'xy' + xerr: { err: 'x', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null}, + yerr: { err: 'y', show: null, asymmetric: null, upperCap: null, lowerCap: null, color: null, radius: null} + } + } + }; + + function processRawData(plot, series, data, datapoints){ + if (!series.points.errorbars) + return; + + // x,y values + var format = [ + { x: true, number: true, required: true }, + { y: true, number: true, required: true } + ]; + + var errors = series.points.errorbars; + // error bars - first X then Y + if (errors == 'x' || errors == 'xy') { + // lower / upper error + if (series.points.xerr.asymmetric) { + format.push({ x: true, number: true, required: true }); + format.push({ x: true, number: true, required: true }); + } else + format.push({ x: true, number: true, required: true }); + } + if (errors == 'y' || errors == 'xy') { + // lower / upper error + if (series.points.yerr.asymmetric) { + format.push({ y: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + } else + format.push({ y: true, number: true, required: true }); + } + datapoints.format = format; + } + + function parseErrors(series, i){ + + var points = series.datapoints.points; + + // read errors from points array + var exl = null, + exu = null, + eyl = null, + eyu = null; + var xerr = series.points.xerr, + yerr = series.points.yerr; + + var eb = series.points.errorbars; + // error bars - first X + if (eb == 'x' || eb == 'xy') { + if (xerr.asymmetric) { + exl = points[i + 2]; + exu = points[i + 3]; + if (eb == 'xy') + if (yerr.asymmetric){ + eyl = points[i + 4]; + eyu = points[i + 5]; + } else eyl = points[i + 4]; + } else { + exl = points[i + 2]; + if (eb == 'xy') + if (yerr.asymmetric) { + eyl = points[i + 3]; + eyu = points[i + 4]; + } else eyl = points[i + 3]; + } + // only Y + } else if (eb == 'y') + if (yerr.asymmetric) { + eyl = points[i + 2]; + eyu = points[i + 3]; + } else eyl = points[i + 2]; + + // symmetric errors? + if (exu == null) exu = exl; + if (eyu == null) eyu = eyl; + + var errRanges = [exl, exu, eyl, eyu]; + // nullify if not showing + if (!xerr.show){ + errRanges[0] = null; + errRanges[1] = null; + } + if (!yerr.show){ + errRanges[2] = null; + errRanges[3] = null; + } + return errRanges; + } + + function drawSeriesErrors(plot, ctx, s){ + + var points = s.datapoints.points, + ps = s.datapoints.pointsize, + ax = [s.xaxis, s.yaxis], + radius = s.points.radius, + err = [s.points.xerr, s.points.yerr]; + + //sanity check, in case some inverted axis hack is applied to flot + var invertX = false; + if (ax[0].p2c(ax[0].max) < ax[0].p2c(ax[0].min)) { + invertX = true; + var tmp = err[0].lowerCap; + err[0].lowerCap = err[0].upperCap; + err[0].upperCap = tmp; + } + + var invertY = false; + if (ax[1].p2c(ax[1].min) < ax[1].p2c(ax[1].max)) { + invertY = true; + var tmp = err[1].lowerCap; + err[1].lowerCap = err[1].upperCap; + err[1].upperCap = tmp; + } + + for (var i = 0; i < s.datapoints.points.length; i += ps) { + + //parse + var errRanges = parseErrors(s, i); + + //cycle xerr & yerr + for (var e = 0; e < err.length; e++){ + + var minmax = [ax[e].min, ax[e].max]; + + //draw this error? + if (errRanges[e * err.length]){ + + //data coordinates + var x = points[i], + y = points[i + 1]; + + //errorbar ranges + var upper = [x, y][e] + errRanges[e * err.length + 1], + lower = [x, y][e] - errRanges[e * err.length]; + + //points outside of the canvas + if (err[e].err == 'x') + if (y > ax[1].max || y < ax[1].min || upper < ax[0].min || lower > ax[0].max) + continue; + if (err[e].err == 'y') + if (x > ax[0].max || x < ax[0].min || upper < ax[1].min || lower > ax[1].max) + continue; + + // prevent errorbars getting out of the canvas + var drawUpper = true, + drawLower = true; + + if (upper > minmax[1]) { + drawUpper = false; + upper = minmax[1]; + } + if (lower < minmax[0]) { + drawLower = false; + lower = minmax[0]; + } + + //sanity check, in case some inverted axis hack is applied to flot + if ((err[e].err == 'x' && invertX) || (err[e].err == 'y' && invertY)) { + //swap coordinates + var tmp = lower; + lower = upper; + upper = tmp; + tmp = drawLower; + drawLower = drawUpper; + drawUpper = tmp; + tmp = minmax[0]; + minmax[0] = minmax[1]; + minmax[1] = tmp; + } + + // convert to pixels + x = ax[0].p2c(x), + y = ax[1].p2c(y), + upper = ax[e].p2c(upper); + lower = ax[e].p2c(lower); + minmax[0] = ax[e].p2c(minmax[0]); + minmax[1] = ax[e].p2c(minmax[1]); + + //same style as points by default + var lw = err[e].lineWidth ? err[e].lineWidth : s.points.lineWidth, + sw = s.points.shadowSize != null ? s.points.shadowSize : s.shadowSize; + + //shadow as for points + if (lw > 0 && sw > 0) { + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w + w/2, minmax); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, w/2, minmax); + } + + ctx.strokeStyle = err[e].color? err[e].color: s.color; + ctx.lineWidth = lw; + //draw it + drawError(ctx, err[e], x, y, upper, lower, drawUpper, drawLower, radius, 0, minmax); + } + } + } + } + + function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){ + + //shadow offset + y += offset; + upper += offset; + lower += offset; + + // error bar - avoid plotting over circles + if (err.err == 'x'){ + if (upper > x + radius) drawPath(ctx, [[upper,y],[Math.max(x + radius,minmax[0]),y]]); + else drawUpper = false; + if (lower < x - radius) drawPath(ctx, [[Math.min(x - radius,minmax[1]),y],[lower,y]] ); + else drawLower = false; + } + else { + if (upper < y - radius) drawPath(ctx, [[x,upper],[x,Math.min(y - radius,minmax[0])]] ); + else drawUpper = false; + if (lower > y + radius) drawPath(ctx, [[x,Math.max(y + radius,minmax[1])],[x,lower]] ); + else drawLower = false; + } + + //internal radius value in errorbar, allows to plot radius 0 points and still keep proper sized caps + //this is a way to get errorbars on lines without visible connecting dots + radius = err.radius != null? err.radius: radius; + + // upper cap + if (drawUpper) { + if (err.upperCap == '-'){ + if (err.err=='x') drawPath(ctx, [[upper,y - radius],[upper,y + radius]] ); + else drawPath(ctx, [[x - radius,upper],[x + radius,upper]] ); + } else if ($.isFunction(err.upperCap)){ + if (err.err=='x') err.upperCap(ctx, upper, y, radius); + else err.upperCap(ctx, x, upper, radius); + } + } + // lower cap + if (drawLower) { + if (err.lowerCap == '-'){ + if (err.err=='x') drawPath(ctx, [[lower,y - radius],[lower,y + radius]] ); + else drawPath(ctx, [[x - radius,lower],[x + radius,lower]] ); + } else if ($.isFunction(err.lowerCap)){ + if (err.err=='x') err.lowerCap(ctx, lower, y, radius); + else err.lowerCap(ctx, x, lower, radius); + } + } + } + + function drawPath(ctx, pts){ + ctx.beginPath(); + ctx.moveTo(pts[0][0], pts[0][1]); + for (var p=1; p < pts.length; p++) + ctx.lineTo(pts[p][0], pts[p][1]); + ctx.stroke(); + } + + function draw(plot, ctx){ + var plotOffset = plot.getPlotOffset(); + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + $.each(plot.getData(), function (i, s) { + if (s.points.errorbars && (s.points.xerr.show || s.points.yerr.show)) + drawSeriesErrors(plot, ctx, s); + }); + ctx.restore(); + } + + function init(plot) { + plot.hooks.processRawData.push(processRawData); + plot.hooks.draw.push(draw); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'errorbars', + version: '1.0' + }); +})(jQuery); diff --git a/app/assets/javascripts/jquery.flot.errorbars.min.js b/app/assets/javascripts/jquery.flot.errorbars.min.js new file mode 100644 index 000000000..84a514b07 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.errorbars.min.js @@ -0,0 +1 @@ +(function($){var options={series:{points:{errorbars:null,xerr:{err:"x",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null},yerr:{err:"y",show:null,asymmetric:null,upperCap:null,lowerCap:null,color:null,radius:null}}}};function processRawData(plot,series,data,datapoints){if(!series.points.errorbars){return}var format=[{x:true,number:true,required:true},{y:true,number:true,required:true}];var errors=series.points.errorbars;if(errors=="x"||errors=="xy"){if(series.points.xerr.asymmetric){format.push({x:true,number:true,required:true});format.push({x:true,number:true,required:true})}else{format.push({x:true,number:true,required:true})}}if(errors=="y"||errors=="xy"){if(series.points.yerr.asymmetric){format.push({y:true,number:true,required:true});format.push({y:true,number:true,required:true})}else{format.push({y:true,number:true,required:true})}}datapoints.format=format}function parseErrors(series,i){var points=series.datapoints.points;var exl=null,exu=null,eyl=null,eyu=null;var xerr=series.points.xerr,yerr=series.points.yerr;var eb=series.points.errorbars;if(eb=="x"||eb=="xy"){if(xerr.asymmetric){exl=points[i+2];exu=points[i+3];if(eb=="xy"){if(yerr.asymmetric){eyl=points[i+4];eyu=points[i+5]}else{eyl=points[i+4]}}}else{exl=points[i+2];if(eb=="xy"){if(yerr.asymmetric){eyl=points[i+3];eyu=points[i+4]}else{eyl=points[i+3]}}}}else{if(eb=="y"){if(yerr.asymmetric){eyl=points[i+2];eyu=points[i+3]}else{eyl=points[i+2]}}}if(exu==null){exu=exl}if(eyu==null){eyu=eyl}var errRanges=[exl,exu,eyl,eyu];if(!xerr.show){errRanges[0]=null;errRanges[1]=null}if(!yerr.show){errRanges[2]=null;errRanges[3]=null}return errRanges}function drawSeriesErrors(plot,ctx,s){var points=s.datapoints.points,ps=s.datapoints.pointsize,ax=[s.xaxis,s.yaxis],radius=s.points.radius,err=[s.points.xerr,s.points.yerr];var invertX=false;if(ax[0].p2c(ax[0].max)<ax[0].p2c(ax[0].min)){invertX=true;var tmp=err[0].lowerCap;err[0].lowerCap=err[0].upperCap;err[0].upperCap=tmp}var invertY=false;if(ax[1].p2c(ax[1].min)<ax[1].p2c(ax[1].max)){invertY=true;var tmp=err[1].lowerCap;err[1].lowerCap=err[1].upperCap;err[1].upperCap=tmp}for(var i=0;i<s.datapoints.points.length;i+=ps){var errRanges=parseErrors(s,i);for(var e=0;e<err.length;e++){var minmax=[ax[e].min,ax[e].max];if(errRanges[e*err.length]){var x=points[i],y=points[i+1];var upper=[x,y][e]+errRanges[e*err.length+1],lower=[x,y][e]-errRanges[e*err.length];if(err[e].err=="x"){if(y>ax[1].max||y<ax[1].min||upper<ax[0].min||lower>ax[0].max){continue}}if(err[e].err=="y"){if(x>ax[0].max||x<ax[0].min||upper<ax[1].min||lower>ax[1].max){continue}}var drawUpper=true,drawLower=true;if(upper>minmax[1]){drawUpper=false;upper=minmax[1]}if(lower<minmax[0]){drawLower=false;lower=minmax[0]}if((err[e].err=="x"&&invertX)||(err[e].err=="y"&&invertY)){var tmp=lower;lower=upper;upper=tmp;tmp=drawLower;drawLower=drawUpper;drawUpper=tmp;tmp=minmax[0];minmax[0]=minmax[1];minmax[1]=tmp}x=ax[0].p2c(x),y=ax[1].p2c(y),upper=ax[e].p2c(upper);lower=ax[e].p2c(lower);minmax[0]=ax[e].p2c(minmax[0]);minmax[1]=ax[e].p2c(minmax[1]);var lw=err[e].lineWidth?err[e].lineWidth:s.points.lineWidth,sw=s.points.shadowSize!=null?s.points.shadowSize:s.shadowSize;if(lw>0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w+w/2,minmax);ctx.strokeStyle="rgba(0,0,0,0.2)";drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,w/2,minmax)}ctx.strokeStyle=err[e].color?err[e].color:s.color;ctx.lineWidth=lw;drawError(ctx,err[e],x,y,upper,lower,drawUpper,drawLower,radius,0,minmax)}}}}function drawError(ctx,err,x,y,upper,lower,drawUpper,drawLower,radius,offset,minmax){y+=offset;upper+=offset;lower+=offset;if(err.err=="x"){if(upper>x+radius){drawPath(ctx,[[upper,y],[Math.max(x+radius,minmax[0]),y]])}else{drawUpper=false}if(lower<x-radius){drawPath(ctx,[[Math.min(x-radius,minmax[1]),y],[lower,y]])}else{drawLower=false}}else{if(upper<y-radius){drawPath(ctx,[[x,upper],[x,Math.min(y-radius,minmax[0])]])}else{drawUpper=false}if(lower>y+radius){drawPath(ctx,[[x,Math.max(y+radius,minmax[1])],[x,lower]])}else{drawLower=false}}radius=err.radius!=null?err.radius:radius;if(drawUpper){if(err.upperCap=="-"){if(err.err=="x"){drawPath(ctx,[[upper,y-radius],[upper,y+radius]])}else{drawPath(ctx,[[x-radius,upper],[x+radius,upper]])}}else{if($.isFunction(err.upperCap)){if(err.err=="x"){err.upperCap(ctx,upper,y,radius)}else{err.upperCap(ctx,x,upper,radius)}}}}if(drawLower){if(err.lowerCap=="-"){if(err.err=="x"){drawPath(ctx,[[lower,y-radius],[lower,y+radius]])}else{drawPath(ctx,[[x-radius,lower],[x+radius,lower]])}}else{if($.isFunction(err.lowerCap)){if(err.err=="x"){err.lowerCap(ctx,lower,y,radius)}else{err.lowerCap(ctx,x,lower,radius)}}}}}function drawPath(ctx,pts){ctx.beginPath();ctx.moveTo(pts[0][0],pts[0][1]);for(var p=1;p<pts.length;p++){ctx.lineTo(pts[p][0],pts[p][1])}ctx.stroke()}function draw(plot,ctx){var plotOffset=plot.getPlotOffset();ctx.save();ctx.translate(plotOffset.left,plotOffset.top);$.each(plot.getData(),function(i,s){if(s.points.errorbars&&(s.points.xerr.show||s.points.yerr.show)){drawSeriesErrors(plot,ctx,s)}});ctx.restore()}function init(plot){plot.hooks.processRawData.push(processRawData);plot.hooks.draw.push(draw)}$.plot.plugins.push({init:init,options:options,name:"errorbars",version:"1.0"})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.flot.js b/app/assets/javascripts/jquery.flot.js new file mode 100644 index 000000000..2855d2eb3 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.js @@ -0,0 +1,3078 @@ +/* Javascript plotting library for jQuery, version 0.8.2-alpha. + +Copyright (c) 2007-2013 IOLA and Ole Laursen. +Licensed under the MIT license. + +*/ + +// first an inline dependency, jquery.colorhelpers.js, we inline it here +// for convenience + +/* Plugin for jQuery for working with colors. + * + * Version 1.1. + * + * Inspiration from jQuery color animation plugin by John Resig. + * + * Released under the MIT license by Ole Laursen, October 2009. + * + * Examples: + * + * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() + * var c = $.color.extract($("#mydiv"), 'background-color'); + * console.log(c.r, c.g, c.b, c.a); + * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" + * + * Note that .scale() and .add() return the same modified object + * instead of making a new one. + * + * V. 1.1: Fix error handling so e.g. parsing an empty string does + * produce a color rather than just crashing. + */ +(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery); + +// the actual Flot code +(function($) { + + // Cache the prototype hasOwnProperty for faster access + + var hasOwnProperty = Object.prototype.hasOwnProperty; + + /////////////////////////////////////////////////////////////////////////// + // The Canvas object is a wrapper around an HTML5 <canvas> tag. + // + // @constructor + // @param {string} cls List of classes to apply to the canvas. + // @param {element} container Element onto which to append the canvas. + // + // Requiring a container is a little iffy, but unfortunately canvas + // operations don't work unless the canvas is attached to the DOM. + + function Canvas(cls, container) { + + var element = container.children("." + cls)[0]; + + if (element == null) { + + element = document.createElement("canvas"); + element.className = cls; + + $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 }) + .appendTo(container); + + // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas + + if (!element.getContext) { + if (window.G_vmlCanvasManager) { + element = window.G_vmlCanvasManager.initElement(element); + } else { + throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode."); + } + } + } + + this.element = element; + + var context = this.context = element.getContext("2d"); + + // Determine the screen's ratio of physical to device-independent + // pixels. This is the ratio between the canvas width that the browser + // advertises and the number of pixels actually present in that space. + + // The iPhone 4, for example, has a device-independent width of 320px, + // but its screen is actually 640px wide. It therefore has a pixel + // ratio of 2, while most normal devices have a ratio of 1. + + var devicePixelRatio = window.devicePixelRatio || 1, + backingStoreRatio = + context.webkitBackingStorePixelRatio || + context.mozBackingStorePixelRatio || + context.msBackingStorePixelRatio || + context.oBackingStorePixelRatio || + context.backingStorePixelRatio || 1; + + this.pixelRatio = devicePixelRatio / backingStoreRatio; + + // Size the canvas to match the internal dimensions of its container + + this.resize(container.width(), container.height()); + + // Collection of HTML div layers for text overlaid onto the canvas + + this.textContainer = null; + this.text = {}; + + // Cache of text fragments and metrics, so we can avoid expensively + // re-calculating them when the plot is re-rendered in a loop. + + this._textCache = {}; + } + + // Resizes the canvas to the given dimensions. + // + // @param {number} width New width of the canvas, in pixels. + // @param {number} width New height of the canvas, in pixels. + + Canvas.prototype.resize = function(width, height) { + + if (width <= 0 || height <= 0) { + throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height); + } + + var element = this.element, + context = this.context, + pixelRatio = this.pixelRatio; + + // Resize the canvas, increasing its density based on the display's + // pixel ratio; basically giving it more pixels without increasing the + // size of its element, to take advantage of the fact that retina + // displays have that many more pixels in the same advertised space. + + // Resizing should reset the state (excanvas seems to be buggy though) + + if (this.width != width) { + element.width = width * pixelRatio; + element.style.width = width + "px"; + this.width = width; + } + + if (this.height != height) { + element.height = height * pixelRatio; + element.style.height = height + "px"; + this.height = height; + } + + // Save the context, so we can reset in case we get replotted. The + // restore ensure that we're really back at the initial state, and + // should be safe even if we haven't saved the initial state yet. + + context.restore(); + context.save(); + + // Scale the coordinate space to match the display density; so even though we + // may have twice as many pixels, we still want lines and other drawing to + // appear at the same size; the extra pixels will just make them crisper. + + context.scale(pixelRatio, pixelRatio); + }; + + // Clears the entire canvas area, not including any overlaid HTML text + + Canvas.prototype.clear = function() { + this.context.clearRect(0, 0, this.width, this.height); + }; + + // Finishes rendering the canvas, including managing the text overlay. + + Canvas.prototype.render = function() { + + var cache = this._textCache; + + // For each text layer, add elements marked as active that haven't + // already been rendered, and remove those that are no longer active. + + for (var layerKey in cache) { + if (hasOwnProperty.call(cache, layerKey)) { + + var layer = this.getTextLayer(layerKey), + layerCache = cache[layerKey]; + + layer.hide(); + + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + + var positions = styleCache[key].positions; + + for (var i = 0, position; position = positions[i]; i++) { + if (position.active) { + if (!position.rendered) { + layer.append(position.element); + position.rendered = true; + } + } else { + positions.splice(i--, 1); + if (position.rendered) { + position.element.detach(); + } + } + } + + if (positions.length == 0) { + delete styleCache[key]; + } + } + } + } + } + + layer.show(); + } + } + }; + + // Creates (if necessary) and returns the text overlay container. + // + // @param {string} classes String of space-separated CSS classes used to + // uniquely identify the text layer. + // @return {object} The jQuery-wrapped text-layer div. + + Canvas.prototype.getTextLayer = function(classes) { + + var layer = this.text[classes]; + + // Create the text layer if it doesn't exist + + if (layer == null) { + + // Create the text layer container, if it doesn't exist + + if (this.textContainer == null) { + this.textContainer = $("<div class='flot-text'></div>") + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0, + 'font-size': "smaller", + color: "#545454" + }) + .insertAfter(this.element); + } + + layer = this.text[classes] = $("<div></div>") + .addClass(classes) + .css({ + position: "absolute", + top: 0, + left: 0, + bottom: 0, + right: 0 + }) + .appendTo(this.textContainer); + } + + return layer; + }; + + // Creates (if necessary) and returns a text info object. + // + // The object looks like this: + // + // { + // width: Width of the text's wrapper div. + // height: Height of the text's wrapper div. + // element: The jQuery-wrapped HTML div containing the text. + // positions: Array of positions at which this text is drawn. + // } + // + // The positions array contains objects that look like this: + // + // { + // active: Flag indicating whether the text should be visible. + // rendered: Flag indicating whether the text is currently visible. + // element: The jQuery-wrapped HTML div containing the text. + // x: X coordinate at which to draw the text. + // y: Y coordinate at which to draw the text. + // } + // + // Each position after the first receives a clone of the original element. + // + // The idea is that that the width, height, and general 'identity' of the + // text is constant no matter where it is placed; the placements are a + // secondary property. + // + // Canvas maintains a cache of recently-used text info objects; getTextInfo + // either returns the cached element or creates a new entry. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {string} text Text string to retrieve info for. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @return {object} a text info object. + + Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) { + + var textStyle, layerCache, styleCache, info; + + // Cast the value to a string, in case we were given a number or such + + text = "" + text; + + // If the font is a font-spec object, generate a CSS font definition + + if (typeof font === "object") { + textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family; + } else { + textStyle = font; + } + + // Retrieve (or create) the cache for the text's layer and styles + + layerCache = this._textCache[layer]; + + if (layerCache == null) { + layerCache = this._textCache[layer] = {}; + } + + styleCache = layerCache[textStyle]; + + if (styleCache == null) { + styleCache = layerCache[textStyle] = {}; + } + + info = styleCache[text]; + + // If we can't find a matching element in our cache, create a new one + + if (info == null) { + + var element = $("<div></div>").html(text) + .css({ + position: "absolute", + 'max-width': width, + top: -9999 + }) + .appendTo(this.getTextLayer(layer)); + + if (typeof font === "object") { + element.css({ + font: textStyle, + color: font.color + }); + } else if (typeof font === "string") { + element.addClass(font); + } + + info = styleCache[text] = { + width: element.outerWidth(true), + height: element.outerHeight(true), + element: element, + positions: [] + }; + + element.detach(); + } + + return info; + }; + + // Adds a text string to the canvas text overlay. + // + // The text isn't drawn immediately; it is marked as rendering, which will + // result in its addition to the canvas on the next render pass. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number} x X coordinate at which to draw the text. + // @param {number} y Y coordinate at which to draw the text. + // @param {string} text Text string to draw. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which to rotate the text, in degrees. + // Angle is currently unused, it will be implemented in the future. + // @param {number=} width Maximum width of the text before it wraps. + // @param {string=} halign Horizontal alignment of the text; either "left", + // "center" or "right". + // @param {string=} valign Vertical alignment of the text; either "top", + // "middle" or "bottom". + + Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) { + + var info = this.getTextInfo(layer, text, font, angle, width), + positions = info.positions; + + // Tweak the div's position to match the text's alignment + + if (halign == "center") { + x -= info.width / 2; + } else if (halign == "right") { + x -= info.width; + } + + if (valign == "middle") { + y -= info.height / 2; + } else if (valign == "bottom") { + y -= info.height; + } + + // Determine whether this text already exists at this position. + // If so, mark it for inclusion in the next render pass. + + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = true; + return; + } + } + + // If the text doesn't exist at this position, create a new entry + + // For the very first position we'll re-use the original element, + // while for subsequent ones we'll clone it. + + position = { + active: true, + rendered: false, + element: positions.length ? info.element.clone() : info.element, + x: x, + y: y + }; + + positions.push(position); + + // Move the element to its final position within the container + + position.element.css({ + top: Math.round(y), + left: Math.round(x), + 'text-align': halign // In case the text wraps + }); + }; + + // Removes one or more text strings from the canvas text overlay. + // + // If no parameters are given, all text within the layer is removed. + // + // Note that the text is not immediately removed; it is simply marked as + // inactive, which will result in its removal on the next render pass. + // This avoids the performance penalty for 'clear and redraw' behavior, + // where we potentially get rid of all text on a layer, but will likely + // add back most or all of it later, as when redrawing axes, for example. + // + // @param {string} layer A string of space-separated CSS classes uniquely + // identifying the layer containing this text. + // @param {number=} x X coordinate of the text. + // @param {number=} y Y coordinate of the text. + // @param {string=} text Text string to remove. + // @param {(string|object)=} font Either a string of space-separated CSS + // classes or a font-spec object, defining the text's font and style. + // @param {number=} angle Angle at which the text is rotated, in degrees. + // Angle is currently unused, it will be implemented in the future. + + Canvas.prototype.removeText = function(layer, x, y, text, font, angle) { + if (text == null) { + var layerCache = this._textCache[layer]; + if (layerCache != null) { + for (var styleKey in layerCache) { + if (hasOwnProperty.call(layerCache, styleKey)) { + var styleCache = layerCache[styleKey]; + for (var key in styleCache) { + if (hasOwnProperty.call(styleCache, key)) { + var positions = styleCache[key].positions; + for (var i = 0, position; position = positions[i]; i++) { + position.active = false; + } + } + } + } + } + } + } else { + var positions = this.getTextInfo(layer, text, font, angle).positions; + for (var i = 0, position; position = positions[i]; i++) { + if (position.x == x && position.y == y) { + position.active = false; + } + } + } + }; + + /////////////////////////////////////////////////////////////////////////// + // The top-level container for the entire plot. + + function Plot(placeholder, data_, options_, plugins) { + // data is on the form: + // [ series1, series2 ... ] + // where series is either just the data as [ [x1, y1], [x2, y2], ... ] + // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } + + var series = [], + options = { + // the color theme used for graphs + colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], + legend: { + show: true, + noColumns: 1, // number of colums in legend table + labelFormatter: null, // fn: string -> string + labelBoxBorderColor: "#ccc", // border color for the little label boxes + container: null, // container (as jQuery object) to put legend in, null means default on top of graph + position: "ne", // position of default legend container within plot + margin: 5, // distance from grid edge to default legend container within plot + backgroundColor: null, // null means auto-detect + backgroundOpacity: 0.85, // set to 0 to avoid background + sorted: null // default to no legend sorting + }, + xaxis: { + show: null, // null = auto-detect, true = always, false = never + position: "bottom", // or "top" + mode: null, // null or "time" + font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" } + color: null, // base color, labels, ticks + tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" + transform: null, // null or f: number -> number to transform axis + inverseTransform: null, // if transform is set, this should be the inverse function + min: null, // min. value to show, null means set automatically + max: null, // max. value to show, null means set automatically + autoscaleMargin: null, // margin in % to add if auto-setting min/max + ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks + tickFormatter: null, // fn: number -> string + labelWidth: null, // size of tick labels in pixels + labelHeight: null, + reserveSpace: null, // whether to reserve space even if axis isn't shown + tickLength: null, // size in pixels of ticks, or "full" for whole line + alignTicksWithAxis: null, // axis number or null for no sync + tickDecimals: null, // no. of decimals, null means auto + tickSize: null, // number or [number, "unit"] + minTickSize: null // number or [number, "unit"] + }, + yaxis: { + autoscaleMargin: 0.02, + position: "left" // or "right" + }, + xaxes: [], + yaxes: [], + series: { + points: { + show: false, + radius: 3, + lineWidth: 2, // in pixels + fill: true, + fillColor: "#ffffff", + symbol: "circle" // or callback + }, + lines: { + // we don't put in show: false so we can see + // whether lines were actively disabled + lineWidth: 2, // in pixels + fill: false, + fillColor: null, + steps: false + // Omit 'zero', so we can later default its value to + // match that of the 'fill' option. + }, + bars: { + show: false, + lineWidth: 2, // in pixels + barWidth: 1, // in units of the x axis + fill: true, + fillColor: null, + align: "left", // "left", "right", or "center" + horizontal: false, + zero: true + }, + shadowSize: 3, + highlightColor: null + }, + grid: { + show: true, + aboveData: false, + color: "#545454", // primary color used for outline and labels + backgroundColor: null, // null for transparent, else color + borderColor: null, // set if different from the grid color + tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" + margin: 0, // distance from the canvas edge to the grid + labelMargin: 5, // in pixels + axisMargin: 8, // in pixels + borderWidth: 2, // in pixels + minBorderMargin: null, // in pixels, null means taken from points radius + markings: null, // array of ranges or fn: axes -> array of ranges + markingsColor: "#f4f4f4", + markingsLineWidth: 2, + // interactive stuff + clickable: false, + hoverable: false, + autoHighlight: true, // highlight in case mouse is near + mouseActiveRadius: 10 // how far the mouse can be away to activate an item + }, + interaction: { + redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow + }, + hooks: {} + }, + surface = null, // the canvas for the plot itself + overlay = null, // canvas for interactive stuff on top of plot + eventHolder = null, // jQuery object that events should be bound to + ctx = null, octx = null, + xaxes = [], yaxes = [], + plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, + plotWidth = 0, plotHeight = 0, + hooks = { + processOptions: [], + processRawData: [], + processDatapoints: [], + processOffset: [], + drawBackground: [], + drawSeries: [], + draw: [], + bindEvents: [], + drawOverlay: [], + shutdown: [] + }, + plot = this; + + // public functions + plot.setData = setData; + plot.setupGrid = setupGrid; + plot.draw = draw; + plot.getPlaceholder = function() { return placeholder; }; + plot.getCanvas = function() { return surface.element; }; + plot.getPlotOffset = function() { return plotOffset; }; + plot.width = function () { return plotWidth; }; + plot.height = function () { return plotHeight; }; + plot.offset = function () { + var o = eventHolder.offset(); + o.left += plotOffset.left; + o.top += plotOffset.top; + return o; + }; + plot.getData = function () { return series; }; + plot.getAxes = function () { + var res = {}, i; + $.each(xaxes.concat(yaxes), function (_, axis) { + if (axis) + res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; + }); + return res; + }; + plot.getXAxes = function () { return xaxes; }; + plot.getYAxes = function () { return yaxes; }; + plot.c2p = canvasToAxisCoords; + plot.p2c = axisToCanvasCoords; + plot.getOptions = function () { return options; }; + plot.highlight = highlight; + plot.unhighlight = unhighlight; + plot.triggerRedrawOverlay = triggerRedrawOverlay; + plot.pointOffset = function(point) { + return { + left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10), + top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10) + }; + }; + plot.shutdown = shutdown; + plot.resize = function () { + var width = placeholder.width(), + height = placeholder.height(); + surface.resize(width, height); + overlay.resize(width, height); + }; + + // public attributes + plot.hooks = hooks; + + // initialize + initPlugins(plot); + parseOptions(options_); + setupCanvases(); + setData(data_); + setupGrid(); + draw(); + bindEvents(); + + + function executeHooks(hook, args) { + args = [plot].concat(args); + for (var i = 0; i < hook.length; ++i) + hook[i].apply(this, args); + } + + function initPlugins() { + + // References to key classes, allowing plugins to modify them + + var classes = { + Canvas: Canvas + }; + + for (var i = 0; i < plugins.length; ++i) { + var p = plugins[i]; + p.init(plot, classes); + if (p.options) + $.extend(true, options, p.options); + } + } + + function parseOptions(opts) { + + $.extend(true, options, opts); + + // $.extend merges arrays, rather than replacing them. When less + // colors are provided than the size of the default palette, we + // end up with those colors plus the remaining defaults, which is + // not expected behavior; avoid it by replacing them here. + + if (opts && opts.colors) { + options.colors = opts.colors; + } + + if (options.xaxis.color == null) + options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + if (options.yaxis.color == null) + options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility + options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color; + if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility + options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color; + + if (options.grid.borderColor == null) + options.grid.borderColor = options.grid.color; + if (options.grid.tickColor == null) + options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); + + // Fill in defaults for axis options, including any unspecified + // font-spec fields, if a font-spec was provided. + + // If no x/y axis options were provided, create one of each anyway, + // since the rest of the code assumes that they exist. + + var i, axisOptions, axisCount, + fontDefaults = { + style: placeholder.css("font-style"), + size: Math.round(0.8 * (+placeholder.css("font-size").replace("px", "") || 13)), + variant: placeholder.css("font-variant"), + weight: placeholder.css("font-weight"), + family: placeholder.css("font-family") + }; + + fontDefaults.lineHeight = fontDefaults.size * 1.15; + + axisCount = options.xaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.xaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.xaxis, axisOptions); + options.xaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + } + } + + axisCount = options.yaxes.length || 1; + for (i = 0; i < axisCount; ++i) { + + axisOptions = options.yaxes[i]; + if (axisOptions && !axisOptions.tickColor) { + axisOptions.tickColor = axisOptions.color; + } + + axisOptions = $.extend(true, {}, options.yaxis, axisOptions); + options.yaxes[i] = axisOptions; + + if (axisOptions.font) { + axisOptions.font = $.extend({}, fontDefaults, axisOptions.font); + if (!axisOptions.font.color) { + axisOptions.font.color = axisOptions.color; + } + } + } + + // backwards compatibility, to be removed in future + if (options.xaxis.noTicks && options.xaxis.ticks == null) + options.xaxis.ticks = options.xaxis.noTicks; + if (options.yaxis.noTicks && options.yaxis.ticks == null) + options.yaxis.ticks = options.yaxis.noTicks; + if (options.x2axis) { + options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); + options.xaxes[1].position = "top"; + } + if (options.y2axis) { + options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); + options.yaxes[1].position = "right"; + } + if (options.grid.coloredAreas) + options.grid.markings = options.grid.coloredAreas; + if (options.grid.coloredAreasColor) + options.grid.markingsColor = options.grid.coloredAreasColor; + if (options.lines) + $.extend(true, options.series.lines, options.lines); + if (options.points) + $.extend(true, options.series.points, options.points); + if (options.bars) + $.extend(true, options.series.bars, options.bars); + if (options.shadowSize != null) + options.series.shadowSize = options.shadowSize; + if (options.highlightColor != null) + options.series.highlightColor = options.highlightColor; + + // save options on axes for future reference + for (i = 0; i < options.xaxes.length; ++i) + getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; + for (i = 0; i < options.yaxes.length; ++i) + getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; + + // add hooks from options + for (var n in hooks) + if (options.hooks[n] && options.hooks[n].length) + hooks[n] = hooks[n].concat(options.hooks[n]); + + executeHooks(hooks.processOptions, [options]); + } + + function setData(d) { + series = parseData(d); + fillInSeriesOptions(); + processData(); + } + + function parseData(d) { + var res = []; + for (var i = 0; i < d.length; ++i) { + var s = $.extend(true, {}, options.series); + + if (d[i].data != null) { + s.data = d[i].data; // move the data instead of deep-copy + delete d[i].data; + + $.extend(true, s, d[i]); + + d[i].data = s.data; + } + else + s.data = d[i]; + res.push(s); + } + + return res; + } + + function axisNumber(obj, coord) { + var a = obj[coord + "axis"]; + if (typeof a == "object") // if we got a real axis, extract number + a = a.n; + if (typeof a != "number") + a = 1; // default to first axis + return a; + } + + function allAxes() { + // return flat array without annoying null entries + return $.grep(xaxes.concat(yaxes), function (a) { return a; }); + } + + function canvasToAxisCoords(pos) { + // return an object with x/y corresponding to all used axes + var res = {}, i, axis; + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) + res["x" + axis.n] = axis.c2p(pos.left); + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) + res["y" + axis.n] = axis.c2p(pos.top); + } + + if (res.x1 !== undefined) + res.x = res.x1; + if (res.y1 !== undefined) + res.y = res.y1; + + return res; + } + + function axisToCanvasCoords(pos) { + // get canvas coords from the first pair of x/y found in pos + var res = {}, i, axis, key; + + for (i = 0; i < xaxes.length; ++i) { + axis = xaxes[i]; + if (axis && axis.used) { + key = "x" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "x"; + + if (pos[key] != null) { + res.left = axis.p2c(pos[key]); + break; + } + } + } + + for (i = 0; i < yaxes.length; ++i) { + axis = yaxes[i]; + if (axis && axis.used) { + key = "y" + axis.n; + if (pos[key] == null && axis.n == 1) + key = "y"; + + if (pos[key] != null) { + res.top = axis.p2c(pos[key]); + break; + } + } + } + + return res; + } + + function getOrCreateAxis(axes, number) { + if (!axes[number - 1]) + axes[number - 1] = { + n: number, // save the number for future reference + direction: axes == xaxes ? "x" : "y", + options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) + }; + + return axes[number - 1]; + } + + function fillInSeriesOptions() { + + var neededColors = series.length, maxIndex = -1, i; + + // Subtract the number of series that already have fixed colors or + // color indexes from the number that we still need to generate. + + for (i = 0; i < series.length; ++i) { + var sc = series[i].color; + if (sc != null) { + neededColors--; + if (typeof sc == "number" && sc > maxIndex) { + maxIndex = sc; + } + } + } + + // If any of the series have fixed color indexes, then we need to + // generate at least as many colors as the highest index. + + if (neededColors <= maxIndex) { + neededColors = maxIndex + 1; + } + + // Generate all the colors, using first the option colors and then + // variations on those colors once they're exhausted. + + var c, colors = [], colorPool = options.colors, + colorPoolSize = colorPool.length, variation = 0; + + for (i = 0; i < neededColors; i++) { + + c = $.color.parse(colorPool[i % colorPoolSize] || "#666"); + + // Each time we exhaust the colors in the pool we adjust + // a scaling factor used to produce more variations on + // those colors. The factor alternates negative/positive + // to produce lighter/darker colors. + + // Reset the variation after every few cycles, or else + // it will end up producing only white or black colors. + + if (i % colorPoolSize == 0 && i) { + if (variation >= 0) { + if (variation < 0.5) { + variation = -variation - 0.2; + } else variation = 0; + } else variation = -variation; + } + + colors[i] = c.scale('rgb', 1 + variation); + } + + // Finalize the series options, filling in their colors + + var colori = 0, s; + for (i = 0; i < series.length; ++i) { + s = series[i]; + + // assign colors + if (s.color == null) { + s.color = colors[colori].toString(); + ++colori; + } + else if (typeof s.color == "number") + s.color = colors[s.color].toString(); + + // turn on lines automatically in case nothing is set + if (s.lines.show == null) { + var v, show = true; + for (v in s) + if (s[v] && s[v].show) { + show = false; + break; + } + if (show) + s.lines.show = true; + } + + // If nothing was provided for lines.zero, default it to match + // lines.fill, since areas by default should extend to zero. + + if (s.lines.zero == null) { + s.lines.zero = !!s.lines.fill; + } + + // setup axes + s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); + s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); + } + } + + function processData() { + var topSentry = Number.POSITIVE_INFINITY, + bottomSentry = Number.NEGATIVE_INFINITY, + fakeInfinity = Number.MAX_VALUE, + i, j, k, m, length, + s, points, ps, x, y, axis, val, f, p, + data, format; + + function updateAxis(axis, min, max) { + if (min < axis.datamin && min != -fakeInfinity) + axis.datamin = min; + if (max > axis.datamax && max != fakeInfinity) + axis.datamax = max; + } + + $.each(allAxes(), function (_, axis) { + // init axis + axis.datamin = topSentry; + axis.datamax = bottomSentry; + axis.used = false; + }); + + for (i = 0; i < series.length; ++i) { + s = series[i]; + s.datapoints = { points: [] }; + + executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); + } + + // first pass: clean and copy data + for (i = 0; i < series.length; ++i) { + s = series[i]; + + data = s.data; + format = s.datapoints.format; + + if (!format) { + format = []; + // find out how to copy + format.push({ x: true, number: true, required: true }); + format.push({ y: true, number: true, required: true }); + + if (s.bars.show || (s.lines.show && s.lines.fill)) { + var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero)); + format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale }); + if (s.bars.horizontal) { + delete format[format.length - 1].y; + format[format.length - 1].x = true; + } + } + + s.datapoints.format = format; + } + + if (s.datapoints.pointsize != null) + continue; // already filled in + + s.datapoints.pointsize = format.length; + + ps = s.datapoints.pointsize; + points = s.datapoints.points; + + var insertSteps = s.lines.show && s.lines.steps; + s.xaxis.used = s.yaxis.used = true; + + for (j = k = 0; j < data.length; ++j, k += ps) { + p = data[j]; + + var nullify = p == null; + if (!nullify) { + for (m = 0; m < ps; ++m) { + val = p[m]; + f = format[m]; + + if (f) { + if (f.number && val != null) { + val = +val; // convert to number + if (isNaN(val)) + val = null; + else if (val == Infinity) + val = fakeInfinity; + else if (val == -Infinity) + val = -fakeInfinity; + } + + if (val == null) { + if (f.required) + nullify = true; + + if (f.defaultValue != null) + val = f.defaultValue; + } + } + + points[k + m] = val; + } + } + + if (nullify) { + for (m = 0; m < ps; ++m) { + val = points[k + m]; + if (val != null) { + f = format[m]; + // extract min/max info + if (f.autoscale !== false) { + if (f.x) { + updateAxis(s.xaxis, val, val); + } + if (f.y) { + updateAxis(s.yaxis, val, val); + } + } + } + points[k + m] = null; + } + } + else { + // a little bit of line specific stuff that + // perhaps shouldn't be here, but lacking + // better means... + if (insertSteps && k > 0 + && points[k - ps] != null + && points[k - ps] != points[k] + && points[k - ps + 1] != points[k + 1]) { + // copy the point to make room for a middle point + for (m = 0; m < ps; ++m) + points[k + ps + m] = points[k + m]; + + // middle point has same y + points[k + 1] = points[k - ps + 1]; + + // we've added a point, better reflect that + k += ps; + } + } + } + } + + // give the hooks a chance to run + for (i = 0; i < series.length; ++i) { + s = series[i]; + + executeHooks(hooks.processDatapoints, [ s, s.datapoints]); + } + + // second pass: find datamax/datamin for auto-scaling + for (i = 0; i < series.length; ++i) { + s = series[i]; + points = s.datapoints.points; + ps = s.datapoints.pointsize; + format = s.datapoints.format; + + var xmin = topSentry, ymin = topSentry, + xmax = bottomSentry, ymax = bottomSentry; + + for (j = 0; j < points.length; j += ps) { + if (points[j] == null) + continue; + + for (m = 0; m < ps; ++m) { + val = points[j + m]; + f = format[m]; + if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity) + continue; + + if (f.x) { + if (val < xmin) + xmin = val; + if (val > xmax) + xmax = val; + } + if (f.y) { + if (val < ymin) + ymin = val; + if (val > ymax) + ymax = val; + } + } + } + + if (s.bars.show) { + // make sure we got room for the bar on the dancing floor + var delta; + + switch (s.bars.align) { + case "left": + delta = 0; + break; + case "right": + delta = -s.bars.barWidth; + break; + default: + delta = -s.bars.barWidth / 2; + } + + if (s.bars.horizontal) { + ymin += delta; + ymax += delta + s.bars.barWidth; + } + else { + xmin += delta; + xmax += delta + s.bars.barWidth; + } + } + + updateAxis(s.xaxis, xmin, xmax); + updateAxis(s.yaxis, ymin, ymax); + } + + $.each(allAxes(), function (_, axis) { + if (axis.datamin == topSentry) + axis.datamin = null; + if (axis.datamax == bottomSentry) + axis.datamax = null; + }); + } + + function setupCanvases() { + + // Make sure the placeholder is clear of everything except canvases + // from a previous plot in this container that we'll try to re-use. + + placeholder.css("padding", 0) // padding messes up the positioning + .children(":not(.flot-base,.flot-overlay)").remove(); + + if (placeholder.css("position") == 'static') + placeholder.css("position", "relative"); // for positioning labels and overlay + + surface = new Canvas("flot-base", placeholder); + overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features + + ctx = surface.context; + octx = overlay.context; + + // define which element we're listening for events on + eventHolder = $(overlay.element).unbind(); + + // If we're re-using a plot object, shut down the old one + + var existing = placeholder.data("plot"); + + if (existing) { + existing.shutdown(); + overlay.clear(); + } + + // save in case we get replotted + placeholder.data("plot", plot); + } + + function bindEvents() { + // bind events + if (options.grid.hoverable) { + eventHolder.mousemove(onMouseMove); + + // Use bind, rather than .mouseleave, because we officially + // still support jQuery 1.2.6, which doesn't define a shortcut + // for mouseenter or mouseleave. This was a bug/oversight that + // was fixed somewhere around 1.3.x. We can return to using + // .mouseleave when we drop support for 1.2.6. + + eventHolder.bind("mouseleave", onMouseLeave); + } + + if (options.grid.clickable) + eventHolder.click(onClick); + + executeHooks(hooks.bindEvents, [eventHolder]); + } + + function shutdown() { + if (redrawTimeout) + clearTimeout(redrawTimeout); + + eventHolder.unbind("mousemove", onMouseMove); + eventHolder.unbind("mouseleave", onMouseLeave); + eventHolder.unbind("click", onClick); + + executeHooks(hooks.shutdown, [eventHolder]); + } + + function setTransformationHelpers(axis) { + // set helper functions on the axis, assumes plot area + // has been computed already + + function identity(x) { return x; } + + var s, m, t = axis.options.transform || identity, + it = axis.options.inverseTransform; + + // precompute how much the axis is scaling a point + // in canvas space + if (axis.direction == "x") { + s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); + m = Math.min(t(axis.max), t(axis.min)); + } + else { + s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); + s = -s; + m = Math.max(t(axis.max), t(axis.min)); + } + + // data point to canvas coordinate + if (t == identity) // slight optimization + axis.p2c = function (p) { return (p - m) * s; }; + else + axis.p2c = function (p) { return (t(p) - m) * s; }; + // canvas coordinate to data point + if (!it) + axis.c2p = function (c) { return m + c / s; }; + else + axis.c2p = function (c) { return it(m + c / s); }; + } + + function measureTickLabels(axis) { + + var opts = axis.options, + ticks = axis.ticks || [], + labelWidth = opts.labelWidth || 0, + labelHeight = opts.labelHeight || 0, + maxWidth = labelWidth || axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = opts.font || "flot-tick-label tickLabel"; + + for (var i = 0; i < ticks.length; ++i) { + + var t = ticks[i]; + + if (!t.label) + continue; + + var info = surface.getTextInfo(layer, t.label, font, null, maxWidth); + + labelWidth = Math.max(labelWidth, info.width); + labelHeight = Math.max(labelHeight, info.height); + } + + axis.labelWidth = opts.labelWidth || labelWidth; + axis.labelHeight = opts.labelHeight || labelHeight; + } + + function allocateAxisBoxFirstPhase(axis) { + // find the bounding box of the axis by looking at label + // widths/heights and ticks, make room by diminishing the + // plotOffset; this first phase only looks at one + // dimension per axis, the other dimension depends on the + // other axes so will have to wait + + var lw = axis.labelWidth, + lh = axis.labelHeight, + pos = axis.options.position, + tickLength = axis.options.tickLength, + axisMargin = options.grid.axisMargin, + padding = options.grid.labelMargin, + all = axis.direction == "x" ? xaxes : yaxes, + index, innermost; + + // determine axis margin + var samePosition = $.grep(all, function (a) { + return a && a.options.position == pos && a.reserveSpace; + }); + if ($.inArray(axis, samePosition) == samePosition.length - 1) + axisMargin = 0; // outermost + + // Determine whether the axis is the first (innermost) on its side + + innermost = $.inArray(axis, samePosition) == 0; + + // determine tick length - if we're innermost, we can use "full" + + if (tickLength == null) { + if (innermost) + tickLength = "full"; + else + tickLength = 5; + } + + if (!isNaN(+tickLength)) + padding += +tickLength; + + // compute box + if (axis.direction == "x") { + lh += padding; + + if (pos == "bottom") { + plotOffset.bottom += lh + axisMargin; + axis.box = { top: surface.height - plotOffset.bottom, height: lh }; + } + else { + axis.box = { top: plotOffset.top + axisMargin, height: lh }; + plotOffset.top += lh + axisMargin; + } + } + else { + lw += padding; + + if (pos == "left") { + axis.box = { left: plotOffset.left + axisMargin, width: lw }; + plotOffset.left += lw + axisMargin; + } + else { + plotOffset.right += lw + axisMargin; + axis.box = { left: surface.width - plotOffset.right, width: lw }; + } + } + + // save for future reference + axis.position = pos; + axis.tickLength = tickLength; + axis.box.padding = padding; + axis.innermost = innermost; + } + + function allocateAxisBoxSecondPhase(axis) { + // now that all axis boxes have been placed in one + // dimension, we can set the remaining dimension coordinates + if (axis.direction == "x") { + axis.box.left = plotOffset.left - axis.labelWidth / 2; + axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth; + } + else { + axis.box.top = plotOffset.top - axis.labelHeight / 2; + axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight; + } + } + + function adjustLayoutForThingsStickingOut() { + // possibly adjust plot offset to ensure everything stays + // inside the canvas and isn't clipped off + + var minMargin = options.grid.minBorderMargin, + margins = { x: 0, y: 0 }, i, axis; + + // check stuff from the plot (FIXME: this should just read + // a value from the series, otherwise it's impossible to + // customize) + if (minMargin == null) { + minMargin = 0; + for (i = 0; i < series.length; ++i) + minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2)); + } + + margins.x = margins.y = Math.ceil(minMargin); + + // check axis labels, note we don't check the actual + // labels but instead use the overall width/height to not + // jump as much around with replots + $.each(allAxes(), function (_, axis) { + var dir = axis.direction; + if (axis.reserveSpace) + margins[dir] = Math.ceil(Math.max(margins[dir], (dir == "x" ? axis.labelWidth : axis.labelHeight) / 2)); + }); + + plotOffset.left = Math.max(margins.x, plotOffset.left); + plotOffset.right = Math.max(margins.x, plotOffset.right); + plotOffset.top = Math.max(margins.y, plotOffset.top); + plotOffset.bottom = Math.max(margins.y, plotOffset.bottom); + } + + function setupGrid() { + var i, axes = allAxes(), showGrid = options.grid.show; + + // Initialize the plot's offset from the edge of the canvas + + for (var a in plotOffset) { + var margin = options.grid.margin || 0; + plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0; + } + + executeHooks(hooks.processOffset, [plotOffset]); + + // If the grid is visible, add its border width to the offset + + for (var a in plotOffset) { + if(typeof(options.grid.borderWidth) == "object") { + plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0; + } + else { + plotOffset[a] += showGrid ? options.grid.borderWidth : 0; + } + } + + // init axes + $.each(axes, function (_, axis) { + axis.show = axis.options.show; + if (axis.show == null) + axis.show = axis.used; // by default an axis is visible if it's got data + + axis.reserveSpace = axis.show || axis.options.reserveSpace; + + setRange(axis); + }); + + if (showGrid) { + + var allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; }); + + $.each(allocatedAxes, function (_, axis) { + // make the ticks + setupTickGeneration(axis); + setTicks(axis); + snapRangeToTicks(axis, axis.ticks); + // find labelWidth/Height for axis + measureTickLabels(axis); + }); + + // with all dimensions calculated, we can compute the + // axis bounding boxes, start from the outside + // (reverse order) + for (i = allocatedAxes.length - 1; i >= 0; --i) + allocateAxisBoxFirstPhase(allocatedAxes[i]); + + // make sure we've got enough space for things that + // might stick out + adjustLayoutForThingsStickingOut(); + + $.each(allocatedAxes, function (_, axis) { + allocateAxisBoxSecondPhase(axis); + }); + } + + plotWidth = surface.width - plotOffset.left - plotOffset.right; + plotHeight = surface.height - plotOffset.bottom - plotOffset.top; + + // now we got the proper plot dimensions, we can compute the scaling + $.each(axes, function (_, axis) { + setTransformationHelpers(axis); + }); + + if (showGrid) { + drawAxisLabels(); + } + + insertLegend(); + } + + function setRange(axis) { + var opts = axis.options, + min = +(opts.min != null ? opts.min : axis.datamin), + max = +(opts.max != null ? opts.max : axis.datamax), + delta = max - min; + + if (delta == 0.0) { + // degenerate case + var widen = max == 0 ? 1 : 0.01; + + if (opts.min == null) + min -= widen; + // always widen max if we couldn't widen min to ensure we + // don't fall into min == max which doesn't work + if (opts.max == null || opts.min != null) + max += widen; + } + else { + // consider autoscaling + var margin = opts.autoscaleMargin; + if (margin != null) { + if (opts.min == null) { + min -= delta * margin; + // make sure we don't go below zero if all values + // are positive + if (min < 0 && axis.datamin != null && axis.datamin >= 0) + min = 0; + } + if (opts.max == null) { + max += delta * margin; + if (max > 0 && axis.datamax != null && axis.datamax <= 0) + max = 0; + } + } + } + axis.min = min; + axis.max = max; + } + + function setupTickGeneration(axis) { + var opts = axis.options; + + // estimate number of ticks + var noTicks; + if (typeof opts.ticks == "number" && opts.ticks > 0) + noTicks = opts.ticks; + else + // heuristic based on the model a*sqrt(x) fitted to + // some data points that seemed reasonable + noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height); + + var delta = (axis.max - axis.min) / noTicks, + dec = -Math.floor(Math.log(delta) / Math.LN10), + maxDec = opts.tickDecimals; + + if (maxDec != null && dec > maxDec) { + dec = maxDec; + } + + var magn = Math.pow(10, -dec), + norm = delta / magn, // norm is between 1.0 and 10.0 + size; + + if (norm < 1.5) { + size = 1; + } else if (norm < 3) { + size = 2; + // special case for 2.5, requires an extra decimal + if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { + size = 2.5; + ++dec; + } + } else if (norm < 7.5) { + size = 5; + } else { + size = 10; + } + + size *= magn; + + if (opts.minTickSize != null && size < opts.minTickSize) { + size = opts.minTickSize; + } + + axis.delta = delta; + axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); + axis.tickSize = opts.tickSize || size; + + // Time mode was moved to a plug-in in 0.8, but since so many people use this + // we'll add an especially friendly make sure they remembered to include it. + + if (opts.mode == "time" && !axis.tickGenerator) { + throw new Error("Time mode requires the flot.time plugin."); + } + + // Flot supports base-10 axes; any other mode else is handled by a plug-in, + // like flot.time.js. + + if (!axis.tickGenerator) { + + axis.tickGenerator = function (axis) { + + var ticks = [], + start = floorInBase(axis.min, axis.tickSize), + i = 0, + v = Number.NaN, + prev; + + do { + prev = v; + v = start + i * axis.tickSize; + ticks.push(v); + ++i; + } while (v < axis.max && v != prev); + return ticks; + }; + + axis.tickFormatter = function (value, axis) { + + var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1; + var formatted = "" + Math.round(value * factor) / factor; + + // If tickDecimals was specified, ensure that we have exactly that + // much precision; otherwise default to the value's own precision. + + if (axis.tickDecimals != null) { + var decimal = formatted.indexOf("."); + var precision = decimal == -1 ? 0 : formatted.length - decimal - 1; + if (precision < axis.tickDecimals) { + return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision); + } + } + + return formatted; + }; + } + + if ($.isFunction(opts.tickFormatter)) + axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; + + if (opts.alignTicksWithAxis != null) { + var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; + if (otherAxis && otherAxis.used && otherAxis != axis) { + // consider snapping min/max to outermost nice ticks + var niceTicks = axis.tickGenerator(axis); + if (niceTicks.length > 0) { + if (opts.min == null) + axis.min = Math.min(axis.min, niceTicks[0]); + if (opts.max == null && niceTicks.length > 1) + axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); + } + + axis.tickGenerator = function (axis) { + // copy ticks, scaled to this axis + var ticks = [], v, i; + for (i = 0; i < otherAxis.ticks.length; ++i) { + v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); + v = axis.min + v * (axis.max - axis.min); + ticks.push(v); + } + return ticks; + }; + + // we might need an extra decimal since forced + // ticks don't necessarily fit naturally + if (!axis.mode && opts.tickDecimals == null) { + var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1), + ts = axis.tickGenerator(axis); + + // only proceed if the tick interval rounded + // with an extra decimal doesn't give us a + // zero at end + if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) + axis.tickDecimals = extraDec; + } + } + } + } + + function setTicks(axis) { + var oticks = axis.options.ticks, ticks = []; + if (oticks == null || (typeof oticks == "number" && oticks > 0)) + ticks = axis.tickGenerator(axis); + else if (oticks) { + if ($.isFunction(oticks)) + // generate the ticks + ticks = oticks(axis); + else + ticks = oticks; + } + + // clean up/labelify the supplied ticks, copy them over + var i, v; + axis.ticks = []; + for (i = 0; i < ticks.length; ++i) { + var label = null; + var t = ticks[i]; + if (typeof t == "object") { + v = +t[0]; + if (t.length > 1) + label = t[1]; + } + else + v = +t; + if (label == null) + label = axis.tickFormatter(v, axis); + if (!isNaN(v)) + axis.ticks.push({ v: v, label: label }); + } + } + + function snapRangeToTicks(axis, ticks) { + if (axis.options.autoscaleMargin && ticks.length > 0) { + // snap to ticks + if (axis.options.min == null) + axis.min = Math.min(axis.min, ticks[0].v); + if (axis.options.max == null && ticks.length > 1) + axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); + } + } + + function draw() { + + surface.clear(); + + executeHooks(hooks.drawBackground, [ctx]); + + var grid = options.grid; + + // draw background, if any + if (grid.show && grid.backgroundColor) + drawBackground(); + + if (grid.show && !grid.aboveData) { + drawGrid(); + } + + for (var i = 0; i < series.length; ++i) { + executeHooks(hooks.drawSeries, [ctx, series[i]]); + drawSeries(series[i]); + } + + executeHooks(hooks.draw, [ctx]); + + if (grid.show && grid.aboveData) { + drawGrid(); + } + + surface.render(); + + // A draw implies that either the axes or data have changed, so we + // should probably update the overlay highlights as well. + + triggerRedrawOverlay(); + } + + function extractRange(ranges, coord) { + var axis, from, to, key, axes = allAxes(); + + for (var i = 0; i < axes.length; ++i) { + axis = axes[i]; + if (axis.direction == coord) { + key = coord + axis.n + "axis"; + if (!ranges[key] && axis.n == 1) + key = coord + "axis"; // support x1axis as xaxis + if (ranges[key]) { + from = ranges[key].from; + to = ranges[key].to; + break; + } + } + } + + // backwards-compat stuff - to be removed in future + if (!ranges[key]) { + axis = coord == "x" ? xaxes[0] : yaxes[0]; + from = ranges[coord + "1"]; + to = ranges[coord + "2"]; + } + + // auto-reverse as an added bonus + if (from != null && to != null && from > to) { + var tmp = from; + from = to; + to = tmp; + } + + return { from: from, to: to, axis: axis }; + } + + function drawBackground() { + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); + ctx.fillRect(0, 0, plotWidth, plotHeight); + ctx.restore(); + } + + function drawGrid() { + var i, axes, bw, bc; + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // draw markings + var markings = options.grid.markings; + if (markings) { + if ($.isFunction(markings)) { + axes = plot.getAxes(); + // xmin etc. is backwards compatibility, to be + // removed in the future + axes.xmin = axes.xaxis.min; + axes.xmax = axes.xaxis.max; + axes.ymin = axes.yaxis.min; + axes.ymax = axes.yaxis.max; + + markings = markings(axes); + } + + for (i = 0; i < markings.length; ++i) { + var m = markings[i], + xrange = extractRange(m, "x"), + yrange = extractRange(m, "y"); + + // fill in missing + if (xrange.from == null) + xrange.from = xrange.axis.min; + if (xrange.to == null) + xrange.to = xrange.axis.max; + if (yrange.from == null) + yrange.from = yrange.axis.min; + if (yrange.to == null) + yrange.to = yrange.axis.max; + + // clip + if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || + yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) + continue; + + xrange.from = Math.max(xrange.from, xrange.axis.min); + xrange.to = Math.min(xrange.to, xrange.axis.max); + yrange.from = Math.max(yrange.from, yrange.axis.min); + yrange.to = Math.min(yrange.to, yrange.axis.max); + + if (xrange.from == xrange.to && yrange.from == yrange.to) + continue; + + // then draw + xrange.from = xrange.axis.p2c(xrange.from); + xrange.to = xrange.axis.p2c(xrange.to); + yrange.from = yrange.axis.p2c(yrange.from); + yrange.to = yrange.axis.p2c(yrange.to); + + if (xrange.from == xrange.to || yrange.from == yrange.to) { + // draw line + ctx.beginPath(); + ctx.strokeStyle = m.color || options.grid.markingsColor; + ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth; + ctx.moveTo(xrange.from, yrange.from); + ctx.lineTo(xrange.to, yrange.to); + ctx.stroke(); + } + else { + // fill area + ctx.fillStyle = m.color || options.grid.markingsColor; + ctx.fillRect(xrange.from, yrange.to, + xrange.to - xrange.from, + yrange.from - yrange.to); + } + } + } + + // draw the ticks + axes = allAxes(); + bw = options.grid.borderWidth; + + for (var j = 0; j < axes.length; ++j) { + var axis = axes[j], box = axis.box, + t = axis.tickLength, x, y, xoff, yoff; + if (!axis.show || axis.ticks.length == 0) + continue; + + ctx.lineWidth = 1; + + // find the edges + if (axis.direction == "x") { + x = 0; + if (t == "full") + y = (axis.position == "top" ? 0 : plotHeight); + else + y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); + } + else { + y = 0; + if (t == "full") + x = (axis.position == "left" ? 0 : plotWidth); + else + x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); + } + + // draw tick bar + if (!axis.innermost) { + ctx.strokeStyle = axis.options.color; + ctx.beginPath(); + xoff = yoff = 0; + if (axis.direction == "x") + xoff = plotWidth + 1; + else + yoff = plotHeight + 1; + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") { + y = Math.floor(y) + 0.5; + } else { + x = Math.floor(x) + 0.5; + } + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + ctx.stroke(); + } + + // draw ticks + + ctx.strokeStyle = axis.options.tickColor; + + ctx.beginPath(); + for (i = 0; i < axis.ticks.length; ++i) { + var v = axis.ticks[i].v; + + xoff = yoff = 0; + + if (isNaN(v) || v < axis.min || v > axis.max + // skip those lying on the axes if we got a border + || (t == "full" + && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0) + && (v == axis.min || v == axis.max))) + continue; + + if (axis.direction == "x") { + x = axis.p2c(v); + yoff = t == "full" ? -plotHeight : t; + + if (axis.position == "top") + yoff = -yoff; + } + else { + y = axis.p2c(v); + xoff = t == "full" ? -plotWidth : t; + + if (axis.position == "left") + xoff = -xoff; + } + + if (ctx.lineWidth == 1) { + if (axis.direction == "x") + x = Math.floor(x) + 0.5; + else + y = Math.floor(y) + 0.5; + } + + ctx.moveTo(x, y); + ctx.lineTo(x + xoff, y + yoff); + } + + ctx.stroke(); + } + + + // draw border + if (bw) { + // If either borderWidth or borderColor is an object, then draw the border + // line by line instead of as one rectangle + bc = options.grid.borderColor; + if(typeof bw == "object" || typeof bc == "object") { + if (typeof bw !== "object") { + bw = {top: bw, right: bw, bottom: bw, left: bw}; + } + if (typeof bc !== "object") { + bc = {top: bc, right: bc, bottom: bc, left: bc}; + } + + if (bw.top > 0) { + ctx.strokeStyle = bc.top; + ctx.lineWidth = bw.top; + ctx.beginPath(); + ctx.moveTo(0 - bw.left, 0 - bw.top/2); + ctx.lineTo(plotWidth, 0 - bw.top/2); + ctx.stroke(); + } + + if (bw.right > 0) { + ctx.strokeStyle = bc.right; + ctx.lineWidth = bw.right; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top); + ctx.lineTo(plotWidth + bw.right / 2, plotHeight); + ctx.stroke(); + } + + if (bw.bottom > 0) { + ctx.strokeStyle = bc.bottom; + ctx.lineWidth = bw.bottom; + ctx.beginPath(); + ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2); + ctx.lineTo(0, plotHeight + bw.bottom / 2); + ctx.stroke(); + } + + if (bw.left > 0) { + ctx.strokeStyle = bc.left; + ctx.lineWidth = bw.left; + ctx.beginPath(); + ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom); + ctx.lineTo(0- bw.left/2, 0); + ctx.stroke(); + } + } + else { + ctx.lineWidth = bw; + ctx.strokeStyle = options.grid.borderColor; + ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); + } + } + + ctx.restore(); + } + + function drawAxisLabels() { + + $.each(allAxes(), function (_, axis) { + var box = axis.box, + legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis", + layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles, + font = axis.options.font || "flot-tick-label tickLabel", + tick, x, y, halign, valign; + + // Remove text before checking for axis.show and ticks.length; + // otherwise plugins, like flot-tickrotor, that draw their own + // tick labels will end up with both theirs and the defaults. + + surface.removeText(layer); + + if (!axis.show || axis.ticks.length == 0) + return; + + for (var i = 0; i < axis.ticks.length; ++i) { + + tick = axis.ticks[i]; + if (!tick.label || tick.v < axis.min || tick.v > axis.max) + continue; + + if (axis.direction == "x") { + halign = "center"; + x = plotOffset.left + axis.p2c(tick.v); + if (axis.position == "bottom") { + y = box.top + box.padding; + } else { + y = box.top + box.height - box.padding; + valign = "bottom"; + } + } else { + valign = "middle"; + y = plotOffset.top + axis.p2c(tick.v); + if (axis.position == "left") { + x = box.left + box.width - box.padding; + halign = "right"; + } else { + x = box.left + box.padding; + } + } + + surface.addText(layer, x, y, tick.label, font, null, null, halign, valign); + } + }); + } + + function drawSeries(series) { + if (series.lines.show) + drawSeriesLines(series); + if (series.bars.show) + drawSeriesBars(series); + if (series.points.show) + drawSeriesPoints(series); + } + + function drawSeriesLines(series) { + function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + prevx = null, prevy = null; + + ctx.beginPath(); + for (var i = ps; i < points.length; i += ps) { + var x1 = points[i - ps], y1 = points[i - ps + 1], + x2 = points[i], y2 = points[i + 1]; + + if (x1 == null || x2 == null) + continue; + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min) { + if (y2 < axisy.min) + continue; // line segment is outside + // compute new intersection point + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min) { + if (y1 < axisy.min) + continue; + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max) { + if (y2 > axisy.max) + continue; + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max) { + if (y1 > axisy.max) + continue; + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (x1 != prevx || y1 != prevy) + ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); + + prevx = x2; + prevy = y2; + ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); + } + ctx.stroke(); + } + + function plotLineArea(datapoints, axisx, axisy) { + var points = datapoints.points, + ps = datapoints.pointsize, + bottom = Math.min(Math.max(0, axisy.min), axisy.max), + i = 0, top, areaOpen = false, + ypos = 1, segmentStart = 0, segmentEnd = 0; + + // we process each segment in two turns, first forward + // direction to sketch out top, then once we hit the + // end we go backwards to sketch the bottom + while (true) { + if (ps > 0 && i > points.length + ps) + break; + + i += ps; // ps is negative if going backwards + + var x1 = points[i - ps], + y1 = points[i - ps + ypos], + x2 = points[i], y2 = points[i + ypos]; + + if (areaOpen) { + if (ps > 0 && x1 != null && x2 == null) { + // at turning point + segmentEnd = i; + ps = -ps; + ypos = 2; + continue; + } + + if (ps < 0 && i == segmentStart + ps) { + // done with the reverse sweep + ctx.fill(); + areaOpen = false; + ps = -ps; + ypos = 1; + i = segmentStart = segmentEnd + ps; + continue; + } + } + + if (x1 == null || x2 == null) + continue; + + // clip x values + + // clip with xmin + if (x1 <= x2 && x1 < axisx.min) { + if (x2 < axisx.min) + continue; + y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.min; + } + else if (x2 <= x1 && x2 < axisx.min) { + if (x1 < axisx.min) + continue; + y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.min; + } + + // clip with xmax + if (x1 >= x2 && x1 > axisx.max) { + if (x2 > axisx.max) + continue; + y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x1 = axisx.max; + } + else if (x2 >= x1 && x2 > axisx.max) { + if (x1 > axisx.max) + continue; + y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; + x2 = axisx.max; + } + + if (!areaOpen) { + // open area + ctx.beginPath(); + ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); + areaOpen = true; + } + + // now first check the case where both is outside + if (y1 >= axisy.max && y2 >= axisy.max) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); + continue; + } + else if (y1 <= axisy.min && y2 <= axisy.min) { + ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); + continue; + } + + // else it's a bit more complicated, there might + // be a flat maxed out rectangle first, then a + // triangular cutout or reverse; to find these + // keep track of the current x values + var x1old = x1, x2old = x2; + + // clip the y values, without shortcutting, we + // go through all cases in turn + + // clip with ymin + if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { + x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.min; + } + else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { + x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.min; + } + + // clip with ymax + if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { + x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y1 = axisy.max; + } + else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { + x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; + y2 = axisy.max; + } + + // if the x value was changed we got a rectangle + // to fill + if (x1 != x1old) { + ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); + // it goes to (x1, y1), but we fill that below + } + + // fill triangular section, this sometimes result + // in redundant points if (x1, y1) hasn't changed + // from previous line to, but we just ignore that + ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + + // fill the other rectangle if it's there + if (x2 != x2old) { + ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); + ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); + } + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + ctx.lineJoin = "round"; + + var lw = series.lines.lineWidth, + sw = series.shadowSize; + // FIXME: consider another form of shadow when filling is turned on + if (lw > 0 && sw > 0) { + // draw shadow as a thick and thin line with transparency + ctx.lineWidth = sw; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + // position shadow at angle from the mid of line + var angle = Math.PI/18; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); + ctx.lineWidth = sw/2; + plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); + if (fillStyle) { + ctx.fillStyle = fillStyle; + plotLineArea(series.datapoints, series.xaxis, series.yaxis); + } + + if (lw > 0) + plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); + ctx.restore(); + } + + function drawSeriesPoints(series) { + function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + var x = points[i], y = points[i + 1]; + if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + continue; + + ctx.beginPath(); + x = axisx.p2c(x); + y = axisy.p2c(y) + offset; + if (symbol == "circle") + ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); + else + symbol(ctx, x, y, radius, shadow); + ctx.closePath(); + + if (fillStyle) { + ctx.fillStyle = fillStyle; + ctx.fill(); + } + ctx.stroke(); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + var lw = series.points.lineWidth, + sw = series.shadowSize, + radius = series.points.radius, + symbol = series.points.symbol; + + // If the user sets the line width to 0, we change it to a very + // small value. A line width of 0 seems to force the default of 1. + // Doing the conditional here allows the shadow setting to still be + // optional even with a lineWidth of 0. + + if( lw == 0 ) + lw = 0.0001; + + if (lw > 0 && sw > 0) { + // draw shadow in two steps + var w = sw / 2; + ctx.lineWidth = w; + ctx.strokeStyle = "rgba(0,0,0,0.1)"; + plotPoints(series.datapoints, radius, null, w + w/2, true, + series.xaxis, series.yaxis, symbol); + + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + plotPoints(series.datapoints, radius, null, w/2, true, + series.xaxis, series.yaxis, symbol); + } + + ctx.lineWidth = lw; + ctx.strokeStyle = series.color; + plotPoints(series.datapoints, radius, + getFillStyle(series.points, series.color), 0, false, + series.xaxis, series.yaxis, symbol); + ctx.restore(); + } + + function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { + var left, right, bottom, top, + drawLeft, drawRight, drawTop, drawBottom, + tmp; + + // in horizontal mode, we start the bar from the left + // instead of from the bottom so it appears to be + // horizontal rather than vertical + if (horizontal) { + drawBottom = drawRight = drawTop = true; + drawLeft = false; + left = b; + right = x; + top = y + barLeft; + bottom = y + barRight; + + // account for negative bars + if (right < left) { + tmp = right; + right = left; + left = tmp; + drawLeft = true; + drawRight = false; + } + } + else { + drawLeft = drawRight = drawTop = true; + drawBottom = false; + left = x + barLeft; + right = x + barRight; + bottom = b; + top = y; + + // account for negative bars + if (top < bottom) { + tmp = top; + top = bottom; + bottom = tmp; + drawBottom = true; + drawTop = false; + } + } + + // clip + if (right < axisx.min || left > axisx.max || + top < axisy.min || bottom > axisy.max) + return; + + if (left < axisx.min) { + left = axisx.min; + drawLeft = false; + } + + if (right > axisx.max) { + right = axisx.max; + drawRight = false; + } + + if (bottom < axisy.min) { + bottom = axisy.min; + drawBottom = false; + } + + if (top > axisy.max) { + top = axisy.max; + drawTop = false; + } + + left = axisx.p2c(left); + bottom = axisy.p2c(bottom); + right = axisx.p2c(right); + top = axisy.p2c(top); + + // fill the bar + if (fillStyleCallback) { + c.fillStyle = fillStyleCallback(bottom, top); + c.fillRect(left, top, right - left, bottom - top) + } + + // draw outline + if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { + c.beginPath(); + + // FIXME: inline moveTo is buggy with excanvas + c.moveTo(left, bottom); + if (drawLeft) + c.lineTo(left, top); + else + c.moveTo(left, top); + if (drawTop) + c.lineTo(right, top); + else + c.moveTo(right, top); + if (drawRight) + c.lineTo(right, bottom); + else + c.moveTo(right, bottom); + if (drawBottom) + c.lineTo(left, bottom); + else + c.moveTo(left, bottom); + c.stroke(); + } + } + + function drawSeriesBars(series) { + function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) { + var points = datapoints.points, ps = datapoints.pointsize; + + for (var i = 0; i < points.length; i += ps) { + if (points[i] == null) + continue; + drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); + } + } + + ctx.save(); + ctx.translate(plotOffset.left, plotOffset.top); + + // FIXME: figure out a way to add shadows (for instance along the right edge) + ctx.lineWidth = series.bars.lineWidth; + ctx.strokeStyle = series.color; + + var barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; + plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis); + ctx.restore(); + } + + function getFillStyle(filloptions, seriesColor, bottom, top) { + var fill = filloptions.fill; + if (!fill) + return null; + + if (filloptions.fillColor) + return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); + + var c = $.color.parse(seriesColor); + c.a = typeof fill == "number" ? fill : 0.4; + c.normalize(); + return c.toString(); + } + + function insertLegend() { + + placeholder.find(".legend").remove(); + + if (!options.legend.show) + return; + + var fragments = [], entries = [], rowStarted = false, + lf = options.legend.labelFormatter, s, label; + + // Build a list of legend entries, with each having a label and a color + + for (var i = 0; i < series.length; ++i) { + s = series[i]; + if (s.label) { + label = lf ? lf(s.label, s) : s.label; + if (label) { + entries.push({ + label: label, + color: s.color + }); + } + } + } + + // Sort the legend using either the default or a custom comparator + + if (options.legend.sorted) { + if ($.isFunction(options.legend.sorted)) { + entries.sort(options.legend.sorted); + } else if (options.legend.sorted == "reverse") { + entries.reverse(); + } else { + var ascending = options.legend.sorted != "descending"; + entries.sort(function(a, b) { + return a.label == b.label ? 0 : ( + (a.label < b.label) != ascending ? 1 : -1 // Logical XOR + ); + }); + } + } + + // Generate markup for the list of entries, in their final order + + for (var i = 0; i < entries.length; ++i) { + + var entry = entries[i]; + + if (i % options.legend.noColumns == 0) { + if (rowStarted) + fragments.push('</tr>'); + fragments.push('<tr>'); + rowStarted = true; + } + + fragments.push( + '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' + + '<td class="legendLabel">' + entry.label + '</td>' + ); + } + + if (rowStarted) + fragments.push('</tr>'); + + if (fragments.length == 0) + return; + + var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>'; + if (options.legend.container != null) + $(options.legend.container).html(table); + else { + var pos = "", + p = options.legend.position, + m = options.legend.margin; + if (m[0] == null) + m = [m, m]; + if (p.charAt(0) == "n") + pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; + else if (p.charAt(0) == "s") + pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; + if (p.charAt(1) == "e") + pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; + else if (p.charAt(1) == "w") + pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; + var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder); + if (options.legend.backgroundOpacity != 0.0) { + // put in the transparent background + // separately to avoid blended labels and + // label boxes + var c = options.legend.backgroundColor; + if (c == null) { + c = options.grid.backgroundColor; + if (c && typeof c == "string") + c = $.color.parse(c); + else + c = $.color.extract(legend, 'background-color'); + c.a = 1; + c = c.toString(); + } + var div = legend.children(); + $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity); + } + } + } + + + // interactive features + + var highlights = [], + redrawTimeout = null; + + // returns the data item the mouse is over, or null if none is found + function findNearbyItem(mouseX, mouseY, seriesFilter) { + var maxDistance = options.grid.mouseActiveRadius, + smallestDistance = maxDistance * maxDistance + 1, + item = null, foundPoint = false, i, j, ps; + + for (i = series.length - 1; i >= 0; --i) { + if (!seriesFilter(series[i])) + continue; + + var s = series[i], + axisx = s.xaxis, + axisy = s.yaxis, + points = s.datapoints.points, + mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster + my = axisy.c2p(mouseY), + maxx = maxDistance / axisx.scale, + maxy = maxDistance / axisy.scale; + + ps = s.datapoints.pointsize; + // with inverse transforms, we can't use the maxx/maxy + // optimization, sadly + if (axisx.options.inverseTransform) + maxx = Number.MAX_VALUE; + if (axisy.options.inverseTransform) + maxy = Number.MAX_VALUE; + + if (s.lines.show || s.points.show) { + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1]; + if (x == null) + continue; + + // For points and lines, the cursor must be within a + // certain distance to the data point + if (x - mx > maxx || x - mx < -maxx || + y - my > maxy || y - my < -maxy) + continue; + + // We have to calculate distances in pixels, not in + // data units, because the scales of the axes may be different + var dx = Math.abs(axisx.p2c(x) - mouseX), + dy = Math.abs(axisy.p2c(y) - mouseY), + dist = dx * dx + dy * dy; // we save the sqrt + + // use <= to ensure last point takes precedence + // (last generally means on top of) + if (dist < smallestDistance) { + smallestDistance = dist; + item = [i, j / ps]; + } + } + } + + if (s.bars.show && !item) { // no other point can be nearby + + var barLeft, barRight; + + switch (s.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -s.bars.barWidth; + break; + default: + barLeft = -s.bars.barWidth / 2; + } + + barRight = barLeft + s.bars.barWidth; + + for (j = 0; j < points.length; j += ps) { + var x = points[j], y = points[j + 1], b = points[j + 2]; + if (x == null) + continue; + + // for a bar graph, the cursor must be inside the bar + if (series[i].bars.horizontal ? + (mx <= Math.max(b, x) && mx >= Math.min(b, x) && + my >= y + barLeft && my <= y + barRight) : + (mx >= x + barLeft && mx <= x + barRight && + my >= Math.min(b, y) && my <= Math.max(b, y))) + item = [i, j / ps]; + } + } + } + + if (item) { + i = item[0]; + j = item[1]; + ps = series[i].datapoints.pointsize; + + return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), + dataIndex: j, + series: series[i], + seriesIndex: i }; + } + + return null; + } + + function onMouseMove(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return s["hoverable"] != false; }); + } + + function onMouseLeave(e) { + if (options.grid.hoverable) + triggerClickHoverEvent("plothover", e, + function (s) { return false; }); + } + + function onClick(e) { + triggerClickHoverEvent("plotclick", e, + function (s) { return s["clickable"] != false; }); + } + + // trigger click or hover event (they send the same parameters + // so we share their code) + function triggerClickHoverEvent(eventname, event, seriesFilter) { + var offset = eventHolder.offset(), + canvasX = event.pageX - offset.left - plotOffset.left, + canvasY = event.pageY - offset.top - plotOffset.top, + pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); + + pos.pageX = event.pageX; + pos.pageY = event.pageY; + + var item = findNearbyItem(canvasX, canvasY, seriesFilter); + + if (item) { + // fill in mouse pos for any listeners out there + item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10); + item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10); + } + + if (options.grid.autoHighlight) { + // clear auto-highlights + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.auto == eventname && + !(item && h.series == item.series && + h.point[0] == item.datapoint[0] && + h.point[1] == item.datapoint[1])) + unhighlight(h.series, h.point); + } + + if (item) + highlight(item.series, item.datapoint, eventname); + } + + placeholder.trigger(eventname, [ pos, item ]); + } + + function triggerRedrawOverlay() { + var t = options.interaction.redrawOverlayInterval; + if (t == -1) { // skip event queue + drawOverlay(); + return; + } + + if (!redrawTimeout) + redrawTimeout = setTimeout(drawOverlay, t); + } + + function drawOverlay() { + redrawTimeout = null; + + // draw highlights + octx.save(); + overlay.clear(); + octx.translate(plotOffset.left, plotOffset.top); + + var i, hi; + for (i = 0; i < highlights.length; ++i) { + hi = highlights[i]; + + if (hi.series.bars.show) + drawBarHighlight(hi.series, hi.point); + else + drawPointHighlight(hi.series, hi.point); + } + octx.restore(); + + executeHooks(hooks.drawOverlay, [octx]); + } + + function highlight(s, point, auto) { + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i == -1) { + highlights.push({ series: s, point: point, auto: auto }); + + triggerRedrawOverlay(); + } + else if (!auto) + highlights[i].auto = false; + } + + function unhighlight(s, point) { + if (s == null && point == null) { + highlights = []; + triggerRedrawOverlay(); + return; + } + + if (typeof s == "number") + s = series[s]; + + if (typeof point == "number") { + var ps = s.datapoints.pointsize; + point = s.datapoints.points.slice(ps * point, ps * (point + 1)); + } + + var i = indexOfHighlight(s, point); + if (i != -1) { + highlights.splice(i, 1); + + triggerRedrawOverlay(); + } + } + + function indexOfHighlight(s, p) { + for (var i = 0; i < highlights.length; ++i) { + var h = highlights[i]; + if (h.series == s && h.point[0] == p[0] + && h.point[1] == p[1]) + return i; + } + return -1; + } + + function drawPointHighlight(series, point) { + var x = point[0], y = point[1], + axisx = series.xaxis, axisy = series.yaxis, + highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(); + + if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) + return; + + var pointRadius = series.points.radius + series.points.lineWidth / 2; + octx.lineWidth = pointRadius; + octx.strokeStyle = highlightColor; + var radius = 1.5 * pointRadius; + x = axisx.p2c(x); + y = axisy.p2c(y); + + octx.beginPath(); + if (series.points.symbol == "circle") + octx.arc(x, y, radius, 0, 2 * Math.PI, false); + else + series.points.symbol(octx, x, y, radius, false); + octx.closePath(); + octx.stroke(); + } + + function drawBarHighlight(series, point) { + var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(), + fillStyle = highlightColor, + barLeft; + + switch (series.bars.align) { + case "left": + barLeft = 0; + break; + case "right": + barLeft = -series.bars.barWidth; + break; + default: + barLeft = -series.bars.barWidth / 2; + } + + octx.lineWidth = series.bars.lineWidth; + octx.strokeStyle = highlightColor; + + drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, + function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); + } + + function getColorOrGradient(spec, bottom, top, defaultColor) { + if (typeof spec == "string") + return spec; + else { + // assume this is a gradient spec; IE currently only + // supports a simple vertical gradient properly, so that's + // what we support too + var gradient = ctx.createLinearGradient(0, top, 0, bottom); + + for (var i = 0, l = spec.colors.length; i < l; ++i) { + var c = spec.colors[i]; + if (typeof c != "string") { + var co = $.color.parse(defaultColor); + if (c.brightness != null) + co = co.scale('rgb', c.brightness); + if (c.opacity != null) + co.a *= c.opacity; + c = co.toString(); + } + gradient.addColorStop(i / (l - 1), c); + } + + return gradient; + } + } + } + + // Add the plot function to the top level of the jQuery object + + $.plot = function(placeholder, data, options) { + //var t0 = new Date(); + var plot = new Plot($(placeholder), data, options, $.plot.plugins); + //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); + return plot; + }; + + $.plot.version = "0.8.2-alpha"; + + $.plot.plugins = []; + + // Also add the plot function as a chainable property + + $.fn.plot = function(data, options) { + return this.each(function() { + $.plot(this, data, options); + }); + }; + + // round to nearby lower multiple of base + function floorInBase(n, base) { + return base * Math.floor(n / base); + } + +})(jQuery); diff --git a/app/assets/javascripts/jquery.flot.min.js b/app/assets/javascripts/jquery.flot.min.js new file mode 100644 index 000000000..b82faa18f --- /dev/null +++ b/app/assets/javascripts/jquery.flot.min.js @@ -0,0 +1 @@ +(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);(function($){var hasOwnProperty=Object.prototype.hasOwnProperty;function Canvas(cls,container){var element=container.children("."+cls)[0];if(element==null){element=document.createElement("canvas");element.className=cls;$(element).css({direction:"ltr",position:"absolute",left:0,top:0}).appendTo(container);if(!element.getContext){if(window.G_vmlCanvasManager){element=window.G_vmlCanvasManager.initElement(element)}else{throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.")}}}this.element=element;var context=this.context=element.getContext("2d");var devicePixelRatio=window.devicePixelRatio||1,backingStoreRatio=context.webkitBackingStorePixelRatio||context.mozBackingStorePixelRatio||context.msBackingStorePixelRatio||context.oBackingStorePixelRatio||context.backingStorePixelRatio||1;this.pixelRatio=devicePixelRatio/backingStoreRatio;this.resize(container.width(),container.height());this.textContainer=null;this.text={};this._textCache={}}Canvas.prototype.resize=function(width,height){if(width<=0||height<=0){throw new Error("Invalid dimensions for plot, width = "+width+", height = "+height)}var element=this.element,context=this.context,pixelRatio=this.pixelRatio;if(this.width!=width){element.width=width*pixelRatio;element.style.width=width+"px";this.width=width}if(this.height!=height){element.height=height*pixelRatio;element.style.height=height+"px";this.height=height}context.restore();context.save();context.scale(pixelRatio,pixelRatio)};Canvas.prototype.clear=function(){this.context.clearRect(0,0,this.width,this.height)};Canvas.prototype.render=function(){var cache=this._textCache;for(var layerKey in cache){if(hasOwnProperty.call(cache,layerKey)){var layer=this.getTextLayer(layerKey),layerCache=cache[layerKey];layer.hide();for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){if(position.active){if(!position.rendered){layer.append(position.element);position.rendered=true}}else{positions.splice(i--,1);if(position.rendered){position.element.detach()}}}if(positions.length==0){delete styleCache[key]}}}}}layer.show()}}};Canvas.prototype.getTextLayer=function(classes){var layer=this.text[classes];if(layer==null){if(this.textContainer==null){this.textContainer=$("<div class='flot-text'></div>").css({position:"absolute",top:0,left:0,bottom:0,right:0,"font-size":"smaller",color:"#545454"}).insertAfter(this.element)}layer=this.text[classes]=$("<div></div>").addClass(classes).css({position:"absolute",top:0,left:0,bottom:0,right:0}).appendTo(this.textContainer)}return layer};Canvas.prototype.getTextInfo=function(layer,text,font,angle,width){var textStyle,layerCache,styleCache,info;text=""+text;if(typeof font==="object"){textStyle=font.style+" "+font.variant+" "+font.weight+" "+font.size+"px/"+font.lineHeight+"px "+font.family}else{textStyle=font}layerCache=this._textCache[layer];if(layerCache==null){layerCache=this._textCache[layer]={}}styleCache=layerCache[textStyle];if(styleCache==null){styleCache=layerCache[textStyle]={}}info=styleCache[text];if(info==null){var element=$("<div></div>").html(text).css({position:"absolute","max-width":width,top:-9999}).appendTo(this.getTextLayer(layer));if(typeof font==="object"){element.css({font:textStyle,color:font.color})}else{if(typeof font==="string"){element.addClass(font)}}info=styleCache[text]={width:element.outerWidth(true),height:element.outerHeight(true),element:element,positions:[]};element.detach()}return info};Canvas.prototype.addText=function(layer,x,y,text,font,angle,width,halign,valign){var info=this.getTextInfo(layer,text,font,angle,width),positions=info.positions;if(halign=="center"){x-=info.width/2}else{if(halign=="right"){x-=info.width}}if(valign=="middle"){y-=info.height/2}else{if(valign=="bottom"){y-=info.height}}for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=true;return}}position={active:true,rendered:false,element:positions.length?info.element.clone():info.element,x:x,y:y};positions.push(position);position.element.css({top:Math.round(y),left:Math.round(x),"text-align":halign})};Canvas.prototype.removeText=function(layer,x,y,text,font,angle){if(text==null){var layerCache=this._textCache[layer];if(layerCache!=null){for(var styleKey in layerCache){if(hasOwnProperty.call(layerCache,styleKey)){var styleCache=layerCache[styleKey];for(var key in styleCache){if(hasOwnProperty.call(styleCache,key)){var positions=styleCache[key].positions;for(var i=0,position;position=positions[i];i++){position.active=false}}}}}}}else{var positions=this.getTextInfo(layer,text,font,angle).positions;for(var i=0,position;position=positions[i];i++){if(position.x==x&&position.y==y){position.active=false}}}};function Plot(placeholder,data_,options_,plugins){var series=[],options={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85,sorted:null},xaxis:{show:null,position:"bottom",mode:null,font:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false,zero:true},shadowSize:3,highlightColor:null},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,margin:0,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},interaction:{redrawOverlayInterval:1000/60},hooks:{}},surface=null,overlay=null,eventHolder=null,ctx=null,octx=null,xaxes=[],yaxes=[],plotOffset={left:0,right:0,top:0,bottom:0},plotWidth=0,plotHeight=0,hooks={processOptions:[],processRawData:[],processDatapoints:[],processOffset:[],drawBackground:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},plot=this;plot.setData=setData;plot.setupGrid=setupGrid;plot.draw=draw;plot.getPlaceholder=function(){return placeholder};plot.getCanvas=function(){return surface.element};plot.getPlotOffset=function(){return plotOffset};plot.width=function(){return plotWidth};plot.height=function(){return plotHeight};plot.offset=function(){var o=eventHolder.offset();o.left+=plotOffset.left;o.top+=plotOffset.top;return o};plot.getData=function(){return series};plot.getAxes=function(){var res={},i;$.each(xaxes.concat(yaxes),function(_,axis){if(axis){res[axis.direction+(axis.n!=1?axis.n:"")+"axis"]=axis}});return res};plot.getXAxes=function(){return xaxes};plot.getYAxes=function(){return yaxes};plot.c2p=canvasToAxisCoords;plot.p2c=axisToCanvasCoords;plot.getOptions=function(){return options};plot.highlight=highlight;plot.unhighlight=unhighlight;plot.triggerRedrawOverlay=triggerRedrawOverlay;plot.pointOffset=function(point){return{left:parseInt(xaxes[axisNumber(point,"x")-1].p2c(+point.x)+plotOffset.left,10),top:parseInt(yaxes[axisNumber(point,"y")-1].p2c(+point.y)+plotOffset.top,10)}};plot.shutdown=shutdown;plot.resize=function(){var width=placeholder.width(),height=placeholder.height();surface.resize(width,height);overlay.resize(width,height)};plot.hooks=hooks;initPlugins(plot);parseOptions(options_);setupCanvases();setData(data_);setupGrid();draw();bindEvents();function executeHooks(hook,args){args=[plot].concat(args);for(var i=0;i<hook.length;++i){hook[i].apply(this,args)}}function initPlugins(){var classes={Canvas:Canvas};for(var i=0;i<plugins.length;++i){var p=plugins[i];p.init(plot,classes);if(p.options){$.extend(true,options,p.options)}}}function parseOptions(opts){$.extend(true,options,opts);if(opts&&opts.colors){options.colors=opts.colors}if(options.xaxis.color==null){options.xaxis.color=$.color.parse(options.grid.color).scale("a",0.22).toString()}if(options.yaxis.color==null){options.yaxis.color=$.color.parse(options.grid.color).scale("a",0.22).toString()}if(options.xaxis.tickColor==null){options.xaxis.tickColor=options.grid.tickColor||options.xaxis.color}if(options.yaxis.tickColor==null){options.yaxis.tickColor=options.grid.tickColor||options.yaxis.color}if(options.grid.borderColor==null){options.grid.borderColor=options.grid.color}if(options.grid.tickColor==null){options.grid.tickColor=$.color.parse(options.grid.color).scale("a",0.22).toString()}var i,axisOptions,axisCount,fontDefaults={style:placeholder.css("font-style"),size:Math.round(0.8*(+placeholder.css("font-size").replace("px","")||13)),variant:placeholder.css("font-variant"),weight:placeholder.css("font-weight"),family:placeholder.css("font-family")};fontDefaults.lineHeight=fontDefaults.size*1.15;axisCount=options.xaxes.length||1;for(i=0;i<axisCount;++i){axisOptions=options.xaxes[i];if(axisOptions&&!axisOptions.tickColor){axisOptions.tickColor=axisOptions.color}axisOptions=$.extend(true,{},options.xaxis,axisOptions);options.xaxes[i]=axisOptions;if(axisOptions.font){axisOptions.font=$.extend({},fontDefaults,axisOptions.font);if(!axisOptions.font.color){axisOptions.font.color=axisOptions.color}}}axisCount=options.yaxes.length||1;for(i=0;i<axisCount;++i){axisOptions=options.yaxes[i];if(axisOptions&&!axisOptions.tickColor){axisOptions.tickColor=axisOptions.color}axisOptions=$.extend(true,{},options.yaxis,axisOptions);options.yaxes[i]=axisOptions;if(axisOptions.font){axisOptions.font=$.extend({},fontDefaults,axisOptions.font);if(!axisOptions.font.color){axisOptions.font.color=axisOptions.color}}}if(options.xaxis.noTicks&&options.xaxis.ticks==null){options.xaxis.ticks=options.xaxis.noTicks}if(options.yaxis.noTicks&&options.yaxis.ticks==null){options.yaxis.ticks=options.yaxis.noTicks}if(options.x2axis){options.xaxes[1]=$.extend(true,{},options.xaxis,options.x2axis);options.xaxes[1].position="top"}if(options.y2axis){options.yaxes[1]=$.extend(true,{},options.yaxis,options.y2axis);options.yaxes[1].position="right"}if(options.grid.coloredAreas){options.grid.markings=options.grid.coloredAreas}if(options.grid.coloredAreasColor){options.grid.markingsColor=options.grid.coloredAreasColor}if(options.lines){$.extend(true,options.series.lines,options.lines)}if(options.points){$.extend(true,options.series.points,options.points)}if(options.bars){$.extend(true,options.series.bars,options.bars)}if(options.shadowSize!=null){options.series.shadowSize=options.shadowSize}if(options.highlightColor!=null){options.series.highlightColor=options.highlightColor}for(i=0;i<options.xaxes.length;++i){getOrCreateAxis(xaxes,i+1).options=options.xaxes[i]}for(i=0;i<options.yaxes.length;++i){getOrCreateAxis(yaxes,i+1).options=options.yaxes[i]}for(var n in hooks){if(options.hooks[n]&&options.hooks[n].length){hooks[n]=hooks[n].concat(options.hooks[n])}}executeHooks(hooks.processOptions,[options])}function setData(d){series=parseData(d);fillInSeriesOptions();processData()}function parseData(d){var res=[];for(var i=0;i<d.length;++i){var s=$.extend(true,{},options.series);if(d[i].data!=null){s.data=d[i].data;delete d[i].data;$.extend(true,s,d[i]);d[i].data=s.data}else{s.data=d[i]}res.push(s)}return res}function axisNumber(obj,coord){var a=obj[coord+"axis"];if(typeof a=="object"){a=a.n}if(typeof a!="number"){a=1}return a}function allAxes(){return $.grep(xaxes.concat(yaxes),function(a){return a})}function canvasToAxisCoords(pos){var res={},i,axis;for(i=0;i<xaxes.length;++i){axis=xaxes[i];if(axis&&axis.used){res["x"+axis.n]=axis.c2p(pos.left)}}for(i=0;i<yaxes.length;++i){axis=yaxes[i];if(axis&&axis.used){res["y"+axis.n]=axis.c2p(pos.top)}}if(res.x1!==undefined){res.x=res.x1}if(res.y1!==undefined){res.y=res.y1}return res}function axisToCanvasCoords(pos){var res={},i,axis,key;for(i=0;i<xaxes.length;++i){axis=xaxes[i];if(axis&&axis.used){key="x"+axis.n;if(pos[key]==null&&axis.n==1){key="x"}if(pos[key]!=null){res.left=axis.p2c(pos[key]);break}}}for(i=0;i<yaxes.length;++i){axis=yaxes[i];if(axis&&axis.used){key="y"+axis.n;if(pos[key]==null&&axis.n==1){key="y"}if(pos[key]!=null){res.top=axis.p2c(pos[key]);break}}}return res}function getOrCreateAxis(axes,number){if(!axes[number-1]){axes[number-1]={n:number,direction:axes==xaxes?"x":"y",options:$.extend(true,{},axes==xaxes?options.xaxis:options.yaxis)}}return axes[number-1]}function fillInSeriesOptions(){var neededColors=series.length,maxIndex=-1,i;for(i=0;i<series.length;++i){var sc=series[i].color;if(sc!=null){neededColors--;if(typeof sc=="number"&&sc>maxIndex){maxIndex=sc}}}if(neededColors<=maxIndex){neededColors=maxIndex+1}var c,colors=[],colorPool=options.colors,colorPoolSize=colorPool.length,variation=0;for(i=0;i<neededColors;i++){c=$.color.parse(colorPool[i%colorPoolSize]||"#666");if(i%colorPoolSize==0&&i){if(variation>=0){if(variation<0.5){variation=-variation-0.2}else{variation=0}}else{variation=-variation}}colors[i]=c.scale("rgb",1+variation)}var colori=0,s;for(i=0;i<series.length;++i){s=series[i];if(s.color==null){s.color=colors[colori].toString();++colori}else{if(typeof s.color=="number"){s.color=colors[s.color].toString()}}if(s.lines.show==null){var v,show=true;for(v in s){if(s[v]&&s[v].show){show=false;break}}if(show){s.lines.show=true}}if(s.lines.zero==null){s.lines.zero=!!s.lines.fill}s.xaxis=getOrCreateAxis(xaxes,axisNumber(s,"x"));s.yaxis=getOrCreateAxis(yaxes,axisNumber(s,"y"))}}function processData(){var topSentry=Number.POSITIVE_INFINITY,bottomSentry=Number.NEGATIVE_INFINITY,fakeInfinity=Number.MAX_VALUE,i,j,k,m,length,s,points,ps,x,y,axis,val,f,p,data,format;function updateAxis(axis,min,max){if(min<axis.datamin&&min!=-fakeInfinity){axis.datamin=min}if(max>axis.datamax&&max!=fakeInfinity){axis.datamax=max}}$.each(allAxes(),function(_,axis){axis.datamin=topSentry;axis.datamax=bottomSentry;axis.used=false});for(i=0;i<series.length;++i){s=series[i];s.datapoints={points:[]};executeHooks(hooks.processRawData,[s,s.data,s.datapoints])}for(i=0;i<series.length;++i){s=series[i];data=s.data;format=s.datapoints.format;if(!format){format=[];format.push({x:true,number:true,required:true});format.push({y:true,number:true,required:true});if(s.bars.show||(s.lines.show&&s.lines.fill)){var autoscale=!!((s.bars.show&&s.bars.zero)||(s.lines.show&&s.lines.zero));format.push({y:true,number:true,required:false,defaultValue:0,autoscale:autoscale});if(s.bars.horizontal){delete format[format.length-1].y;format[format.length-1].x=true}}s.datapoints.format=format}if(s.datapoints.pointsize!=null){continue}s.datapoints.pointsize=format.length;ps=s.datapoints.pointsize;points=s.datapoints.points;var insertSteps=s.lines.show&&s.lines.steps;s.xaxis.used=s.yaxis.used=true;for(j=k=0;j<data.length;++j,k+=ps){p=data[j];var nullify=p==null;if(!nullify){for(m=0;m<ps;++m){val=p[m];f=format[m];if(f){if(f.number&&val!=null){val=+val;if(isNaN(val)){val=null}else{if(val==Infinity){val=fakeInfinity}else{if(val==-Infinity){val=-fakeInfinity}}}}if(val==null){if(f.required){nullify=true}if(f.defaultValue!=null){val=f.defaultValue}}}points[k+m]=val}}if(nullify){for(m=0;m<ps;++m){val=points[k+m];if(val!=null){f=format[m];if(f.autoscale!==false){if(f.x){updateAxis(s.xaxis,val,val)}if(f.y){updateAxis(s.yaxis,val,val)}}}points[k+m]=null}}else{if(insertSteps&&k>0&&points[k-ps]!=null&&points[k-ps]!=points[k]&&points[k-ps+1]!=points[k+1]){for(m=0;m<ps;++m){points[k+ps+m]=points[k+m]}points[k+1]=points[k-ps+1];k+=ps}}}}for(i=0;i<series.length;++i){s=series[i];executeHooks(hooks.processDatapoints,[s,s.datapoints])}for(i=0;i<series.length;++i){s=series[i];points=s.datapoints.points;ps=s.datapoints.pointsize;format=s.datapoints.format;var xmin=topSentry,ymin=topSentry,xmax=bottomSentry,ymax=bottomSentry;for(j=0;j<points.length;j+=ps){if(points[j]==null){continue}for(m=0;m<ps;++m){val=points[j+m];f=format[m];if(!f||f.autoscale===false||val==fakeInfinity||val==-fakeInfinity){continue}if(f.x){if(val<xmin){xmin=val}if(val>xmax){xmax=val}}if(f.y){if(val<ymin){ymin=val}if(val>ymax){ymax=val}}}}if(s.bars.show){var delta;switch(s.bars.align){case"left":delta=0;break;case"right":delta=-s.bars.barWidth;break;default:delta=-s.bars.barWidth/2}if(s.bars.horizontal){ymin+=delta;ymax+=delta+s.bars.barWidth}else{xmin+=delta;xmax+=delta+s.bars.barWidth}}updateAxis(s.xaxis,xmin,xmax);updateAxis(s.yaxis,ymin,ymax)}$.each(allAxes(),function(_,axis){if(axis.datamin==topSentry){axis.datamin=null}if(axis.datamax==bottomSentry){axis.datamax=null}})}function setupCanvases(){placeholder.css("padding",0).children(":not(.flot-base,.flot-overlay)").remove();if(placeholder.css("position")=="static"){placeholder.css("position","relative")}surface=new Canvas("flot-base",placeholder);overlay=new Canvas("flot-overlay",placeholder);ctx=surface.context;octx=overlay.context;eventHolder=$(overlay.element).unbind();var existing=placeholder.data("plot");if(existing){existing.shutdown();overlay.clear()}placeholder.data("plot",plot)}function bindEvents(){if(options.grid.hoverable){eventHolder.mousemove(onMouseMove);eventHolder.bind("mouseleave",onMouseLeave)}if(options.grid.clickable){eventHolder.click(onClick)}executeHooks(hooks.bindEvents,[eventHolder])}function shutdown(){if(redrawTimeout){clearTimeout(redrawTimeout)}eventHolder.unbind("mousemove",onMouseMove);eventHolder.unbind("mouseleave",onMouseLeave);eventHolder.unbind("click",onClick);executeHooks(hooks.shutdown,[eventHolder])}function setTransformationHelpers(axis){function identity(x){return x}var s,m,t=axis.options.transform||identity,it=axis.options.inverseTransform;if(axis.direction=="x"){s=axis.scale=plotWidth/Math.abs(t(axis.max)-t(axis.min));m=Math.min(t(axis.max),t(axis.min))}else{s=axis.scale=plotHeight/Math.abs(t(axis.max)-t(axis.min));s=-s;m=Math.max(t(axis.max),t(axis.min))}if(t==identity){axis.p2c=function(p){return(p-m)*s}}else{axis.p2c=function(p){return(t(p)-m)*s}}if(!it){axis.c2p=function(c){return m+c/s}}else{axis.c2p=function(c){return it(m+c/s)}}}function measureTickLabels(axis){var opts=axis.options,ticks=axis.ticks||[],labelWidth=opts.labelWidth||0,labelHeight=opts.labelHeight||0,maxWidth=labelWidth||axis.direction=="x"?Math.floor(surface.width/(ticks.length||1)):null,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=opts.font||"flot-tick-label tickLabel";for(var i=0;i<ticks.length;++i){var t=ticks[i];if(!t.label){continue}var info=surface.getTextInfo(layer,t.label,font,null,maxWidth);labelWidth=Math.max(labelWidth,info.width);labelHeight=Math.max(labelHeight,info.height)}axis.labelWidth=opts.labelWidth||labelWidth;axis.labelHeight=opts.labelHeight||labelHeight}function allocateAxisBoxFirstPhase(axis){var lw=axis.labelWidth,lh=axis.labelHeight,pos=axis.options.position,tickLength=axis.options.tickLength,axisMargin=options.grid.axisMargin,padding=options.grid.labelMargin,all=axis.direction=="x"?xaxes:yaxes,index,innermost;var samePosition=$.grep(all,function(a){return a&&a.options.position==pos&&a.reserveSpace});if($.inArray(axis,samePosition)==samePosition.length-1){axisMargin=0}innermost=$.inArray(axis,samePosition)==0;if(tickLength==null){if(innermost){tickLength="full"}else{tickLength=5}}if(!isNaN(+tickLength)){padding+=+tickLength}if(axis.direction=="x"){lh+=padding;if(pos=="bottom"){plotOffset.bottom+=lh+axisMargin;axis.box={top:surface.height-plotOffset.bottom,height:lh}}else{axis.box={top:plotOffset.top+axisMargin,height:lh};plotOffset.top+=lh+axisMargin}}else{lw+=padding;if(pos=="left"){axis.box={left:plotOffset.left+axisMargin,width:lw};plotOffset.left+=lw+axisMargin}else{plotOffset.right+=lw+axisMargin;axis.box={left:surface.width-plotOffset.right,width:lw}}}axis.position=pos;axis.tickLength=tickLength;axis.box.padding=padding;axis.innermost=innermost}function allocateAxisBoxSecondPhase(axis){if(axis.direction=="x"){axis.box.left=plotOffset.left-axis.labelWidth/2;axis.box.width=surface.width-plotOffset.left-plotOffset.right+axis.labelWidth}else{axis.box.top=plotOffset.top-axis.labelHeight/2;axis.box.height=surface.height-plotOffset.bottom-plotOffset.top+axis.labelHeight}}function adjustLayoutForThingsStickingOut(){var minMargin=options.grid.minBorderMargin,margins={x:0,y:0},i,axis;if(minMargin==null){minMargin=0;for(i=0;i<series.length;++i){minMargin=Math.max(minMargin,2*(series[i].points.radius+series[i].points.lineWidth/2))}}margins.x=margins.y=Math.ceil(minMargin);$.each(allAxes(),function(_,axis){var dir=axis.direction;if(axis.reserveSpace){margins[dir]=Math.ceil(Math.max(margins[dir],(dir=="x"?axis.labelWidth:axis.labelHeight)/2))}});plotOffset.left=Math.max(margins.x,plotOffset.left);plotOffset.right=Math.max(margins.x,plotOffset.right);plotOffset.top=Math.max(margins.y,plotOffset.top);plotOffset.bottom=Math.max(margins.y,plotOffset.bottom)}function setupGrid(){var i,axes=allAxes(),showGrid=options.grid.show;for(var a in plotOffset){var margin=options.grid.margin||0;plotOffset[a]=typeof margin=="number"?margin:margin[a]||0}executeHooks(hooks.processOffset,[plotOffset]);for(var a in plotOffset){if(typeof(options.grid.borderWidth)=="object"){plotOffset[a]+=showGrid?options.grid.borderWidth[a]:0}else{plotOffset[a]+=showGrid?options.grid.borderWidth:0}}$.each(axes,function(_,axis){axis.show=axis.options.show;if(axis.show==null){axis.show=axis.used}axis.reserveSpace=axis.show||axis.options.reserveSpace;setRange(axis)});if(showGrid){var allocatedAxes=$.grep(axes,function(axis){return axis.reserveSpace});$.each(allocatedAxes,function(_,axis){setupTickGeneration(axis);setTicks(axis);snapRangeToTicks(axis,axis.ticks);measureTickLabels(axis)});for(i=allocatedAxes.length-1;i>=0;--i){allocateAxisBoxFirstPhase(allocatedAxes[i])}adjustLayoutForThingsStickingOut();$.each(allocatedAxes,function(_,axis){allocateAxisBoxSecondPhase(axis)})}plotWidth=surface.width-plotOffset.left-plotOffset.right;plotHeight=surface.height-plotOffset.bottom-plotOffset.top;$.each(axes,function(_,axis){setTransformationHelpers(axis)});if(showGrid){drawAxisLabels()}insertLegend()}function setRange(axis){var opts=axis.options,min=+(opts.min!=null?opts.min:axis.datamin),max=+(opts.max!=null?opts.max:axis.datamax),delta=max-min;if(delta==0){var widen=max==0?1:0.01;if(opts.min==null){min-=widen}if(opts.max==null||opts.min!=null){max+=widen}}else{var margin=opts.autoscaleMargin;if(margin!=null){if(opts.min==null){min-=delta*margin;if(min<0&&axis.datamin!=null&&axis.datamin>=0){min=0}}if(opts.max==null){max+=delta*margin;if(max>0&&axis.datamax!=null&&axis.datamax<=0){max=0}}}}axis.min=min;axis.max=max}function setupTickGeneration(axis){var opts=axis.options;var noTicks;if(typeof opts.ticks=="number"&&opts.ticks>0){noTicks=opts.ticks}else{noTicks=0.3*Math.sqrt(axis.direction=="x"?surface.width:surface.height)}var delta=(axis.max-axis.min)/noTicks,dec=-Math.floor(Math.log(delta)/Math.LN10),maxDec=opts.tickDecimals;if(maxDec!=null&&dec>maxDec){dec=maxDec}var magn=Math.pow(10,-dec),norm=delta/magn,size;if(norm<1.5){size=1}else{if(norm<3){size=2;if(norm>2.25&&(maxDec==null||dec+1<=maxDec)){size=2.5;++dec}}else{if(norm<7.5){size=5}else{size=10}}}size*=magn;if(opts.minTickSize!=null&&size<opts.minTickSize){size=opts.minTickSize}axis.delta=delta;axis.tickDecimals=Math.max(0,maxDec!=null?maxDec:dec);axis.tickSize=opts.tickSize||size;if(opts.mode=="time"&&!axis.tickGenerator){throw new Error("Time mode requires the flot.time plugin.")}if(!axis.tickGenerator){axis.tickGenerator=function(axis){var ticks=[],start=floorInBase(axis.min,axis.tickSize),i=0,v=Number.NaN,prev;do{prev=v;v=start+i*axis.tickSize;ticks.push(v);++i}while(v<axis.max&&v!=prev);return ticks};axis.tickFormatter=function(value,axis){var factor=axis.tickDecimals?Math.pow(10,axis.tickDecimals):1;var formatted=""+Math.round(value*factor)/factor;if(axis.tickDecimals!=null){var decimal=formatted.indexOf(".");var precision=decimal==-1?0:formatted.length-decimal-1;if(precision<axis.tickDecimals){return(precision?formatted:formatted+".")+(""+factor).substr(1,axis.tickDecimals-precision)}}return formatted}}if($.isFunction(opts.tickFormatter)){axis.tickFormatter=function(v,axis){return""+opts.tickFormatter(v,axis)}}if(opts.alignTicksWithAxis!=null){var otherAxis=(axis.direction=="x"?xaxes:yaxes)[opts.alignTicksWithAxis-1];if(otherAxis&&otherAxis.used&&otherAxis!=axis){var niceTicks=axis.tickGenerator(axis);if(niceTicks.length>0){if(opts.min==null){axis.min=Math.min(axis.min,niceTicks[0])}if(opts.max==null&&niceTicks.length>1){axis.max=Math.max(axis.max,niceTicks[niceTicks.length-1])}}axis.tickGenerator=function(axis){var ticks=[],v,i;for(i=0;i<otherAxis.ticks.length;++i){v=(otherAxis.ticks[i].v-otherAxis.min)/(otherAxis.max-otherAxis.min);v=axis.min+v*(axis.max-axis.min);ticks.push(v)}return ticks};if(!axis.mode&&opts.tickDecimals==null){var extraDec=Math.max(0,-Math.floor(Math.log(axis.delta)/Math.LN10)+1),ts=axis.tickGenerator(axis);if(!(ts.length>1&&/\..*0$/.test((ts[1]-ts[0]).toFixed(extraDec)))){axis.tickDecimals=extraDec}}}}}function setTicks(axis){var oticks=axis.options.ticks,ticks=[];if(oticks==null||(typeof oticks=="number"&&oticks>0)){ticks=axis.tickGenerator(axis)}else{if(oticks){if($.isFunction(oticks)){ticks=oticks(axis)}else{ticks=oticks}}}var i,v;axis.ticks=[];for(i=0;i<ticks.length;++i){var label=null;var t=ticks[i];if(typeof t=="object"){v=+t[0];if(t.length>1){label=t[1]}}else{v=+t}if(label==null){label=axis.tickFormatter(v,axis)}if(!isNaN(v)){axis.ticks.push({v:v,label:label})}}}function snapRangeToTicks(axis,ticks){if(axis.options.autoscaleMargin&&ticks.length>0){if(axis.options.min==null){axis.min=Math.min(axis.min,ticks[0].v)}if(axis.options.max==null&&ticks.length>1){axis.max=Math.max(axis.max,ticks[ticks.length-1].v)}}}function draw(){surface.clear();executeHooks(hooks.drawBackground,[ctx]);var grid=options.grid;if(grid.show&&grid.backgroundColor){drawBackground()}if(grid.show&&!grid.aboveData){drawGrid()}for(var i=0;i<series.length;++i){executeHooks(hooks.drawSeries,[ctx,series[i]]);drawSeries(series[i])}executeHooks(hooks.draw,[ctx]);if(grid.show&&grid.aboveData){drawGrid()}surface.render();triggerRedrawOverlay()}function extractRange(ranges,coord){var axis,from,to,key,axes=allAxes();for(var i=0;i<axes.length;++i){axis=axes[i];if(axis.direction==coord){key=coord+axis.n+"axis";if(!ranges[key]&&axis.n==1){key=coord+"axis"}if(ranges[key]){from=ranges[key].from;to=ranges[key].to;break}}}if(!ranges[key]){axis=coord=="x"?xaxes[0]:yaxes[0];from=ranges[coord+"1"];to=ranges[coord+"2"]}if(from!=null&&to!=null&&from>to){var tmp=from;from=to;to=tmp}return{from:from,to:to,axis:axis}}function drawBackground(){ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.fillStyle=getColorOrGradient(options.grid.backgroundColor,plotHeight,0,"rgba(255, 255, 255, 0)");ctx.fillRect(0,0,plotWidth,plotHeight);ctx.restore()}function drawGrid(){var i,axes,bw,bc;ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var markings=options.grid.markings;if(markings){if($.isFunction(markings)){axes=plot.getAxes();axes.xmin=axes.xaxis.min;axes.xmax=axes.xaxis.max;axes.ymin=axes.yaxis.min;axes.ymax=axes.yaxis.max;markings=markings(axes)}for(i=0;i<markings.length;++i){var m=markings[i],xrange=extractRange(m,"x"),yrange=extractRange(m,"y");if(xrange.from==null){xrange.from=xrange.axis.min}if(xrange.to==null){xrange.to=xrange.axis.max}if(yrange.from==null){yrange.from=yrange.axis.min}if(yrange.to==null){yrange.to=yrange.axis.max}if(xrange.to<xrange.axis.min||xrange.from>xrange.axis.max||yrange.to<yrange.axis.min||yrange.from>yrange.axis.max){continue}xrange.from=Math.max(xrange.from,xrange.axis.min);xrange.to=Math.min(xrange.to,xrange.axis.max);yrange.from=Math.max(yrange.from,yrange.axis.min);yrange.to=Math.min(yrange.to,yrange.axis.max);if(xrange.from==xrange.to&&yrange.from==yrange.to){continue}xrange.from=xrange.axis.p2c(xrange.from);xrange.to=xrange.axis.p2c(xrange.to);yrange.from=yrange.axis.p2c(yrange.from);yrange.to=yrange.axis.p2c(yrange.to);if(xrange.from==xrange.to||yrange.from==yrange.to){ctx.beginPath();ctx.strokeStyle=m.color||options.grid.markingsColor;ctx.lineWidth=m.lineWidth||options.grid.markingsLineWidth;ctx.moveTo(xrange.from,yrange.from);ctx.lineTo(xrange.to,yrange.to);ctx.stroke()}else{ctx.fillStyle=m.color||options.grid.markingsColor;ctx.fillRect(xrange.from,yrange.to,xrange.to-xrange.from,yrange.from-yrange.to)}}}axes=allAxes();bw=options.grid.borderWidth;for(var j=0;j<axes.length;++j){var axis=axes[j],box=axis.box,t=axis.tickLength,x,y,xoff,yoff;if(!axis.show||axis.ticks.length==0){continue}ctx.lineWidth=1;if(axis.direction=="x"){x=0;if(t=="full"){y=(axis.position=="top"?0:plotHeight)}else{y=box.top-plotOffset.top+(axis.position=="top"?box.height:0)}}else{y=0;if(t=="full"){x=(axis.position=="left"?0:plotWidth)}else{x=box.left-plotOffset.left+(axis.position=="left"?box.width:0)}}if(!axis.innermost){ctx.strokeStyle=axis.options.color;ctx.beginPath();xoff=yoff=0;if(axis.direction=="x"){xoff=plotWidth+1}else{yoff=plotHeight+1}if(ctx.lineWidth==1){if(axis.direction=="x"){y=Math.floor(y)+0.5}else{x=Math.floor(x)+0.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff);ctx.stroke()}ctx.strokeStyle=axis.options.tickColor;ctx.beginPath();for(i=0;i<axis.ticks.length;++i){var v=axis.ticks[i].v;xoff=yoff=0;if(isNaN(v)||v<axis.min||v>axis.max||(t=="full"&&((typeof bw=="object"&&bw[axis.position]>0)||bw>0)&&(v==axis.min||v==axis.max))){continue}if(axis.direction=="x"){x=axis.p2c(v);yoff=t=="full"?-plotHeight:t;if(axis.position=="top"){yoff=-yoff}}else{y=axis.p2c(v);xoff=t=="full"?-plotWidth:t;if(axis.position=="left"){xoff=-xoff}}if(ctx.lineWidth==1){if(axis.direction=="x"){x=Math.floor(x)+0.5}else{y=Math.floor(y)+0.5}}ctx.moveTo(x,y);ctx.lineTo(x+xoff,y+yoff)}ctx.stroke()}if(bw){bc=options.grid.borderColor;if(typeof bw=="object"||typeof bc=="object"){if(typeof bw!=="object"){bw={top:bw,right:bw,bottom:bw,left:bw}}if(typeof bc!=="object"){bc={top:bc,right:bc,bottom:bc,left:bc}}if(bw.top>0){ctx.strokeStyle=bc.top;ctx.lineWidth=bw.top;ctx.beginPath();ctx.moveTo(0-bw.left,0-bw.top/2);ctx.lineTo(plotWidth,0-bw.top/2);ctx.stroke()}if(bw.right>0){ctx.strokeStyle=bc.right;ctx.lineWidth=bw.right;ctx.beginPath();ctx.moveTo(plotWidth+bw.right/2,0-bw.top);ctx.lineTo(plotWidth+bw.right/2,plotHeight);ctx.stroke()}if(bw.bottom>0){ctx.strokeStyle=bc.bottom;ctx.lineWidth=bw.bottom;ctx.beginPath();ctx.moveTo(plotWidth+bw.right,plotHeight+bw.bottom/2);ctx.lineTo(0,plotHeight+bw.bottom/2);ctx.stroke()}if(bw.left>0){ctx.strokeStyle=bc.left;ctx.lineWidth=bw.left;ctx.beginPath();ctx.moveTo(0-bw.left/2,plotHeight+bw.bottom);ctx.lineTo(0-bw.left/2,0);ctx.stroke()}}else{ctx.lineWidth=bw;ctx.strokeStyle=options.grid.borderColor;ctx.strokeRect(-bw/2,-bw/2,plotWidth+bw,plotHeight+bw)}}ctx.restore()}function drawAxisLabels(){$.each(allAxes(),function(_,axis){var box=axis.box,legacyStyles=axis.direction+"Axis "+axis.direction+axis.n+"Axis",layer="flot-"+axis.direction+"-axis flot-"+axis.direction+axis.n+"-axis "+legacyStyles,font=axis.options.font||"flot-tick-label tickLabel",tick,x,y,halign,valign;surface.removeText(layer);if(!axis.show||axis.ticks.length==0){return}for(var i=0;i<axis.ticks.length;++i){tick=axis.ticks[i];if(!tick.label||tick.v<axis.min||tick.v>axis.max){continue}if(axis.direction=="x"){halign="center";x=plotOffset.left+axis.p2c(tick.v);if(axis.position=="bottom"){y=box.top+box.padding}else{y=box.top+box.height-box.padding;valign="bottom"}}else{valign="middle";y=plotOffset.top+axis.p2c(tick.v);if(axis.position=="left"){x=box.left+box.width-box.padding;halign="right"}else{x=box.left+box.padding}}surface.addText(layer,x,y,tick.label,font,null,null,halign,valign)}})}function drawSeries(series){if(series.lines.show){drawSeriesLines(series)}if(series.bars.show){drawSeriesBars(series)}if(series.points.show){drawSeriesPoints(series)}}function drawSeriesLines(series){function plotLine(datapoints,xoffset,yoffset,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null;ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null){continue}if(y1<=y2&&y1<axisy.min){if(y2<axisy.min){continue}x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else{if(y2<=y1&&y2<axisy.min){if(y1<axisy.min){continue}x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}}if(y1>=y2&&y1>axisy.max){if(y2>axisy.max){continue}x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else{if(y2>=y1&&y2>axisy.max){if(y1>axisy.max){continue}x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}}if(x1<=x2&&x1<axisx.min){if(x2<axisx.min){continue}y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min}else{if(x2<=x1&&x2<axisx.min){if(x1<axisx.min){continue}y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min}}if(x1>=x2&&x1>axisx.max){if(x2>axisx.max){continue}y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else{if(x2>=x1&&x2>axisx.max){if(x1>axisx.max){continue}y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}}if(x1!=prevx||y1!=prevy){ctx.moveTo(axisx.p2c(x1)+xoffset,axisy.p2c(y1)+yoffset)}prevx=x2;prevy=y2;ctx.lineTo(axisx.p2c(x2)+xoffset,axisy.p2c(y2)+yoffset)}ctx.stroke()}function plotLineArea(datapoints,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize,bottom=Math.min(Math.max(0,axisy.min),axisy.max),i=0,top,areaOpen=false,ypos=1,segmentStart=0,segmentEnd=0;while(true){if(ps>0&&i>points.length+ps){break}i+=ps;var x1=points[i-ps],y1=points[i-ps+ypos],x2=points[i],y2=points[i+ypos];if(areaOpen){if(ps>0&&x1!=null&&x2==null){segmentEnd=i;ps=-ps;ypos=2;continue}if(ps<0&&i==segmentStart+ps){ctx.fill();areaOpen=false;ps=-ps;ypos=1;i=segmentStart=segmentEnd+ps;continue}}if(x1==null||x2==null){continue}if(x1<=x2&&x1<axisx.min){if(x2<axisx.min){continue}y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min}else{if(x2<=x1&&x2<axisx.min){if(x1<axisx.min){continue}y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min}}if(x1>=x2&&x1>axisx.max){if(x2>axisx.max){continue}y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else{if(x2>=x1&&x2>axisx.max){if(x1>axisx.max){continue}y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}}if(!areaOpen){ctx.beginPath();ctx.moveTo(axisx.p2c(x1),axisy.p2c(bottom));areaOpen=true}if(y1>=axisy.max&&y2>=axisy.max){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.max));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.max));continue}else{if(y1<=axisy.min&&y2<=axisy.min){ctx.lineTo(axisx.p2c(x1),axisy.p2c(axisy.min));ctx.lineTo(axisx.p2c(x2),axisy.p2c(axisy.min));continue}}var x1old=x1,x2old=x2;if(y1<=y2&&y1<axisy.min&&y2>=axisy.min){x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else{if(y2<=y1&&y2<axisy.min&&y1>=axisy.min){x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}}if(y1>=y2&&y1>axisy.max&&y2<=axisy.max){x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else{if(y2>=y1&&y2>axisy.max&&y1<=axisy.max){x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}}if(x1!=x1old){ctx.lineTo(axisx.p2c(x1old),axisy.p2c(y1))}ctx.lineTo(axisx.p2c(x1),axisy.p2c(y1));ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));if(x2!=x2old){ctx.lineTo(axisx.p2c(x2),axisy.p2c(y2));ctx.lineTo(axisx.p2c(x2old),axisy.p2c(y2))}}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineJoin="round";var lw=series.lines.lineWidth,sw=series.shadowSize;if(lw>0&&sw>0){ctx.lineWidth=sw;ctx.strokeStyle="rgba(0,0,0,0.1)";var angle=Math.PI/18;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/2),Math.cos(angle)*(lw/2+sw/2),series.xaxis,series.yaxis);ctx.lineWidth=sw/2;plotLine(series.datapoints,Math.sin(angle)*(lw/2+sw/4),Math.cos(angle)*(lw/2+sw/4),series.xaxis,series.yaxis)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;var fillStyle=getFillStyle(series.lines,series.color,0,plotHeight);if(fillStyle){ctx.fillStyle=fillStyle;plotLineArea(series.datapoints,series.xaxis,series.yaxis)}if(lw>0){plotLine(series.datapoints,0,0,series.xaxis,series.yaxis)}ctx.restore()}function drawSeriesPoints(series){function plotPoints(datapoints,radius,fillStyle,offset,shadow,axisx,axisy,symbol){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;i<points.length;i+=ps){var x=points[i],y=points[i+1];if(x==null||x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max){continue}ctx.beginPath();x=axisx.p2c(x);y=axisy.p2c(y)+offset;if(symbol=="circle"){ctx.arc(x,y,radius,0,shadow?Math.PI:Math.PI*2,false)}else{symbol(ctx,x,y,radius,shadow)}ctx.closePath();if(fillStyle){ctx.fillStyle=fillStyle;ctx.fill()}ctx.stroke()}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);var lw=series.points.lineWidth,sw=series.shadowSize,radius=series.points.radius,symbol=series.points.symbol;if(lw==0){lw=0.0001}if(lw>0&&sw>0){var w=sw/2;ctx.lineWidth=w;ctx.strokeStyle="rgba(0,0,0,0.1)";plotPoints(series.datapoints,radius,null,w+w/2,true,series.xaxis,series.yaxis,symbol);ctx.strokeStyle="rgba(0,0,0,0.2)";plotPoints(series.datapoints,radius,null,w/2,true,series.xaxis,series.yaxis,symbol)}ctx.lineWidth=lw;ctx.strokeStyle=series.color;plotPoints(series.datapoints,radius,getFillStyle(series.points,series.color),0,false,series.xaxis,series.yaxis,symbol);ctx.restore()}function drawBar(x,y,b,barLeft,barRight,fillStyleCallback,axisx,axisy,c,horizontal,lineWidth){var left,right,bottom,top,drawLeft,drawRight,drawTop,drawBottom,tmp;if(horizontal){drawBottom=drawRight=drawTop=true;drawLeft=false;left=b;right=x;top=y+barLeft;bottom=y+barRight;if(right<left){tmp=right;right=left;left=tmp;drawLeft=true;drawRight=false}}else{drawLeft=drawRight=drawTop=true;drawBottom=false;left=x+barLeft;right=x+barRight;bottom=b;top=y;if(top<bottom){tmp=top;top=bottom;bottom=tmp;drawBottom=true;drawTop=false}}if(right<axisx.min||left>axisx.max||top<axisy.min||bottom>axisy.max){return}if(left<axisx.min){left=axisx.min;drawLeft=false}if(right>axisx.max){right=axisx.max;drawRight=false}if(bottom<axisy.min){bottom=axisy.min;drawBottom=false}if(top>axisy.max){top=axisy.max;drawTop=false}left=axisx.p2c(left);bottom=axisy.p2c(bottom);right=axisx.p2c(right);top=axisy.p2c(top);if(fillStyleCallback){c.fillStyle=fillStyleCallback(bottom,top);c.fillRect(left,top,right-left,bottom-top)}if(lineWidth>0&&(drawLeft||drawRight||drawTop||drawBottom)){c.beginPath();c.moveTo(left,bottom);if(drawLeft){c.lineTo(left,top)}else{c.moveTo(left,top)}if(drawTop){c.lineTo(right,top)}else{c.moveTo(right,top)}if(drawRight){c.lineTo(right,bottom)}else{c.moveTo(right,bottom)}if(drawBottom){c.lineTo(left,bottom)}else{c.moveTo(left,bottom)}c.stroke()}}function drawSeriesBars(series){function plotBars(datapoints,barLeft,barRight,fillStyleCallback,axisx,axisy){var points=datapoints.points,ps=datapoints.pointsize;for(var i=0;i<points.length;i+=ps){if(points[i]==null){continue}drawBar(points[i],points[i+1],points[i+2],barLeft,barRight,fillStyleCallback,axisx,axisy,ctx,series.bars.horizontal,series.bars.lineWidth)}}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineWidth=series.bars.lineWidth;ctx.strokeStyle=series.color;var barLeft;switch(series.bars.align){case"left":barLeft=0;break;case"right":barLeft=-series.bars.barWidth;break;default:barLeft=-series.bars.barWidth/2}var fillStyleCallback=series.bars.fill?function(bottom,top){return getFillStyle(series.bars,series.color,bottom,top)}:null;plotBars(series.datapoints,barLeft,barLeft+series.bars.barWidth,fillStyleCallback,series.xaxis,series.yaxis);ctx.restore()}function getFillStyle(filloptions,seriesColor,bottom,top){var fill=filloptions.fill;if(!fill){return null}if(filloptions.fillColor){return getColorOrGradient(filloptions.fillColor,bottom,top,seriesColor)}var c=$.color.parse(seriesColor);c.a=typeof fill=="number"?fill:0.4;c.normalize();return c.toString()}function insertLegend(){placeholder.find(".legend").remove();if(!options.legend.show){return}var fragments=[],entries=[],rowStarted=false,lf=options.legend.labelFormatter,s,label;for(var i=0;i<series.length;++i){s=series[i];if(s.label){label=lf?lf(s.label,s):s.label;if(label){entries.push({label:label,color:s.color})}}}if(options.legend.sorted){if($.isFunction(options.legend.sorted)){entries.sort(options.legend.sorted)}else{if(options.legend.sorted=="reverse"){entries.reverse()}else{var ascending=options.legend.sorted!="descending";entries.sort(function(a,b){return a.label==b.label?0:((a.label<b.label)!=ascending?1:-1)})}}}for(var i=0;i<entries.length;++i){var entry=entries[i];if(i%options.legend.noColumns==0){if(rowStarted){fragments.push("</tr>")}fragments.push("<tr>");rowStarted=true}fragments.push('<td class="legendColorBox"><div style="border:1px solid '+options.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+entry.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+entry.label+"</td>")}if(rowStarted){fragments.push("</tr>")}if(fragments.length==0){return}var table='<table style="font-size:smaller;color:'+options.grid.color+'">'+fragments.join("")+"</table>";if(options.legend.container!=null){$(options.legend.container).html(table)}else{var pos="",p=options.legend.position,m=options.legend.margin;if(m[0]==null){m=[m,m]}if(p.charAt(0)=="n"){pos+="top:"+(m[1]+plotOffset.top)+"px;"}else{if(p.charAt(0)=="s"){pos+="bottom:"+(m[1]+plotOffset.bottom)+"px;"}}if(p.charAt(1)=="e"){pos+="right:"+(m[0]+plotOffset.right)+"px;"}else{if(p.charAt(1)=="w"){pos+="left:"+(m[0]+plotOffset.left)+"px;"}}var legend=$('<div class="legend">'+table.replace('style="','style="position:absolute;'+pos+";")+"</div>").appendTo(placeholder);if(options.legend.backgroundOpacity!=0){var c=options.legend.backgroundColor;if(c==null){c=options.grid.backgroundColor;if(c&&typeof c=="string"){c=$.color.parse(c)}else{c=$.color.extract(legend,"background-color")}c.a=1;c=c.toString()}var div=legend.children();$('<div style="position:absolute;width:'+div.width()+"px;height:"+div.height()+"px;"+pos+"background-color:"+c+';"> </div>').prependTo(legend).css("opacity",options.legend.backgroundOpacity)}}}var highlights=[],redrawTimeout=null;function findNearbyItem(mouseX,mouseY,seriesFilter){var maxDistance=options.grid.mouseActiveRadius,smallestDistance=maxDistance*maxDistance+1,item=null,foundPoint=false,i,j,ps;for(i=series.length-1;i>=0;--i){if(!seriesFilter(series[i])){continue}var s=series[i],axisx=s.xaxis,axisy=s.yaxis,points=s.datapoints.points,mx=axisx.c2p(mouseX),my=axisy.c2p(mouseY),maxx=maxDistance/axisx.scale,maxy=maxDistance/axisy.scale;ps=s.datapoints.pointsize;if(axisx.options.inverseTransform){maxx=Number.MAX_VALUE}if(axisy.options.inverseTransform){maxy=Number.MAX_VALUE}if(s.lines.show||s.points.show){for(j=0;j<points.length;j+=ps){var x=points[j],y=points[j+1];if(x==null){continue}if(x-mx>maxx||x-mx<-maxx||y-my>maxy||y-my<-maxy){continue}var dx=Math.abs(axisx.p2c(x)-mouseX),dy=Math.abs(axisy.p2c(y)-mouseY),dist=dx*dx+dy*dy;if(dist<smallestDistance){smallestDistance=dist;item=[i,j/ps]}}}if(s.bars.show&&!item){var barLeft,barRight;switch(s.bars.align){case"left":barLeft=0;break;case"right":barLeft=-s.bars.barWidth;break;default:barLeft=-s.bars.barWidth/2}barRight=barLeft+s.bars.barWidth;for(j=0;j<points.length;j+=ps){var x=points[j],y=points[j+1],b=points[j+2];if(x==null){continue}if(series[i].bars.horizontal?(mx<=Math.max(b,x)&&mx>=Math.min(b,x)&&my>=y+barLeft&&my<=y+barRight):(mx>=x+barLeft&&mx<=x+barRight&&my>=Math.min(b,y)&&my<=Math.max(b,y))){item=[i,j/ps]}}}}if(item){i=item[0];j=item[1];ps=series[i].datapoints.pointsize;return{datapoint:series[i].datapoints.points.slice(j*ps,(j+1)*ps),dataIndex:j,series:series[i],seriesIndex:i}}return null}function onMouseMove(e){if(options.grid.hoverable){triggerClickHoverEvent("plothover",e,function(s){return s.hoverable!=false})}}function onMouseLeave(e){if(options.grid.hoverable){triggerClickHoverEvent("plothover",e,function(s){return false})}}function onClick(e){triggerClickHoverEvent("plotclick",e,function(s){return s.clickable!=false})}function triggerClickHoverEvent(eventname,event,seriesFilter){var offset=eventHolder.offset(),canvasX=event.pageX-offset.left-plotOffset.left,canvasY=event.pageY-offset.top-plotOffset.top,pos=canvasToAxisCoords({left:canvasX,top:canvasY});pos.pageX=event.pageX;pos.pageY=event.pageY;var item=findNearbyItem(canvasX,canvasY,seriesFilter);if(item){item.pageX=parseInt(item.series.xaxis.p2c(item.datapoint[0])+offset.left+plotOffset.left,10);item.pageY=parseInt(item.series.yaxis.p2c(item.datapoint[1])+offset.top+plotOffset.top,10)}if(options.grid.autoHighlight){for(var i=0;i<highlights.length;++i){var h=highlights[i];if(h.auto==eventname&&!(item&&h.series==item.series&&h.point[0]==item.datapoint[0]&&h.point[1]==item.datapoint[1])){unhighlight(h.series,h.point)}}if(item){highlight(item.series,item.datapoint,eventname)}}placeholder.trigger(eventname,[pos,item])}function triggerRedrawOverlay(){var t=options.interaction.redrawOverlayInterval;if(t==-1){drawOverlay();return}if(!redrawTimeout){redrawTimeout=setTimeout(drawOverlay,t)}}function drawOverlay(){redrawTimeout=null;octx.save();overlay.clear();octx.translate(plotOffset.left,plotOffset.top);var i,hi;for(i=0;i<highlights.length;++i){hi=highlights[i];if(hi.series.bars.show){drawBarHighlight(hi.series,hi.point)}else{drawPointHighlight(hi.series,hi.point)}}octx.restore();executeHooks(hooks.drawOverlay,[octx])}function highlight(s,point,auto){if(typeof s=="number"){s=series[s]}if(typeof point=="number"){var ps=s.datapoints.pointsize;point=s.datapoints.points.slice(ps*point,ps*(point+1))}var i=indexOfHighlight(s,point);if(i==-1){highlights.push({series:s,point:point,auto:auto});triggerRedrawOverlay()}else{if(!auto){highlights[i].auto=false}}}function unhighlight(s,point){if(s==null&&point==null){highlights=[];triggerRedrawOverlay();return}if(typeof s=="number"){s=series[s]}if(typeof point=="number"){var ps=s.datapoints.pointsize;point=s.datapoints.points.slice(ps*point,ps*(point+1))}var i=indexOfHighlight(s,point);if(i!=-1){highlights.splice(i,1);triggerRedrawOverlay()}}function indexOfHighlight(s,p){for(var i=0;i<highlights.length;++i){var h=highlights[i];if(h.series==s&&h.point[0]==p[0]&&h.point[1]==p[1]){return i}}return -1}function drawPointHighlight(series,point){var x=point[0],y=point[1],axisx=series.xaxis,axisy=series.yaxis,highlightColor=(typeof series.highlightColor==="string")?series.highlightColor:$.color.parse(series.color).scale("a",0.5).toString();if(x<axisx.min||x>axisx.max||y<axisy.min||y>axisy.max){return}var pointRadius=series.points.radius+series.points.lineWidth/2;octx.lineWidth=pointRadius;octx.strokeStyle=highlightColor;var radius=1.5*pointRadius;x=axisx.p2c(x);y=axisy.p2c(y);octx.beginPath();if(series.points.symbol=="circle"){octx.arc(x,y,radius,0,2*Math.PI,false)}else{series.points.symbol(octx,x,y,radius,false)}octx.closePath();octx.stroke()}function drawBarHighlight(series,point){var highlightColor=(typeof series.highlightColor==="string")?series.highlightColor:$.color.parse(series.color).scale("a",0.5).toString(),fillStyle=highlightColor,barLeft;switch(series.bars.align){case"left":barLeft=0;break;case"right":barLeft=-series.bars.barWidth;break;default:barLeft=-series.bars.barWidth/2}octx.lineWidth=series.bars.lineWidth;octx.strokeStyle=highlightColor;drawBar(point[0],point[1],point[2]||0,barLeft,barLeft+series.bars.barWidth,function(){return fillStyle},series.xaxis,series.yaxis,octx,series.bars.horizontal,series.bars.lineWidth)}function getColorOrGradient(spec,bottom,top,defaultColor){if(typeof spec=="string"){return spec}else{var gradient=ctx.createLinearGradient(0,top,0,bottom);for(var i=0,l=spec.colors.length;i<l;++i){var c=spec.colors[i];if(typeof c!="string"){var co=$.color.parse(defaultColor);if(c.brightness!=null){co=co.scale("rgb",c.brightness)}if(c.opacity!=null){co.a*=c.opacity}c=co.toString()}gradient.addColorStop(i/(l-1),c)}return gradient}}}$.plot=function(placeholder,data,options){var plot=new Plot($(placeholder),data,options,$.plot.plugins);return plot};$.plot.version="0.8.2-alpha";$.plot.plugins=[];$.fn.plot=function(data,options){return this.each(function(){$.plot(this,data,options)})};function floorInBase(n,base){return base*Math.floor(n/base)}})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.flot.tickrotor.js b/app/assets/javascripts/jquery.flot.tickrotor.js new file mode 100644 index 000000000..404b2b0a7 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.tickrotor.js @@ -0,0 +1,205 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is flot-tickrotor. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Cote <mcote@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * flot-tickrotor: flot plugin to display angled X-axis tick labels. + * + * Requires flot 0.7 or higher and a browser supporting <canvas>. + * + * To activate, just set xaxis.rotateTicks to an angle in degrees. Labels + * are rotated clockwise, so if you want the labels to angle up and to the right (/) + * you need to provide an angle > 90. The text will be flipped so that it is still + * right-side-up. + * Angles greater than or equal to 180 are ignored. + */ +(function ($) { + var options = { }; + + function init(plot) { + // Taken from flot-axislabels. + // This is kind of a hack. There are no hooks in Flot between + // the creation and measuring of the ticks (setTicks, measureTickLabels + // in setupGrid() ) and the drawing of the ticks and plot box + // (insertAxisLabels in setupGrid() ). + // + // Therefore, we use a trick where we run the draw routine twice: + // the first time to get the tick measurements, so that we can change + // them, and then have it draw it again. + var ticks = []; + var font; + var secondPass = false; + var rotateTicks, rotateTicksRads, radsAboveHoriz; + plot.hooks.draw.push(function (plot, ctx) { + if (!secondPass) { + var opts = plot.getAxes().xaxis.options; + if (opts.rotateTicks === undefined) { + return; + } + + rotateTicks = parseInt(opts.rotateTicks, 10); + if (rotateTicks.toString() != opts.rotateTicks || rotateTicks == 0 || rotateTicks >= 180) { + return; + } + + rotateTicksRads = rotateTicks * Math.PI/180; + if (rotateTicks > 90) { + radsAboveHoriz = Math.PI - rotateTicksRads; + } else { + radsAboveHoriz = Math.PI/2 - rotateTicksRads; + } + + font = opts.rotateTicksFont; + if (!font) { + font = $('.tickLabel').css('font'); + } + if (!font) { + font = 'smaller sans-serif'; + } + + var elem, maxLabelWidth = 0, maxLabelHeight = 0, minX = 0, maxX = 0; + + var xaxis = plot.getAxes().xaxis; + ticks = plot.getAxes().xaxis.ticks; + opts.ticks = []; // we'll make our own + + var x; + for (var i = 0; i < ticks.length; i++) { + elem = $('<span style="font:' + font + '">' + ticks[i].label + '</span>'); + plot.getPlaceholder().append(elem); + ticks[i].height = elem.outerHeight(true); + ticks[i].width = elem.outerWidth(true); + elem.remove(); + if (ticks[i].height > maxLabelHeight) { + maxLabelHeight = ticks[i].height; + } + if (ticks[i].width > maxLabelWidth) { + maxLabelWidth = ticks[i].width; + } + var tick = ticks[i]; + // See second-draw code below for explanation of offsets. + if (rotateTicks > 90) { + // See if any labels are too long and require increased left + // padding. + x = Math.round(plot.getPlotOffset().left + xaxis.p2c(tick.v)) + - Math.ceil(Math.cos(radsAboveHoriz) * tick.height) + - Math.ceil(Math.cos(radsAboveHoriz) * tick.width); + if (x < minX) { + minX = x; + } + } else { + // See if any labels are too long and require increased right + // padding. + x = Math.round(plot.getPlotOffset().left + xaxis.p2c(tick.v)) + + Math.ceil(Math.cos(radsAboveHoriz) * tick.height) + + Math.ceil(Math.cos(radsAboveHoriz) * tick.width); + if (x > maxX) { + maxX = x; + } + } + } + + // Calculate maximum label height after rotating. + if (rotateTicks > 90) { + var acuteRads = rotateTicksRads - Math.PI/2; + opts.labelHeight = Math.ceil(Math.sin(acuteRads) * maxLabelWidth) + + Math.ceil(Math.sin(acuteRads) * maxLabelHeight); + } else { + var acuteRads = Math.PI/2 - rotateTicksRads; + // Center such that the top of the label is at the center of the tick. + opts.labelHeight = Math.ceil(Math.sin(rotateTicksRads) * maxLabelWidth) + + Math.ceil(Math.sin(acuteRads) * maxLabelHeight); + } + + if (minX < 0) { + plot.getAxes().yaxis.options.labelWidth = -1 * minX; + } + + // Doesn't seem to work if there are no values using the second y axis. + //if (maxX > xaxis.box.left + xaxis.box.width) { + // plot.getAxes().y2axis.options.labelWidth = maxX - xaxis.box.left - xaxis.box.width; + //} + + // re-draw with new label widths and heights + secondPass = true; + plot.setupGrid(); + plot.draw(); + } else { + if (ticks.length == 0) { + return; + } + var xaxis = plot.getAxes().xaxis; + var box = xaxis.box; + var tick, label, xoffset, yoffset; + for (var i = 0; i < ticks.length; i++) { + tick = ticks[i]; + if (!tick.label) { + continue; + } + ctx.save(); + ctx.font = font; + if (rotateTicks <= 90) { + // Center such that the top of the label is at the center of the tick. + xoffset = -Math.ceil(Math.cos(radsAboveHoriz) * tick.height); + yoffset = Math.ceil(Math.sin(radsAboveHoriz) * tick.height); + ctx.translate(Math.round(plot.getPlotOffset().left + xaxis.p2c(tick.v)) + xoffset, + box.top + box.padding + plot.getOptions().grid.labelMargin + yoffset); + ctx.rotate(rotateTicksRads); + } else { + // We want the text to facing up, so we have to rotate counterclockwise, + // which means the label has to *end* at the center of the tick. + xoffset = Math.ceil(Math.cos(radsAboveHoriz) * tick.height) + - Math.ceil(Math.cos(radsAboveHoriz) * tick.width); + yoffset = Math.ceil(Math.sin(radsAboveHoriz) * tick.width) + + Math.ceil(Math.sin(radsAboveHoriz) * tick.height); + ctx.translate(Math.round(plot.getPlotOffset().left + xaxis.p2c(tick.v) + xoffset), + box.top + box.padding + plot.getOptions().grid.labelMargin + yoffset); + ctx.rotate(-radsAboveHoriz); + } + ctx.fillText(tick.label, 0, 0); + ctx.restore(); + } + } + }); + } + + $.plot.plugins.push({ + init: init, + options: options, + name: 'tickRotor', + version: '1.0' + }); +})(jQuery); diff --git a/app/assets/javascripts/jquery.flot.tickrotor.min.js b/app/assets/javascripts/jquery.flot.tickrotor.min.js new file mode 100644 index 000000000..87fc79d10 --- /dev/null +++ b/app/assets/javascripts/jquery.flot.tickrotor.min.js @@ -0,0 +1 @@ +(function($){var options={};function init(plot){var ticks=[];var font;var secondPass=false;var rotateTicks,rotateTicksRads,radsAboveHoriz;plot.hooks.draw.push(function(plot,ctx){if(!secondPass){var opts=plot.getAxes().xaxis.options;if(opts.rotateTicks===undefined){return}rotateTicks=parseInt(opts.rotateTicks,10);if(rotateTicks.toString()!=opts.rotateTicks||rotateTicks==0||rotateTicks>=180){return}rotateTicksRads=rotateTicks*Math.PI/180;if(rotateTicks>90){radsAboveHoriz=Math.PI-rotateTicksRads}else{radsAboveHoriz=Math.PI/2-rotateTicksRads}font=opts.rotateTicksFont;if(!font){font=$(".tickLabel").css("font")}if(!font){font="smaller sans-serif"}var elem,maxLabelWidth=0,maxLabelHeight=0,minX=0,maxX=0;var xaxis=plot.getAxes().xaxis;ticks=plot.getAxes().xaxis.ticks;opts.ticks=[];var x;for(var i=0;i<ticks.length;i++){elem=$('<span style="font:'+font+'">'+ticks[i].label+"</span>");plot.getPlaceholder().append(elem);ticks[i].height=elem.outerHeight(true);ticks[i].width=elem.outerWidth(true);elem.remove();if(ticks[i].height>maxLabelHeight){maxLabelHeight=ticks[i].height}if(ticks[i].width>maxLabelWidth){maxLabelWidth=ticks[i].width}var tick=ticks[i];if(rotateTicks>90){x=Math.round(plot.getPlotOffset().left+xaxis.p2c(tick.v))-Math.ceil(Math.cos(radsAboveHoriz)*tick.height)-Math.ceil(Math.cos(radsAboveHoriz)*tick.width);if(x<minX){minX=x}}else{x=Math.round(plot.getPlotOffset().left+xaxis.p2c(tick.v))+Math.ceil(Math.cos(radsAboveHoriz)*tick.height)+Math.ceil(Math.cos(radsAboveHoriz)*tick.width);if(x>maxX){maxX=x}}}if(rotateTicks>90){var acuteRads=rotateTicksRads-Math.PI/2;opts.labelHeight=Math.ceil(Math.sin(acuteRads)*maxLabelWidth)+Math.ceil(Math.sin(acuteRads)*maxLabelHeight)}else{var acuteRads=Math.PI/2-rotateTicksRads;opts.labelHeight=Math.ceil(Math.sin(rotateTicksRads)*maxLabelWidth)+Math.ceil(Math.sin(acuteRads)*maxLabelHeight)}if(minX<0){plot.getAxes().yaxis.options.labelWidth=-1*minX}secondPass=true;plot.setupGrid();plot.draw()}else{if(ticks.length==0){return}var xaxis=plot.getAxes().xaxis;var box=xaxis.box;var tick,label,xoffset,yoffset;for(var i=0;i<ticks.length;i++){tick=ticks[i];if(!tick.label){continue}ctx.save();ctx.font=font;if(rotateTicks<=90){xoffset=-Math.ceil(Math.cos(radsAboveHoriz)*tick.height);yoffset=Math.ceil(Math.sin(radsAboveHoriz)*tick.height);ctx.translate(Math.round(plot.getPlotOffset().left+xaxis.p2c(tick.v))+xoffset,box.top+box.padding+plot.getOptions().grid.labelMargin+yoffset);ctx.rotate(rotateTicksRads)}else{xoffset=Math.ceil(Math.cos(radsAboveHoriz)*tick.height)-Math.ceil(Math.cos(radsAboveHoriz)*tick.width);yoffset=Math.ceil(Math.sin(radsAboveHoriz)*tick.width)+Math.ceil(Math.sin(radsAboveHoriz)*tick.height);ctx.translate(Math.round(plot.getPlotOffset().left+xaxis.p2c(tick.v)+xoffset),box.top+box.padding+plot.getOptions().grid.labelMargin+yoffset);ctx.rotate(-radsAboveHoriz)}ctx.fillText(tick.label,0,0);ctx.restore()}}})}$.plot.plugins.push({init:init,options:options,name:"tickRotor",version:"1.0"})})(jQuery);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.form.js b/app/assets/javascripts/jquery.form.js new file mode 100644 index 000000000..bc0061418 --- /dev/null +++ b/app/assets/javascripts/jquery.form.js @@ -0,0 +1,11 @@ +/*! + * jQuery Form Plugin + * version: 2.83 (11-JUL-2011) + * @requires jQuery v1.3.2 or later + * + * Examples and documentation at: http://malsup.com/jquery/form/ + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ +(function(a){function b(){var a="[jquery.form] "+Array.prototype.join.call(arguments,"");if(window.console&&window.console.log){window.console.log(a)}else if(window.opera&&window.opera.postError){window.opera.postError(a)}}a.fn.ajaxSubmit=function(c){function t(e){function C(c){if(o.aborted||B){return}try{z=w(n)}catch(d){b("cannot access response document: ",d);c=v}if(c===u&&o){o.abort("timeout");return}else if(c==v&&o){o.abort("server abort");return}if(!z||z.location.href==j.iframeSrc){if(!r)return}n.detachEvent?n.detachEvent("onload",C):n.removeEventListener("load",C,false);var e="success",f;try{if(r){throw"timeout"}var g=j.dataType=="xml"||z.XMLDocument||a.isXMLDoc(z);b("isXml="+g);if(!g&&window.opera&&(z.body==null||z.body.innerHTML=="")){if(--A){b("requeing onLoad callback, DOM not available");setTimeout(C,250);return}}var h=z.body?z.body:z.documentElement;o.responseText=h?h.innerHTML:null;o.responseXML=z.XMLDocument?z.XMLDocument:z;if(g)j.dataType="xml";o.getResponseHeader=function(a){var b={"content-type":j.dataType};return b[a]};if(h){o.status=Number(h.getAttribute("status"))||o.status;o.statusText=h.getAttribute("statusText")||o.statusText}var i=j.dataType||"";var l=/(json|script|text)/.test(i.toLowerCase());if(l||j.textarea){var p=z.getElementsByTagName("textarea")[0];if(p){o.responseText=p.value;o.status=Number(p.getAttribute("status"))||o.status;o.statusText=p.getAttribute("statusText")||o.statusText}else if(l){var q=z.getElementsByTagName("pre")[0];var t=z.getElementsByTagName("body")[0];if(q){o.responseText=q.textContent?q.textContent:q.innerHTML}else if(t){o.responseText=t.innerHTML}}}else if(j.dataType=="xml"&&!o.responseXML&&o.responseText!=null){o.responseXML=D(o.responseText)}try{y=F(o,j.dataType,j)}catch(c){e="parsererror";o.error=f=c||e}}catch(c){b("error caught: ",c);e="error";o.error=f=c||e}if(o.aborted){b("upload aborted");e=null}if(o.status){e=o.status>=200&&o.status<300||o.status===304?"success":"error"}if(e==="success"){j.success&&j.success.call(j.context,y,"success",o);k&&a.event.trigger("ajaxSuccess",[o,j])}else if(e){if(f==undefined)f=o.statusText;j.error&&j.error.call(j.context,o,e,f);k&&a.event.trigger("ajaxError",[o,j,f])}k&&a.event.trigger("ajaxComplete",[o,j]);if(k&&!--a.active){a.event.trigger("ajaxStop")}j.complete&&j.complete.call(j.context,o,e);B=true;if(j.timeout)clearTimeout(s);setTimeout(function(){if(!j.iframeTarget)m.remove();o.responseXML=null},100)}function x(){function h(){try{var a=w(n).readyState;b("state = "+a);if(a.toLowerCase()=="uninitialized")setTimeout(h,50)}catch(c){b("Server abort: ",c," (",c.name,")");C(v);s&&clearTimeout(s);s=undefined}}var c=g.attr("target"),e=g.attr("action");f.setAttribute("target",l);if(!d){f.setAttribute("method","POST")}if(e!=j.url){f.setAttribute("action",j.url)}if(!j.skipEncodingOverride&&(!d||/post/i.test(d))){g.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"})}if(j.timeout){s=setTimeout(function(){r=true;C(u)},j.timeout)}var i=[];try{if(j.extraData){for(var k in j.extraData){i.push(a('<input type="hidden" name="'+k+'" />').attr("value",j.extraData[k]).appendTo(f)[0])}}if(!j.iframeTarget){m.appendTo("body");n.attachEvent?n.attachEvent("onload",C):n.addEventListener("load",C,false)}setTimeout(h,15);f.submit()}finally{f.setAttribute("action",e);if(c){f.setAttribute("target",c)}else{g.removeAttr("target")}a(i).remove()}}function w(a){var b=a.contentWindow?a.contentWindow.document:a.contentDocument?a.contentDocument:a.document;return b}var f=g[0],h,i,j,k,l,m,n,o,p,q,r,s;var t=!!a.fn.prop;if(e){for(i=0;i<e.length;i++){h=a(f[e[i].name]);h[t?"prop":"attr"]("disabled",false)}}if(a(":input[name=submit],:input[id=submit]",f).length){alert('Error: Form elements must not have name or id of "submit".');return}j=a.extend(true,{},a.ajaxSettings,c);j.context=j.context||j;l="jqFormIO"+(new Date).getTime();if(j.iframeTarget){m=a(j.iframeTarget);q=m.attr("name");if(q==null)m.attr("name",l);else l=q}else{m=a('<iframe name="'+l+'" src="'+j.iframeSrc+'" />');m.css({position:"absolute",top:"-1000px",left:"-1000px"})}n=m[0];o={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(c){var d=c==="timeout"?"timeout":"aborted";b("aborting upload... "+d);this.aborted=1;m.attr("src",j.iframeSrc);o.error=d;j.error&&j.error.call(j.context,o,d,c);k&&a.event.trigger("ajaxError",[o,j,d]);j.complete&&j.complete.call(j.context,o,d)}};k=j.global;if(k&&!(a.active++)){a.event.trigger("ajaxStart")}if(k){a.event.trigger("ajaxSend",[o,j])}if(j.beforeSend&&j.beforeSend.call(j.context,o,j)===false){if(j.global){a.active--}return}if(o.aborted){return}p=f.clk;if(p){q=p.name;if(q&&!p.disabled){j.extraData=j.extraData||{};j.extraData[q]=p.value;if(p.type=="image"){j.extraData[q+".x"]=f.clk_x;j.extraData[q+".y"]=f.clk_y}}}var u=1;var v=2;if(j.forceSync){x()}else{setTimeout(x,10)}var y,z,A=50,B;var D=a.parseXML||function(a,b){if(window.ActiveXObject){b=new ActiveXObject("Microsoft.XMLDOM");b.async="false";b.loadXML(a)}else{b=(new DOMParser).parseFromString(a,"text/xml")}return b&&b.documentElement&&b.documentElement.nodeName!="parsererror"?b:null};var E=a.parseJSON||function(a){return window["eval"]("("+a+")")};var F=function(b,c,d){var e=b.getResponseHeader("content-type")||"",f=c==="xml"||!c&&e.indexOf("xml")>=0,g=f?b.responseXML:b.responseText;if(f&&g.documentElement.nodeName==="parsererror"){a.error&&a.error("parsererror")}if(d&&d.dataFilter){g=d.dataFilter(g,c)}if(typeof g==="string"){if(c==="json"||!c&&e.indexOf("json")>=0){g=E(g)}else if(c==="script"||!c&&e.indexOf("javascript")>=0){a.globalEval(g)}}return g}}if(!this.length){b("ajaxSubmit: skipping submit process - no element selected");return this}var d,e,f,g=this;if(typeof c=="function"){c={success:c}}d=this.attr("method");e=this.attr("action");f=typeof e==="string"?a.trim(e):"";f=f||window.location.href||"";if(f){f=(f.match(/^([^#]+)/)||[])[1]}c=a.extend(true,{url:f,success:a.ajaxSettings.success,type:d||"GET",iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},c);var h={};this.trigger("form-pre-serialize",[this,c,h]);if(h.veto){b("ajaxSubmit: submit vetoed via form-pre-serialize trigger");return this}if(c.beforeSerialize&&c.beforeSerialize(this,c)===false){b("ajaxSubmit: submit aborted via beforeSerialize callback");return this}var i,j,k=this.formToArray(c.semantic);if(c.data){c.extraData=c.data;for(i in c.data){if(c.data[i]instanceof Array){for(var l in c.data[i]){k.push({name:i,value:c.data[i][l]})}}else{j=c.data[i];j=a.isFunction(j)?j():j;k.push({name:i,value:j})}}}if(c.beforeSubmit&&c.beforeSubmit(k,this,c)===false){b("ajaxSubmit: submit aborted via beforeSubmit callback");return this}this.trigger("form-submit-validate",[k,this,c,h]);if(h.veto){b("ajaxSubmit: submit vetoed via form-submit-validate trigger");return this}var m=a.param(k);if(c.type.toUpperCase()=="GET"){c.url+=(c.url.indexOf("?")>=0?"&":"?")+m;c.data=null}else{c.data=m}var n=[];if(c.resetForm){n.push(function(){g.resetForm()})}if(c.clearForm){n.push(function(){g.clearForm()})}if(!c.dataType&&c.target){var o=c.success||function(){};n.push(function(b){var d=c.replaceTarget?"replaceWith":"html";a(c.target)[d](b).each(o,arguments)})}else if(c.success){n.push(c.success)}c.success=function(a,b,d){var e=c.context||c;for(var f=0,h=n.length;f<h;f++){n[f].apply(e,[a,b,d||g,g])}};var p=a("input:file",this).length>0;var q="multipart/form-data";var r=g.attr("enctype")==q||g.attr("encoding")==q;if(c.iframe!==false&&(p||c.iframe||r)){if(c.closeKeepAlive){a.get(c.closeKeepAlive,function(){t(k)})}else{t(k)}}else{if(a.browser.msie&&d=="get"){var s=g[0].getAttribute("method");if(typeof s==="string")c.type=s}a.ajax(c)}this.trigger("form-submit-notify",[this,c]);return this};a.fn.ajaxForm=function(c){if(this.length===0){var d={s:this.selector,c:this.context};if(!a.isReady&&d.s){b("DOM not ready, queuing ajaxForm");a(function(){a(d.s,d.c).ajaxForm(c)});return this}b("terminating; zero elements found by selector"+(a.isReady?"":" (DOM not ready)"));return this}return this.ajaxFormUnbind().bind("submit.form-plugin",function(b){if(!b.isDefaultPrevented()){b.preventDefault();a(this).ajaxSubmit(c)}}).bind("click.form-plugin",function(b){var c=b.target;var d=a(c);if(!d.is(":submit,input:image")){var e=d.closest(":submit");if(e.length==0){return}c=e[0]}var f=this;f.clk=c;if(c.type=="image"){if(b.offsetX!=undefined){f.clk_x=b.offsetX;f.clk_y=b.offsetY}else if(typeof a.fn.offset=="function"){var g=d.offset();f.clk_x=b.pageX-g.left;f.clk_y=b.pageY-g.top}else{f.clk_x=b.pageX-c.offsetLeft;f.clk_y=b.pageY-c.offsetTop}}setTimeout(function(){f.clk=f.clk_x=f.clk_y=null},100)})};a.fn.ajaxFormUnbind=function(){return this.unbind("submit.form-plugin click.form-plugin")};a.fn.formToArray=function(b){var c=[];if(this.length===0){return c}var d=this[0];var e=b?d.getElementsByTagName("*"):d.elements;if(!e){return c}var f,g,h,i,j,k,l;for(f=0,k=e.length;f<k;f++){j=e[f];h=j.name;if(!h){continue}if(b&&d.clk&&j.type=="image"){if(!j.disabled&&d.clk==j){c.push({name:h,value:a(j).val()});c.push({name:h+".x",value:d.clk_x},{name:h+".y",value:d.clk_y})}continue}i=a.fieldValue(j,true);if(i&&i.constructor==Array){for(g=0,l=i.length;g<l;g++){c.push({name:h,value:i[g]})}}else if(i!==null&&typeof i!="undefined"){c.push({name:h,value:i})}}if(!b&&d.clk){var m=a(d.clk),n=m[0];h=n.name;if(h&&!n.disabled&&n.type=="image"){c.push({name:h,value:m.val()});c.push({name:h+".x",value:d.clk_x},{name:h+".y",value:d.clk_y})}}return c};a.fn.formSerialize=function(b){return a.param(this.formToArray(b))};a.fn.fieldSerialize=function(b){var c=[];this.each(function(){var d=this.name;if(!d){return}var e=a.fieldValue(this,b);if(e&&e.constructor==Array){for(var f=0,g=e.length;f<g;f++){c.push({name:d,value:e[f]})}}else if(e!==null&&typeof e!="undefined"){c.push({name:this.name,value:e})}});return a.param(c)};a.fn.fieldValue=function(b){for(var c=[],d=0,e=this.length;d<e;d++){var f=this[d];var g=a.fieldValue(f,b);if(g===null||typeof g=="undefined"||g.constructor==Array&&!g.length){continue}g.constructor==Array?a.merge(c,g):c.push(g)}return c};a.fieldValue=function(b,c){var d=b.name,e=b.type,f=b.tagName.toLowerCase();if(c===undefined){c=true}if(c&&(!d||b.disabled||e=="reset"||e=="button"||(e=="checkbox"||e=="radio")&&!b.checked||(e=="submit"||e=="image")&&b.form&&b.form.clk!=b||f=="select"&&b.selectedIndex==-1)){return null}if(f=="select"){var g=b.selectedIndex;if(g<0){return null}var h=[],i=b.options;var j=e=="select-one";var k=j?g+1:i.length;for(var l=j?g:0;l<k;l++){var m=i[l];if(m.selected){var n=m.value;if(!n){n=m.attributes&&m.attributes["value"]&&!m.attributes["value"].specified?m.text:m.value}if(j){return n}h.push(n)}}return h}return a(b).val()};a.fn.clearForm=function(){return this.each(function(){a("input,select,textarea",this).clearFields()})};a.fn.clearFields=a.fn.clearInputs=function(){var a=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var b=this.type,c=this.tagName.toLowerCase();if(a.test(b)||c=="textarea"){this.value=""}else if(b=="checkbox"||b=="radio"){this.checked=false}else if(c=="select"){this.selectedIndex=-1}})};a.fn.resetForm=function(){return this.each(function(){if(typeof this.reset=="function"||typeof this.reset=="object"&&!this.reset.nodeType){this.reset()}})};a.fn.enable=function(a){if(a===undefined){a=true}return this.each(function(){this.disabled=!a})};a.fn.selected=function(b){if(b===undefined){b=true}return this.each(function(){var c=this.type;if(c=="checkbox"||c=="radio"){this.checked=b}else if(this.tagName.toLowerCase()=="option"){var d=a(this).parent("select");if(b&&d[0]&&d[0].type=="select-one"){d.find("option").selected(false)}this.selected=b}})};})(jQuery)
\ No newline at end of file diff --git a/app/assets/javascripts/jquery.js b/app/assets/javascripts/jquery.js new file mode 100644 index 000000000..16ad06c5a --- /dev/null +++ b/app/assets/javascripts/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.2 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( +a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f +.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window);
\ No newline at end of file diff --git a/app/assets/javascripts/jquery_ujs.js b/app/assets/javascripts/jquery_ujs.js new file mode 100644 index 000000000..50121d6ed --- /dev/null +++ b/app/assets/javascripts/jquery_ujs.js @@ -0,0 +1,393 @@ +(function($, undefined) { + +/** + * Unobtrusive scripting adapter for jQuery + * https://github.com/rails/jquery-ujs + * + * Requires jQuery 1.7.0 or later. + * + * Released under the MIT license + * + */ + + // Cut down on the number of issues from people inadvertently including jquery_ujs twice + // by detecting and raising an error when it happens. + if ( $.rails !== undefined ) { + $.error('jquery-ujs has already been loaded!'); + } + + // Shorthand to make it a little easier to call public rails functions from within rails.js + var rails; + + $.rails = rails = { + // Link elements bound by jquery-ujs + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', + + // Button elements boud jquery-ujs + buttonClickSelector: 'button[data-remote]', + + // Select elements bound by jquery-ujs + inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', + + // Form elements bound by jquery-ujs + formSubmitSelector: 'form', + + // Form input elements bound by jquery-ujs + formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])', + + // Form input elements disabled during form submission + disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', + + // Form input elements re-enabled after form submission + enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', + + // Form required input elements + requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', + + // Form file input elements + fileInputSelector: 'input[type=file]', + + // Link onClick disable selector with possible reenable after remote submission + linkDisableSelector: 'a[data-disable-with]', + + // Make sure that every Ajax request sends the CSRF token + CSRFProtection: function(xhr) { + var token = $('meta[name="csrf-token"]').attr('content'); + if (token) xhr.setRequestHeader('X-CSRF-Token', token); + }, + + // Triggers an event on an element and returns false if the event result is false + fire: function(obj, name, data) { + var event = $.Event(name); + obj.trigger(event, data); + return event.result !== false; + }, + + // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm + confirm: function(message) { + return confirm(message); + }, + + // Default ajax function, may be overridden with custom function in $.rails.ajax + ajax: function(options) { + return $.ajax(options); + }, + + // Default way to get an element's href. May be overridden at $.rails.href. + href: function(element) { + return element.attr('href'); + }, + + // Submits "remote" forms and links with ajax + handleRemote: function(element) { + var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options; + + if (rails.fire(element, 'ajax:before')) { + elCrossDomain = element.data('cross-domain'); + crossDomain = elCrossDomain === undefined ? null : elCrossDomain; + withCredentials = element.data('with-credentials') || null; + dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); + + if (element.is('form')) { + method = element.attr('method'); + url = element.attr('action'); + data = element.serializeArray(); + // memoized value from clicked submit button + var button = element.data('ujs:submit-button'); + if (button) { + data.push(button); + element.data('ujs:submit-button', null); + } + } else if (element.is(rails.inputChangeSelector)) { + method = element.data('method'); + url = element.data('url'); + data = element.serialize(); + if (element.data('params')) data = data + "&" + element.data('params'); + } else if (element.is(rails.buttonClickSelector)) { + method = element.data('method') || 'get'; + url = element.data('url'); + data = element.serialize(); + if (element.data('params')) data = data + "&" + element.data('params'); + } else { + method = element.data('method'); + url = rails.href(element); + data = element.data('params') || null; + } + + options = { + type: method || 'GET', data: data, dataType: dataType, + // stopping the "ajax:beforeSend" event will cancel the ajax request + beforeSend: function(xhr, settings) { + if (settings.dataType === undefined) { + xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); + } + return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); + }, + success: function(data, status, xhr) { + element.trigger('ajax:success', [data, status, xhr]); + }, + complete: function(xhr, status) { + element.trigger('ajax:complete', [xhr, status]); + }, + error: function(xhr, status, error) { + element.trigger('ajax:error', [xhr, status, error]); + }, + crossDomain: crossDomain + }; + + // There is no withCredentials for IE6-8 when + // "Enable native XMLHTTP support" is disabled + if (withCredentials) { + options.xhrFields = { + withCredentials: withCredentials + }; + } + + // Only pass url to `ajax` options if not blank + if (url) { options.url = url; } + + var jqxhr = rails.ajax(options); + element.trigger('ajax:send', jqxhr); + return jqxhr; + } else { + return false; + } + }, + + // Handles "data-method" on links such as: + // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> + handleMethod: function(link) { + var href = rails.href(link), + method = link.data('method'), + target = link.attr('target'), + csrf_token = $('meta[name=csrf-token]').attr('content'), + csrf_param = $('meta[name=csrf-param]').attr('content'), + form = $('<form method="post" action="' + href + '"></form>'), + metadata_input = '<input name="_method" value="' + method + '" type="hidden" />'; + + if (csrf_param !== undefined && csrf_token !== undefined) { + metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />'; + } + + if (target) { form.attr('target', target); } + + form.hide().append(metadata_input).appendTo('body'); + form.submit(); + }, + + /* Disables form elements: + - Caches element value in 'ujs:enable-with' data store + - Replaces element text with value of 'data-disable-with' attribute + - Sets disabled property to true + */ + disableFormElements: function(form) { + form.find(rails.disableSelector).each(function() { + var element = $(this), method = element.is('button') ? 'html' : 'val'; + element.data('ujs:enable-with', element[method]()); + element[method](element.data('disable-with')); + element.prop('disabled', true); + }); + }, + + /* Re-enables disabled form elements: + - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) + - Sets disabled property to false + */ + enableFormElements: function(form) { + form.find(rails.enableSelector).each(function() { + var element = $(this), method = element.is('button') ? 'html' : 'val'; + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); + element.prop('disabled', false); + }); + }, + + /* For 'data-confirm' attribute: + - Fires `confirm` event + - Shows the confirmation dialog + - Fires the `confirm:complete` event + + Returns `true` if no function stops the chain and user chose yes; `false` otherwise. + Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. + Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function + return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. + */ + allowAction: function(element) { + var message = element.data('confirm'), + answer = false, callback; + if (!message) { return true; } + + if (rails.fire(element, 'confirm')) { + answer = rails.confirm(message); + callback = rails.fire(element, 'confirm:complete', [answer]); + } + return answer && callback; + }, + + // Helper function which checks for blank inputs in a form that match the specified CSS selector + blankInputs: function(form, specifiedSelector, nonBlank) { + var inputs = $(), input, valueToCheck, + selector = specifiedSelector || 'input,textarea', + allInputs = form.find(selector); + + allInputs.each(function() { + input = $(this); + valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val(); + // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey + if (!valueToCheck === !nonBlank) { + + // Don't count unchecked required radio if other radio with same name is checked + if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) { + return true; // Skip to next input + } + + inputs = inputs.add(input); + } + }); + return inputs.length ? inputs : false; + }, + + // Helper function which checks for non-blank inputs in a form that match the specified CSS selector + nonBlankInputs: function(form, specifiedSelector) { + return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank + }, + + // Helper function, needed to provide consistent behavior in IE + stopEverything: function(e) { + $(e.target).trigger('ujs:everythingStopped'); + e.stopImmediatePropagation(); + return false; + }, + + // replace element's html with the 'data-disable-with' after storing original html + // and prevent clicking on it + disableElement: function(element) { + element.data('ujs:enable-with', element.html()); // store enabled state + element.html(element.data('disable-with')); // set to disabled state + element.bind('click.railsDisable', function(e) { // prevent further clicking + return rails.stopEverything(e); + }); + }, + + // restore element to its original state which was disabled by 'disableElement' above + enableElement: function(element) { + if (element.data('ujs:enable-with') !== undefined) { + element.html(element.data('ujs:enable-with')); // set to old enabled state + element.removeData('ujs:enable-with'); // clean up cache + } + element.unbind('click.railsDisable'); // enable element + } + + }; + + if (rails.fire($(document), 'rails:attachBindings')) { + + $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); + + $(document).delegate(rails.linkDisableSelector, 'ajax:complete', function() { + rails.enableElement($(this)); + }); + + $(document).delegate(rails.linkClickSelector, 'click.rails', function(e) { + var link = $(this), method = link.data('method'), data = link.data('params'); + if (!rails.allowAction(link)) return rails.stopEverything(e); + + if (link.is(rails.linkDisableSelector)) rails.disableElement(link); + + if (link.data('remote') !== undefined) { + if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; } + + var handleRemote = rails.handleRemote(link); + // response from rails.handleRemote() will either be false or a deferred object promise. + if (handleRemote === false) { + rails.enableElement(link); + } else { + handleRemote.error( function() { rails.enableElement(link); } ); + } + return false; + + } else if (link.data('method')) { + rails.handleMethod(link); + return false; + } + }); + + $(document).delegate(rails.buttonClickSelector, 'click.rails', function(e) { + var button = $(this); + if (!rails.allowAction(button)) return rails.stopEverything(e); + + rails.handleRemote(button); + return false; + }); + + $(document).delegate(rails.inputChangeSelector, 'change.rails', function(e) { + var link = $(this); + if (!rails.allowAction(link)) return rails.stopEverything(e); + + rails.handleRemote(link); + return false; + }); + + $(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) { + var form = $(this), + remote = form.data('remote') !== undefined, + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); + + if (!rails.allowAction(form)) return rails.stopEverything(e); + + // skip other logic when required values are missing or file upload is present + if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { + return rails.stopEverything(e); + } + + if (remote) { + if (nonBlankFileInputs) { + // slight timeout so that the submit button gets properly serialized + // (make it easy for event handler to serialize form without disabled values) + setTimeout(function(){ rails.disableFormElements(form); }, 13); + var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); + + // re-enable form elements if event bindings return false (canceling normal form submission) + if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); } + + return aborted; + } + + rails.handleRemote(form); + return false; + + } else { + // slight timeout so that the submit button gets properly serialized + setTimeout(function(){ rails.disableFormElements(form); }, 13); + } + }); + + $(document).delegate(rails.formInputClickSelector, 'click.rails', function(event) { + var button = $(this); + + if (!rails.allowAction(button)) return rails.stopEverything(event); + + // register the pressed submit button + var name = button.attr('name'), + data = name ? {name:name, value:button.val()} : null; + + button.closest('form').data('ujs:submit-button', data); + }); + + $(document).delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { + if (this == event.target) rails.disableFormElements($(this)); + }); + + $(document).delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { + if (this == event.target) rails.enableFormElements($(this)); + }); + + $(function(){ + // making sure that all forms have actual up-to-date token(cached forms contain old one) + var csrf_token = $('meta[name=csrf-token]').attr('content'); + var csrf_param = $('meta[name=csrf-param]').attr('content'); + $('form input[name="' + csrf_param + '"]').val(csrf_token); + }); + } + +})( jQuery ); diff --git a/app/assets/javascripts/new-request.js b/app/assets/javascripts/new-request.js new file mode 100644 index 000000000..f8f2a0665 --- /dev/null +++ b/app/assets/javascripts/new-request.js @@ -0,0 +1,14 @@ +$(document).ready(function() { + $('.batch_public_body_list').hide(); + var showtext = $('.batch_public_body_toggle').attr('data-showtext'); + var hidetext = $('.batch_public_body_toggle').attr('data-hidetext'); + $('.toggle-message').text(showtext); + $('.batch_public_body_toggle').click(function(){ + $('.batch_public_body_list').toggle(); + if ($('.toggle-message').text() == showtext){ + $('.toggle-message').text(hidetext); + }else{ + $('.toggle-message').text(showtext); + } + }) +}) diff --git a/app/assets/javascripts/profile-photos.js b/app/assets/javascripts/profile-photos.js new file mode 100644 index 000000000..c7ebe894d --- /dev/null +++ b/app/assets/javascripts/profile-photos.js @@ -0,0 +1,3 @@ +// ... +//= require jquery.Jcrop +//= require profile_photo diff --git a/app/assets/javascripts/profile_photo.js b/app/assets/javascripts/profile_photo.js new file mode 100644 index 000000000..6d637b439 --- /dev/null +++ b/app/assets/javascripts/profile_photo.js @@ -0,0 +1,49 @@ +// Remember to invoke within jQuery(window).load(...) +// If you don't, Jcrop may not initialize properly +jQuery(window).load(function(){ + // The size of the initial selection (largest, centreted rectangle) + var w = jQuery('#profile_photo_cropbox').width(); + var h = jQuery('#profile_photo_cropbox').height(); + var t = 0; + var l = 0; + var initial; + if (h < w) { + initial = h; + l = (w - initial) / 2; + } else { + initial = w; + t = (h - initial) / 2; + } + + jQuery('#profile_photo_cropbox').Jcrop({ + onChange: showPreview, + onSelect: showPreview, + aspectRatio: 1, + setSelect: [ l, t, initial, initial ] + }); + +}); + +// Our simple event handler, called from onChange and onSelect +// event handlers, as per the Jcrop invocation above +function showPreview(coords) +{ + if (parseInt(coords.w) > 0) + { + var rx = 100 / coords.w; + var ry = 100 / coords.h; + + jQuery('#profile_photo_preview').css({ + width: Math.round(rx * jQuery('#profile_photo_cropbox').width()) + 'px', + height: Math.round(ry * jQuery('#profile_photo_cropbox').height()) + 'px', + marginLeft: '-' + Math.round(rx * coords.x) + 'px', + marginTop: '-' + Math.round(ry * coords.y) + 'px' + }); + + $('#x').val(coords.x); + $('#y').val(coords.y); + $('#w').val(coords.w); + $('#h').val(coords.h); + } +} + diff --git a/app/assets/javascripts/select-authorities.js b/app/assets/javascripts/select-authorities.js new file mode 100644 index 000000000..843f5c0ad --- /dev/null +++ b/app/assets/javascripts/select-authorities.js @@ -0,0 +1,67 @@ +$(document).ready(function() { + + function add_option(selector, value, text) { + var optionExists = ($(selector + ' option[value=' + value + ']').length > 0); + if(!optionExists){ + $(selector).append("<option value=" + value + ">" + text + "</option>"); + } + } + // Transfer a set of select options defined by 'from_selector' to another select, + // defined by 'to_selector' + function transfer_options(from_selector, to_selector){ + $(from_selector).each(function() + { + add_option(to_selector, $(this).val(), $(this).text()); + $(this).remove(); + }) + $('#public_body_query').val(''); + return false; + } + + // Submit the search form once the text reaches a certain length + $("#public_body_query").keypress($.debounce( 300, function() { + if ($('#public_body_query').val().length >= 3) { + $('#body_search_form').submit(); + } + })); + + // Populate the candidate list with json search results + $('#body_search_form').on('ajax:success', function(event, data, status, xhr) { + $('#select_body_candidates').empty(); + $.each(data, function(key, value) + { + add_option('#select_body_candidates', value['id'], value['name']); + }); + }); + + // Add a hidden element to the submit form for every option in the selected list + $('#body_submit_button').click(function(){ + $('#select_body_selections option').each(function() + { + $('#body_submit_form').append('<input type="hidden" value="' + $(this).val() + '" name="public_body_ids[]">' ); + }) + }) + + // Transfer selected candidates to selected list + $('#body_select_button').click(function(){ + return transfer_options('#select_body_candidates option:selected', '#select_body_selections'); + }) + + // Transfer selected selected options back to candidate list + $('#body_deselect_button').click(function(){ + return transfer_options('#select_body_selections option:selected', '#select_body_candidates'); + }) + + // Transfer all candidates to selected list + $('#body_select_all_button').click(function(){ + return transfer_options('#select_body_candidates option', '#select_body_selections'); + }) + + // Transfer all selected back to candidate list + $('#body_deselect_all_button').click(function(){ + return transfer_options('#select_body_selections option', '#select_body_candidates'); + }) + + // Show the buttons for selecting and deselecting all + $('.select_all_button').show(); +}) diff --git a/app/assets/javascripts/stats-graphs.js b/app/assets/javascripts/stats-graphs.js new file mode 100644 index 000000000..ff3d3a11a --- /dev/null +++ b/app/assets/javascripts/stats-graphs.js @@ -0,0 +1,135 @@ +/* From http://stackoverflow.com/a/10284006/223092 */ +function zip(arrays) { + return arrays[0].map(function(_,i){ + return arrays.map(function(array){return array[i]}) + }); +} + +$(document).ready(function() { + $.each(graphs_data, function(index, graph_data) { + var graph_id = graph_data.id, + dataset, + plot, + graph_data, + graph_div = $('#' + graph_id), + previousPoint = null; + + if (!graph_data.x_values) { + /* Then there's no data for this graph */ + return true; + } + + graph_div.css('width', '700px'); + graph_div.css('height', '600px'); + + dataset = [ + {'color': 'orange', + 'bars': { + 'show': true, + 'barWidth': 0.5, + 'align': 'center' + }, + 'data': zip([graph_data.x_values, + graph_data.y_values]) + } + ] + + if (graph_data.errorbars) { + dataset.push({ + 'color': 'orange', + 'points': { + // Don't show these, just draw error bars: + 'radius': 0, + 'errorbars': 'y', + 'yerr': { + 'asymmetric': true, + 'show': true, + 'upperCap': "-", + 'lowerCap': "-", + 'radius': 5 + } + }, + 'data': zip([graph_data.x_values, + graph_data.y_values, + graph_data.cis_below, + graph_data.cis_above]) + }); + } + + options = { + 'grid': { 'hoverable': true, 'clickable': true }, + 'xaxis': { + 'ticks': graph_data.x_ticks, + 'rotateTicks': 90 + }, + 'yaxis': { + 'min': 0, + 'max': graph_data.y_max + }, + 'xaxes': [{ + 'axisLabel': graph_data.x_axis, + 'axisLabelPadding': 20, + 'axisLabelColour': 'black' + }], + 'yaxes': [{ + 'axisLabel': graph_data.y_axis, + 'axisLabelPadding': 20, + 'axisLabelColour': 'black' + }], + 'series': { + 'lines': { + 'show': false + } + }, + } + + plot = $.plot(graph_div, + dataset, + options); + + graph_div.bind("plotclick", function(event, pos, item) { + var i, pb, url, name; + if (item) { + i = item.dataIndex; + pb = graph_data.public_bodies[i]; + url = pb.url; + name = pb.name; + window.location.href = url; + } + }); + + /* This code is adapted from: + http://www.flotcharts.org/flot/examples/interacting/ */ + + function showTooltip(x, y, contents) { + $('<div id="flot-tooltip">' + contents + '</div>').css({ + 'position': 'absolute', + 'display': 'none', + 'top': y + 10, + 'left': x + 10, + 'border': '1px solid #fdd', + 'padding': '2px', + 'background-color': '#fee', + 'opacity': 0.80 + }).appendTo("body").fadeIn(200); + } + + graph_div.bind("plothover", function (event, pos, item) { + var escapedName, x, y; + if (item) { + if (previousPoint != item.dataIndex) { + previousPoint = item.dataIndex; + $("#flot-tooltip").remove(); + escapedName = $('<div />').text( + graph_data.tooltips[item.dataIndex]).html(); + showTooltip(item.pageX, + item.pageY, + escapedName); + } + } else { + $("#flot-tooltip").remove(); + previousPoint = null; + } + }); + }); +}); diff --git a/app/assets/javascripts/stats.js b/app/assets/javascripts/stats.js new file mode 100644 index 000000000..8c743ad87 --- /dev/null +++ b/app/assets/javascripts/stats.js @@ -0,0 +1,6 @@ +// ... +//= require jquery.flot.min +//= require jquery.flot.errorbars.min +//= require jquery.flot.tickrotor.min +//= require jquery.flot.axislabels +//= require stats-graphs diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss new file mode 100644 index 000000000..b0de2eb7b --- /dev/null +++ b/app/assets/stylesheets/admin.scss @@ -0,0 +1,108 @@ +/* As we're namespacing bootstrap to class admin, which is applied to the body + element in the admin interface (no id or class allowed on the HTML element + in HTML 4.01) and to the navbar also, so it can be styled with bootstrap + when showing for admin users on the front end, re-apply the bootstrap html + and body styles here. +*/ +html { + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body.admin { + margin: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 20px; + color: #333333; + background-color: white; +} + +/* When the admin stylesheet is loaded (and so the user is seeing the admin navbar), pad the banner of the front end interface so that it isn't hidden, and move any special notice down too. +*/ + +.entirebody { + padding-top: 42px; +} + +.admin { + + @import "compass/css3"; + @import "bootstrap"; + + #main { + padding-top: 50px; + + } + + .form-inline { + display: inline; + } + + table .form { + display: inline-block; + margin: 0; + } + + .accordion-group { + border: none; + } + .accordion-heading { + .btn { + float: left; + margin: 6px 15px; + } + .accordion-toggle { + padding: 2px; + &:hover { + text-decoration: none; + } + } + } + + div.item-detail { + div:nth-child(odd) span { + background-color: #eee; + } + } + + span.label.tag { + margin-right: 2px; + a { + color: white; + } + } + + body.admin blockquote p { + font-size: 13px; + display: inline; + } + + div#user_locale_switcher { + div.btn-group:before, + div.btn-group:after { + display: inline; + } + } + + #request_hidden_user_subject_field { + width: 440px; + } + #request_hidden_user_explanation_field { + width: 100%; + height: 15em; + } + #request_hidden_user_subject, + #request_hide_button, + #request_hidden_user_explanation { + display: none; + } + + #outgoing_message_body, + #comment_body { + width: 750px; + } + +} + diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 000000000..097221b2f --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,7 @@ +/* ... +*= require_self +*= require main +*= require custom +*= require jquery.ui.datepicker +*= require jquery.ui.tabs +*/ diff --git a/app/assets/stylesheets/custom.css b/app/assets/stylesheets/custom.css new file mode 100644 index 000000000..f1df5cca5 --- /dev/null +++ b/app/assets/stylesheets/custom.css @@ -0,0 +1,5 @@ +/* Themes will typically add a custom.css file with their own CSS. + This file is present to stop errors where THEME_URLS is empty, + since it is mentioned in the application.css manifest. Themes + should prepend their directories to the asset path so this will + be overriden by any custom.css in the theme. */ diff --git a/app/assets/stylesheets/fonts.scss b/app/assets/stylesheets/fonts.scss new file mode 100644 index 000000000..2bfb8d222 --- /dev/null +++ b/app/assets/stylesheets/fonts.scss @@ -0,0 +1,73 @@ + +@font-face { + font-family: 'DeliciousBold'; + src: font-url('delicious-bold-webfont.eot'); + src: font-url('delicious-bold-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-bold-webfont.woff') format('woff'), + font-url('delicious-bold-webfont.ttf') format('truetype'), + font-url('delicious-bold-webfont.svg#DeliciousBold') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'DeliciousBoldItalic'; + src: font-url('delicious-bolditalic-webfont.eot'); + src: font-url('delicious-bolditalic-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-bolditalic-webfont.woff') format('woff'), + font-url('delicious-bolditalic-webfont.ttf') format('truetype'), + font-url('delicious-bolditalic-webfont.svg#DeliciousBoldItalic') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'DeliciousHeavyRegular'; + src: font-url('delicious-heavy-webfont.eot'); + src: font-url('delicious-heavy-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-heavy-webfont.woff') format('woff'), + font-url('delicious-heavy-webfont.ttf') format('truetype'), + font-url('delicious-heavy-webfont.svg#DeliciousHeavyRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'DeliciousItalic'; + src: font-url('delicious-italic-webfont.eot'); + src: font-url('delicious-italic-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-italic-webfont.woff') format('woff'), + font-url('delicious-italic-webfont.ttf') format('truetype'), + font-url('delicious-italic-webfont.svg#DeliciousItalic') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'DeliciousRoman'; + src: font-url('delicious-roman-webfont.eot'); + src: font-url('delicious-roman-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-roman-webfont.woff') format('woff'), + font-url('delicious-roman-webfont.ttf') format('truetype'), + font-url('delicious-roman-webfont.svg#DeliciousRoman') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'DeliciousSmallCapsRegular'; + src: font-url('delicious-smallcaps-webfont.eot'); + src: font-url('delicious-smallcaps-webfont.eot?#iefix') format('embedded-opentype'), + font-url('delicious-smallcaps-webfont.woff') format('woff'), + font-url('delicious-smallcaps-webfont.ttf') format('truetype'), + font-url('delicious-smallcaps-webfont.svg#DeliciousSmallCapsRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} + diff --git a/app/assets/stylesheets/ie6.css b/app/assets/stylesheets/ie6.css new file mode 100644 index 000000000..5eff5fac9 --- /dev/null +++ b/app/assets/stylesheets/ie6.css @@ -0,0 +1,3 @@ +#everypage { + position: absolute; +} diff --git a/app/assets/stylesheets/ie7.css b/app/assets/stylesheets/ie7.css new file mode 100644 index 000000000..c6e8fe4f2 --- /dev/null +++ b/app/assets/stylesheets/ie7.css @@ -0,0 +1,18 @@ +form input[type=submit], a.link_button_green { + border: solid 0px #FFF; + line-height: 16px; + padding-top: 3px +} + +input#navigation_search_query { + margin-bottom: -5px; +} + +.request_left span.head { + line-height: 35px; +} + +#header_left, +#left_column { + margin-top: 20px; +}
\ No newline at end of file diff --git a/app/assets/stylesheets/jquery-ui-1.8.15.custom.css b/app/assets/stylesheets/jquery-ui-1.8.15.custom.css new file mode 100755 index 000000000..386d854ff --- /dev/null +++ b/app/assets/stylesheets/jquery-ui-1.8.15.custom.css @@ -0,0 +1,375 @@ +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana,Arial,sans-serif&fwDefault=normal&fsDefault=&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=01_flat.png&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=01_flat.png&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=01_flat.png&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=01_flat.png&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=01_flat.png&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=01_flat.png&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=01_flat.png&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=01_flat.png&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=01_flat.png&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Verdana,Arial,sans-serif; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Verdana,Arial,sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x; color: #222222; } +.ui-widget-content a { color: #222222; } +.ui-widget-header { border: 1px solid #aaaaaa; background: #cccccc url(images/ui-bg_flat_75_cccccc_40x100.png) 50% 50% repeat-x; color: #222222; font-weight: bold; } +.ui-widget-header a { color: #222222; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #d3d3d3; background: #e6e6e6 url(images/ui-bg_flat_75_e6e6e6_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #555555; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #555555; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #999999; background: #dadada url(images/ui-bg_flat_75_dadada_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-hover a, .ui-state-hover a:hover { color: #212121; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #aaaaaa; background: #ffffff url(images/ui-bg_flat_65_ffffff_40x100.png) 50% 50% repeat-x; font-weight: normal; color: #212121; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #212121; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fcefa1; background: #fbf9ee url(images/ui-bg_flat_55_fbf9ee_40x100.png) 50% 50% repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #fef1ec url(images/ui-bg_flat_95_fef1ec_40x100.png) 50% 50% repeat-x; color: #cd0a0a; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #cd0a0a; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #cd0a0a; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_888888_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_454545_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_2e83ff_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_cd0a0a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); } +.ui-widget-shadow { margin: -8px 0 0 -8px; padding: 8px; background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x; opacity: .30;filter:Alpha(Opacity=30); -moz-border-radius: 8px; -khtml-border-radius: 8px; -webkit-border-radius: 8px; border-radius: 8px; }/* + * jQuery UI Tabs 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Tabs#theming + */ +.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ +.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; } +.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; } +.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; } +.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; } +.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */ +.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; } +.ui-tabs .ui-tabs-hide { display: none !important; } +/* + * jQuery UI Datepicker 1.8.16 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +}
\ No newline at end of file diff --git a/app/assets/stylesheets/jquery.Jcrop.css b/app/assets/stylesheets/jquery.Jcrop.css new file mode 100644 index 000000000..24925dc94 --- /dev/null +++ b/app/assets/stylesheets/jquery.Jcrop.css @@ -0,0 +1,35 @@ +/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */ +.jcrop-holder { text-align: left; } + +.jcrop-vline, .jcrop-hline +{ + font-size: 0; + position: absolute; + background: white url('Jcrop.gif') top left repeat; +} +.jcrop-vline { height: 100%; width: 1px !important; } +.jcrop-hline { width: 100%; height: 1px !important; } +.jcrop-handle { + font-size: 1px; + width: 7px !important; + height: 7px !important; + border: 1px #eee solid; + background-color: #333; + *width: 9px; + *height: 9px; +} + +.jcrop-tracker { width: 100%; height: 100%; } + +.custom .jcrop-vline, +.custom .jcrop-hline +{ + background: yellow; +} +.custom .jcrop-handle +{ + border-color: black; + background-color: #C7BB00; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} diff --git a/app/assets/stylesheets/jquery.fancybox-1.3.4.css b/app/assets/stylesheets/jquery.fancybox-1.3.4.css new file mode 100755 index 000000000..6f53d8f4a --- /dev/null +++ b/app/assets/stylesheets/jquery.fancybox-1.3.4.css @@ -0,0 +1,359 @@ +/*
+ * FancyBox - jQuery Plugin
+ * Simple and fancy lightbox alternative
+ *
+ * Examples and documentation at: http://fancybox.net
+ *
+ * Copyright (c) 2008 - 2010 Janis Skarnelis
+ * That said, it is hardly a one-person project. Many people have submitted bugs, code, and offered their advice freely. Their support is greatly appreciated.
+ *
+ * Version: 1.3.4 (11/11/2010)
+ * Requires: jQuery v1.3+
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */
+
+#fancybox-loading {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ width: 40px;
+ height: 40px;
+ margin-top: -20px;
+ margin-left: -20px;
+ cursor: pointer;
+ overflow: hidden;
+ z-index: 1104;
+ display: none;
+}
+
+#fancybox-loading div {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 40px;
+ height: 480px;
+ background-image: url('fancybox.png');
+}
+
+#fancybox-overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ z-index: 1100;
+ display: none;
+}
+
+#fancybox-tmp {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ overflow: auto;
+ display: none;
+}
+
+#fancybox-wrap {
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: 20px;
+ z-index: 1101;
+ outline: none;
+ display: none;
+}
+
+#fancybox-outer {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ background: #fff;
+}
+
+#fancybox-content {
+ width: 0;
+ height: 0;
+ padding: 0;
+ outline: none;
+ position: relative;
+ overflow: hidden;
+ z-index: 1102;
+ border: 0px solid #fff;
+}
+
+#fancybox-hide-sel-frame {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: transparent;
+ z-index: 1101;
+}
+
+#fancybox-close {
+ position: absolute;
+ top: -15px;
+ right: -15px;
+ width: 30px;
+ height: 30px;
+ background: transparent url('fancybox.png') -40px 0px;
+ cursor: pointer;
+ z-index: 1103;
+ display: none;
+}
+
+#fancybox-error {
+ color: #444;
+ font: normal 12px/20px Arial;
+ padding: 14px;
+ margin: 0;
+}
+
+#fancybox-img {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none;
+ line-height: 0;
+ vertical-align: top;
+}
+
+#fancybox-frame {
+ width: 100%;
+ height: 100%;
+ border: none;
+ display: block;
+}
+
+#fancybox-left, #fancybox-right {
+ position: absolute;
+ bottom: 0px;
+ height: 100%;
+ width: 35%;
+ cursor: pointer;
+ outline: none;
+ background: transparent url('blank.gif');
+ z-index: 1102;
+ display: none;
+}
+
+#fancybox-left {
+ left: 0px;
+}
+
+#fancybox-right {
+ right: 0px;
+}
+
+#fancybox-left-ico, #fancybox-right-ico {
+ position: absolute;
+ top: 50%;
+ left: -9999px;
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ cursor: pointer;
+ z-index: 1102;
+ display: block;
+}
+
+#fancybox-left-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -30px;
+}
+
+#fancybox-right-ico {
+ background-image: url('fancybox.png');
+ background-position: -40px -60px;
+}
+
+#fancybox-left:hover, #fancybox-right:hover {
+ visibility: visible; /* IE6 */
+}
+
+#fancybox-left:hover span {
+ left: 20px;
+}
+
+#fancybox-right:hover span {
+ left: auto;
+ right: 20px;
+}
+
+.fancybox-bg {
+ position: absolute;
+ padding: 0;
+ margin: 0;
+ border: 0;
+ width: 20px;
+ height: 20px;
+ z-index: 1001;
+}
+
+#fancybox-bg-n {
+ top: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+}
+
+#fancybox-bg-ne {
+ top: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -162px;
+}
+
+#fancybox-bg-e {
+ top: 0;
+ right: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+ background-position: -20px 0px;
+}
+
+#fancybox-bg-se {
+ bottom: -20px;
+ right: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -182px;
+}
+
+#fancybox-bg-s {
+ bottom: -20px;
+ left: 0;
+ width: 100%;
+ background-image: url('fancybox-x.png');
+ background-position: 0px -20px;
+}
+
+#fancybox-bg-sw {
+ bottom: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -142px;
+}
+
+#fancybox-bg-w {
+ top: 0;
+ left: -20px;
+ height: 100%;
+ background-image: url('fancybox-y.png');
+}
+
+#fancybox-bg-nw {
+ top: -20px;
+ left: -20px;
+ background-image: url('fancybox.png');
+ background-position: -40px -122px;
+}
+
+#fancybox-title {
+ font-family: Helvetica;
+ font-size: 12px;
+ z-index: 1102;
+}
+
+.fancybox-title-inside {
+ padding-bottom: 10px;
+ text-align: center;
+ color: #333;
+ background: #fff;
+ position: relative;
+}
+
+.fancybox-title-outside {
+ padding-top: 10px;
+ color: #fff;
+}
+
+.fancybox-title-over {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ color: #FFF;
+ text-align: left;
+}
+
+#fancybox-title-over {
+ padding: 10px;
+ background-image: url('fancy_title_over.png');
+ display: block;
+}
+
+.fancybox-title-float {
+ position: absolute;
+ left: 0;
+ bottom: -20px;
+ height: 32px;
+}
+
+#fancybox-title-float-wrap {
+ border: none;
+ border-collapse: collapse;
+ width: auto;
+}
+
+#fancybox-title-float-wrap td {
+ border: none;
+ white-space: nowrap;
+}
+
+#fancybox-title-float-left {
+ padding: 0 0 0 15px;
+ background: url('fancybox.png') -40px -90px no-repeat;
+}
+
+#fancybox-title-float-main {
+ color: #FFF;
+ line-height: 29px;
+ font-weight: bold;
+ padding: 0 0 3px 0;
+ background: url('fancybox-x.png') 0px -40px;
+}
+
+#fancybox-title-float-right {
+ padding: 0 0 0 15px;
+ background: url('fancybox.png') -55px -90px no-repeat;
+}
+
+/* IE6 */
+
+.fancybox-ie6 #fancybox-close { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_close.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-left-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-right-ico { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_nav_right.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-title-over { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_over.png', sizingMethod='scale'); zoom: 1; }
+.fancybox-ie6 #fancybox-title-float-left { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_left.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-main { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_main.png', sizingMethod='scale'); }
+.fancybox-ie6 #fancybox-title-float-right { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_title_right.png', sizingMethod='scale'); }
+
+.fancybox-ie6 #fancybox-bg-w, .fancybox-ie6 #fancybox-bg-e, .fancybox-ie6 #fancybox-left, .fancybox-ie6 #fancybox-right, #fancybox-hide-sel-frame {
+ height: expression(this.parentNode.clientHeight + "px");
+}
+
+#fancybox-loading.fancybox-ie6 {
+ position: absolute; margin-top: 0;
+ top: expression( (-20 + (document.documentElement.clientHeight ? document.documentElement.clientHeight/2 : document.body.clientHeight/2 ) + ( ignoreMe = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop )) + 'px');
+}
+
+#fancybox-loading.fancybox-ie6 div { background: transparent; filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_loading.png', sizingMethod='scale'); }
+
+/* IE6, IE7, IE8 */
+
+.fancybox-ie .fancybox-bg { background: transparent !important; }
+
+.fancybox-ie #fancybox-bg-n { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_n.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-ne { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_ne.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-e { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_e.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-se { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_se.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-s { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_s.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-sw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_sw.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-w { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_w.png', sizingMethod='scale'); }
+.fancybox-ie #fancybox-bg-nw { filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='fancybox/fancy_shadow_nw.png', sizingMethod='scale'); }
\ No newline at end of file diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss new file mode 100644 index 000000000..3118eab3d --- /dev/null +++ b/app/assets/stylesheets/main.scss @@ -0,0 +1,1824 @@ +body { +text-align:center; +color:#444; +font-size:15px; +font-family: sans-serif; +margin:0; +padding:0; +} + +#banner { +top:0; +background-color:#F3F3F3; +left:0; +width:100%; +border:none; +height:160px; +border:none; +margin:0; +} + +#navigation_search { +position:absolute; +left:0; +z-index:150; +text-align:right; +-moz-opacity:0.7; +filter:alpha(opacity= 70) !important; +opacity:0.7; +width:auto; +right:0; +top:10px; +} + +#navigation_search input { +background-color:#fff; +color:#000; +border-color:#010101; +border-style:solid; +border-width:1px; +} + +#navigation_search input#navigation_search_button { +background: image-url('search-button.png') no-repeat center center; +width: 27px; +height: 26px; +color: transparent; +font-size: 0; +margin: 0; +padding: 0; +position: relative; +top: -4px; +left: -5px; +} + +#navigation_search input#navigation_search_query { +width:20.25em; +font-size: 0.8em; +padding: 5px; +margin: 5px -1px 0 0; + +} + +#navigation_search p { +margin:0 0.6em 0 0; +} + +#topnav { +position:relative; +left:0; +height:auto; +overflow:auto; +z-index:100; +background-color:#000; +background:transparent; +top:120px; +margin-left:115px; +width:auto; +font-family:Arial, sans-serif; +font-size:1.2em; +padding:0; +} + +#topnav ul { +list-style:none; +margin:0; +padding:0; +} + +#topnav li a,#topnav li a:visited { +display:block; +color:#444; +text-decoration:none; +margin:0; +padding:0; +} + +#topnav li a:hover, a:hover { +color:purple; +} + +#logged_in_bar { +clear:none; +font-size:0.9em; +z-index:200; +color:#444; +top:14px; +right:22em; +float:none; +position:absolute; +padding:0.2em 10px 0.25em 1em; +} + +#logged_in_bar a,#logged_in_bar a:visited { +color:#444; +} + +#wrapper { +position:relative; +clear:both; +top:0; +text-align:left; +overflow:visible; +width:900px; +margin:0 auto 1.2em; +} + +#wrapper_google_embed { +position:relative; +clear:both; +width:100%; +height:90%; +text-align:left; +overflow:visible; +margin:0; +} + +#content { +position:relative; +width:875px; +padding:1em; +} + +h1,h2,h3 { +font-family:sans-serif; +font-weight:700; +line-height:1em; +letter-spacing:0; +color:#222; +clear:left; +} + +h1 { +font-size:2.2em; +margin-bottom:15px; +margin-top:10px; +} + +h2 { +font-size:1.4em; +} + +.highlight { +background:#FF0; +border-color:#A3A3A3; +border-style:dotted; +border-width:0; +} + +dl { +margin-top:24px; +line-height:160%; +} + +dt { +font-weight:700; +} + +dd { +width:auto; +margin:18px 0 36px; +} + +#stepwise_make_request { +color:#222; +font-size:1.1em; +text-align:left; +width:412px; +margin:0 14em 40px 0; +} + +#stepwise_make_request_view_email { +text-align:center; +background-color:#d0d0d0; +margin:0; +padding:1em 0; +} + +#frontpage_examples div#examples_0 { +float:left; +margin-left:0; +width:49%; +} + +#frontpage_examples div#examples_1 { +float:right; +margin-right:0; +width:49%; +} + +#frontpage_examples ul { +text-align:center; +list-style:none; +margin:0; +padding:1em 0; +} + +div#twitter { +margin-top: 30px; +} + +img.twitter-icon { +vertical-align:middle; +} + +.blog_post { +margin-bottom:2em; +} + +.request_listing,.body_listing,.user_listing { +font-size:0.9em; +margin-top:1.5em; +border-bottom:#9C9C9C; +overflow:hidden; +border-style:none none solid; +border-width:0 0 1px; +padding:0 0 1.5em; +} + +.body_listing { +_width:47em; +padding-bottom:16px; +} + +span.head { +display:block; +font-size:1.4em; +font-weight:700; +padding:12px 0 0; +} + +.request_listing span.head /* full page request list only */ { +min-height:32px; +background-image:image-url('navimg/request-icon.png'); +background-repeat:no-repeat; +background-position:4px 0; +margin:0 0 0.3em; +padding:8px 0 0 42px; +} + +.body_listing span.head /* full page request list only */ { +min-height:32px; +background-image:image-url('navimg/auth-icon.png'); +background-repeat:no-repeat; +background-position:4px 0; +margin:0 0 0.3em; +padding:8px 0 0 42px; +} + +.user_listing span.head /* full page request list only */ { +min-height:32px; +background-image:image-url('navimg/user-icon.png'); +background-repeat:no-repeat; +background-position:4px 0; +margin:0 0 0.3em; +padding:8px 0 0 42px; +} + +.user_listing span.no_icon /* full page request list only */ { +background-image:none; +} + +span.bottomline { +clear:left; +display:block; +padding:10px 0 0 42px; +} + +.request_listing span.bottomline +/* full page request list only */ { +width:35em; +background-repeat:no-repeat; +background-position:left center; +min-height:42px; +margin:0 0 0.6em; +} + +.request_icon_line { +background-repeat:no-repeat; +background-position:left center; +min-height:24px; +clear:left; +padding:8px 0 10px 42px; +} + +.icon_waiting_response,.icon_waiting_classification,.icon_waiting_clarification { +background-image:image-url('status-pending.png'); +color:#A68C2E; +} + +.icon_rejected { +background-image:image-url('navimg/status-icons-fail.png'); +} + +.icon_not_held, .icon_attention_requested { +background-image:image-url('status-not-held.png'); +color:#A68C2E; +} + +.icon_successful,.icon_partially_successful { +background-image:image-url('status-complete.png'); +color:#69952F; +} + +.icon_requires_admin,.icon_waiting_response_overdue,.icon_waiting_response_very_overdue { +background-image:image-url('status-overdue.png'); +color:#C1272D; +} + +.icon_gone_postal { +background-image:image-url('status-gone-postal.png'); +color:#A68C2E; +} + +.icon_error_message { +background-image:image-url('status-error.png'); +color:#C1272D; +} + +.icon_internal_review { +background-image:image-url('status-internal-review.png'); +color:#A68C2E; +} + +.icon_user_withdrawn { +background-image:image-url('status-withdrawn.png'); +color:#A68C2E; +} + +span.desc { +display:block; +float:right; +clear:none; +font-style:italic; +color:#3F3F3F; +overflow:hidden; +} + +.request_listing span.desc +/* full page request list only */ { +width:25em; +background-image:image-url('navimg/quote-open.png'); +background-repeat:no-repeat; +background-position:0 0; +padding:8px 0 0 25px; +} + +.body_listing span.desc { +background-image:none; +float:left; +clear:both; +padding:0 0 0 42px; +} + +.request_short_listing p { +font-size:0.8em; +margin-top:-0.8em; +margin-bottom:0; +} + +div.pagination { +text-align:center; +padding-top:0.3em; +} + +div.pagination span.current { +background-color:#000; +color:#FFF; +padding:0 0.6em 0.1em; +} + +span.disabled { +color:#B2B2B2; +padding:0 0.6em 0.1em; +} + +div.pagination a,div.pagination a:visited { +text-decoration:none; +padding:0 0.6em 0.1em; +} + +div.pagination a:hover { +background-color:#626262; +color:#FFF; +} + +#error,.errorExplanation,#hidden_request { +color:#FF0606; +font-weight:700; +background-color:#fee; +border-color:#FF0C11; +border-style:solid; +border-width:1px; +} + +#error,#hidden_request { +padding:0.5em; +} + +.fieldWithErrors { +display:block; +background-color:#fee; +background:none; +border:solid 0 #FFF; +border-color:#FF0C11; +border-style:solid; +border-width:1px; +padding:0.2em; +} + +#notice, .notice { +color:#16C132; +font-size:1.4em; +font-weight:700; +background-color:#D5FFD8; +border-color:#1EFF38; +border-style:solid; +border-width:1px; +padding:0.5em; +} + +.describe_state_form,.undescribed_requests,.gone_postal_help { +font-weight:400; +margin-bottom:1em; +font-size:1em; +color:#454545; +float:left; +width:39em; +} + +.requires_admin_details { +margin-left:1.8em; +width:37em; +} + +#show_response_view { +width:35em; +} + +div.correspondence { +width:40em; +float:left; +overflow:auto; +border-color:#5F5F5F; +border-style:solid; +border-width:1px; +margin:0 0 1em; +padding:0 0.5em; +} + +div.correspondence h2 { +text-align:right; +font-size:1em; +} + +.event_actions { +text-align:right; +} + +div#after_actions { +float:left; +margin-bottom:1em; +width:39em; +} + +div#anyone_actions { +margin-bottom:1em; +} + +div[id|="outgoing"] p { +} + +div[id|="incoming"] { +background-color:#F3F3F3; +} + +div[id|="incoming"] p { +} + +div[id|="comment"] { +width:35em; +margin-left:50px; +} + +div[id|="comment"] h2 { +font-size:1em; +} + +.comment_quote { +float:left; +margin-right:0.6em; +} + +.preview_subject { +margin:1em 1.2em 0 0.8em; +} + +.attachments { +border-color:#010101; +border-width:1px; +margin:0 0 1em; +} + +a img.attachment_image { +float:left; +border:0; +vertical-align:middle; +margin:0 0.2em 0.2em 0; +} + +.attachments hr.top { +clear:both; +margin:0 0 1em; +} + +.attachments hr.bottom { +clear:both; +margin:1em 0 0; +} + +#followup { +clear:both; +float:left; +} + +#view-html-content { +margin-left:1em; +margin-right:1em; +} + +#view-html-content img { +max-width:50em; +} + +.view_html_prefix { +text-align:left; +background-color:#E7E7E7; +border-bottom:1px solid #5f5f5f; +height:3em; +padding:0.5em 1em; +} + +.view_html_logo { +float:left; +margin-right:1em; +} + +.view_html_logo img { +border:0; +} + +.view_html_download_link { +float:right; +margin-left:1em; +} + +#authority_selection { +float:left; +width:40%; +} + +#authority_search_ahead_results { +width:26em; +} + +#authority_preview { +width:45%; +float:right; +background-color:#FFFFE0; +padding-left:1em; +padding-right:1em; +overflow:hidden; +margin-top:-67px; +} + +#authority_preview #header_left,#authority_preview.request_left,#authority_preview #stepwise_make_request { +width:95%; +} + +#request_advice { +float:right; +width:250px; +margin-top:1em; +} + +#request_advice ul { +margin:0 auto; +} + +#request_advice ul li { +margin:0 0 1em; +} + +#request_header { +background-color:#FFFFE0; +padding-top:0.5em; +padding-bottom:1em; +} + +#request_form label,label.form_label { +display:block; +float:left; +clear:none; +width:100px; +text-align:left; +margin:2px 0 0; +padding:0 10px 0 0; +} + +.form_item_note,.form_note { +width:34em; +margin-left:110px; +font-size:1em; +} + +.form_item_note { +margin-top:-1em; +} + +.form_button { +margin:0 0 0 9em; +} + +p#sign_in_reason, p#superuser_message { +text-align:center; +font-size:1.4em; +font-weight:700; +line-height:1em; +} +p#superuser_message { + font-size:1.2em; +} +#signup,#signin { +clear:none; +margin-bottom:1em; +float:none; +margin-top:20px; +width:auto; +} + +#signup .errorExplanation, #signin .errorExplanation { + width: inherit; +} + +#signup h2,#signin h2 { +font-size:1.1em; +} + +#signup { +float:right; +} + +#sign_alone #signin { +margin-left:25%; +} + +#signup .form_item_note,#signin .form_note { +font-size:0.9em; +margin-left:11.5em; +width:inherit; +} + +div.controller_help dt a,div.controller_help h1 a,div#help_unhappy h1 a.hover_a { +text-decoration:none; +font-size:0.9em; +background-color:#fff; +} + +div.controller_help dt:hover > a,div.controller_help h1:hover > a,div#help_unhappy h1:hover > a.hover_a { +color:#777; +} + +#hash_link_padding { +margin-bottom:10em; +} + +#contact_preamble { +width:auto; +margin:0 0 30px; +} + +div.feed_link_main { +display:inline; +} + +#footer { +position:relative; +clear:both; +float:left; +width:100%; +height:2em; +font-size:0.85em; +background-color:#F0F0F0; +border-color:#FFF; +border-style:solid; +border-width:3px 0 0; +margin:60px 0 0; +padding:0.5em 0; +} + +.popup { +background-color:#D5FFD8; +border:solid 3px #16C132; +z-index:2000; +overflow:auto; +text-align:center; +margin-top:2px; +} + +.popup .popup-content{ +margin:0.5em; +width: 95%; +float: left; +} + +.popup p { +margin: 0; +} + +#everypage h2,#everypage h3 { +margin:0.5em 0; +} + + +.popup .popup-close { +color:#FFF; +text-decoration:none; +display:inline-block; +border-radius:2px; +-moz-border-radius:2px; +cursor:pointer; +background: image-url('small-green-cross.png') no-repeat 0; +width:15px; +height:15px; +border:solid 0 #FFF; +text-indent:-999px; +overflow:hidden; +float:right; +padding:10px 0; +} + +#game_sidebar { +float:right; +clear:none; +width:20em; +font-size:0.9em; +margin:0 0 2em 2em; +} + +#user_photo_on_profile img,#user_photo_on_profile #set_photo { +width:96px; +height:96px; +float:left; +vertical-align:middle; +text-align:center; +border:1px solid #ddd; +margin-right:5px; +padding:2px; +} + +.user_photo_on_request img { +width:48px; +height:48px; +float:left; +vertical-align:middle; +border:1px solid #ddd; +margin-right:5px; +padding:2px; +} + +.user_photo_on_comment img { +width:36px; +height:36px; +float:left; +vertical-align:middle; +border:1px solid #ddd; +margin-right:5px; +margin-top:5px; +padding:2px; +} + +.user_photo_on_search img { +width:48px; +height:48px; +vertical-align:middle; +border:1px solid #ddd; +margin-right:5px; +padding:2px; +} + +div.user_about_me { +overflow:auto; +margin:1em 1.5em; +padding:0 0.5em; +} + +#user_public_banned { +background-color:#d0d0d0; +margin:0 14em 0 0; +padding:0.5em 1em; +} + +#user_public_banned .details { +margin-left:4em; +margin-right:4em; +font-size:0.9em; +font-style:italic; +} + +div.lang { +text-align:right; +font-size:0.9em; +right:0; +z-index:200; +top:40px; +position:absolute; +padding:0; +} + +div#user_locale_switcher { +margin:5px; +} + +#topnav li,#signin,.user_photo_on_search { +float:left; +} + +#view-html-content table,#request_details table { +border-collapse:collapse; +margin-bottom:1em; +} + +#view-html-content td,th,#request_details td,th { +border:solid 1px #000; +} + +#view-html-content td,#request_details td { +vertical-align:top; +max-width:30em; +overflow:auto; +} + +#view-html-content tr:nth-child(odd),#request_details tr.odd { +background-color:#bbb; +} + +#view-html-content tr:nth-child(even),#request_details tr.even { +background-color:#ddd; +} + +h3,.request_short_listing h3 { +font-size:1.2em; +} + +#frontpage_examples p,#frontpage_examples h2 { +text-align:center; +clear:both; +} + +.request_short_listing,#request_form { +margin-top:1em; +} + +.form_explanation,div[id|="comment"] p { +font-size:0.9em; +} + +.undescribed_requests,#preview_form p,.attachment { +clear:both; +} + +.single_user,#user_change_password_email,#user_not_logged_in { +clear:left; +} + +.correspondence_text,.comment_in_request_text { +margin:0 1.2em 0 0.9em; +} + +#request_header_text,#request_search_ahead_results { +font-size:0.9em; +margin-left:11em; +} + +div.feed_link img,div.act_link img { +border:none; +vertical-align:middle; +text-decoration:none; +} + +#follow_box { + + padding: 4px; +} + +#follow_box .feed_link { + text-align: center; +} + +#follow_count { + color: #93278F; + font-family: 'DeliciousBold', Arial, sans-serif; + font-weight: 700; + font-size: 60px; + line-height: 60px; + text-align: right; + float: left; + margin-top: -15px; + margin-right: 5px; +} +.follow_count { + clear:both; +} + +#follow_box h2 { + margin: 0; +} + +h2,dt { +font-size:1.8em; +} + +h3 { +text-decoration:none; +font-size:1.6em; +margin-top:3px; +margin-bottom:10px; +} + +a { +text-decoration:underline; +} + +#banner_inner { +width:890px; +position:relative; +margin:auto; +} + +#banner_inner a#logo { +position:absolute; +left:0; +top:70px; +z-index:1000; +} + +a img { +border:none; +} + +#navigation_search input[type=image] { +border:0; +margin-bottom:-9px; +margin-left:-4px; +} + +#navigation_search input[type=text] { +border-radius:5px 0 0 5px; +-moz-border-radius:5px 0 0 5px; +border-color:#222; +height: 14px; +} + +#topnav ul li { +margin:0 3px; +padding:10px 15px; +} + +#topnav ul li.selected { +background:#FFF; +} + +.request_right { +padding-top:5px; +width:245px; +float:left; +} + +#request_header_text { +margin-left:110px; +width: 30em; +border-radius:3px; +-moz-border-radius:3px; +margin-top: 10px; +background-color:#D5FFD8; +border-color:#1EFF38; +border-style:solid; +border-width:1px; +padding:0.5em; + +font-style: italic; + + +} + +#request_header_text h3 { + font-size: 1em; +} + +#stepwise_make_request a img { +margin-bottom:-10px; +margin-top:-10px; +margin-left:6px; +} + +p.subtitle { +margin-top:10px; +margin-bottom:20px; +font-size:1.2em; +font-style:normal; +color:#222; +} + +.results_section { +margin-bottom:40px; +} + +.results_section div:last-child { +border-bottom-width:0; +padding-bottom:0; +} + +.request_listing,.user_listing,.body_listing { +border-bottom:1px solid #DDD; +margin:0 0 -1px; +padding:12px 0 6px; +} + +.request_listing span.head,.user_listing span.head,.body_listing span.head { +background:none; +font-size: 1.5em; +margin-bottom:6px; +padding:0; +} + +.request_listing span.head a,.user_listing span.head a,.body_listing span.head a { +text-decoration:none; +font-size:1.3em; +margin-top:3px; +display:block; +margin-bottom:-6px; +} + +.request_listing .requester { +padding-bottom:0; +} + +.body_listing span.desc,.body_listing span.bottomline,.user_listing span.bottomline { +font-style:normal; +font-weight:400; +margin:0; +padding:0; +} + +.request_listing span.bottomline { +font-style:normal; +margin-bottom:0; +margin-top:12px; +background-position:top left; +font-size:1.1em; +font-weight:400; +min-height:36px; +padding:3px 0 0 27px; +} + +.user_listing { +padding-top:10px; +padding-bottom:0; +} + +.icon_failed,.icon_rejected { +background-image:image-url('status-denied.png'); +color:#C1272D; +} + +#request_sidebar { +width:212px; +} + +.feed_link { +padding:4px 0; +} + +.request_listing span.desc { +background:image-url('quote-marks.png') no-repeat; +min-height:60px; +width:100%; +color:#444; +line-height:18px; +padding:0 0 0 40px; +} + +#search_form { +margin:0 -6px 20px 0; +} + +#advanced-search input[type=text] { +width:auto; +} + +#search_form input[type=submit] { +border-radius:0 2px 2px 0; +-moz-border-radius:0 2px 2px 0; +} + +#header_right { +float:right; +width:230px; +padding-top:27px; +} + +.feed_link,.act_link { +display:block !important; +margin-bottom:10px; +} + +.feed_link a,.act_link a,#header_right > a { +text-decoration:none; +} + +.feed_link a img,.act_link img,.act_link a img { +padding-right:2px; +} + +form.feed_form input[type="submit"] { +line-height:12px; +padding:2px 4px; +} + +#header_right > br { +line-height:200%; +} + +#general_search h2 { +clear:both; +margin-top:20px; +} + +h2.foi_results,h2.person_results,h2.publicbody_results { +padding-top:0; +padding-bottom:15px; +margin-bottom:0; +margin-top:0; +} + +h2.foi_results { +width:600px; +} + +.list_toggle_controls { +padding-bottom:20px; +} + +#request_advice ol { +margin-left:18px; +margin-top:20px; +display:block; +padding:0; +} + +#request_advice ol li { +padding-bottom:5px; +} + +#request_form label,label.form_label,span#to_public_body { +font-size: 1.1em; +} + +#date_range label,#filter_requests_form label { +display:inline; +float:none; +padding-right:5px; +} + +#date_range label.title,#filter_requests_form label.title,h3.title { +display:inline-block; +float:none; +width:110px; +} + +h3.title { +width:114px; +margin-bottom:5px; +vertical-align: middle; +} + +.filter-request-types { + display: inline-block; + width: 455px; + vertical-align: middle; + margin-top: 3px; + line-height: 1.5em; +} + +#requests-subfilters div { +margin-top:10px; +} + +#requests-subfilters #latest_status_0,#requests-subfilters #request_variety_0 { +margin-left:0; +} + +#requests-subfilters input[type=checkbox] { +margin-left:117px; +} + +span#to_public_body { +font-weight: bold; +} + +#left_column { +width:600px; +float:left; +} + +#right_column_flip { +width:220px; +float:left; +margin-top:20px; +} + +#left_column_flip { +width:620px; +float:right; +margin-top:10px; +} + +#right_column { +width:220px; +float:right; +margin-top:30px; +} + +#left_half { +width:45%; +float:left; +} + +#right_half { +width:45%; +float:right; +} + +#middle_strip { +float:left; +width:10%; +height:100px; +text-align:center; +margin-top:45px; +font-size:1.2em; +font-family:Georgia; +font-style:italic; +color:#444; +} + +#sign_together h1 { +width:320px; +text-align:center; +} + +#sign_together .form_button { +margin-left:10.5em; +} + +form input[type=text],form input[type=password] { +font-size:1.1em; +width:200px; +color:#555; +border-radius:3px; +-moz-border-radius:3px; +border-color:#BBB; +border-style:solid; +border-width:1px; +padding:5px; +} + +form input.use-datepicker[type=text] { +width:130px !important; +background:image-url('calendar.png') no-repeat 115px 3px; +border-radius:3px !important; +-moz-border-radius:3px !important; +font-size:1.1em !important; +margin:0 !important; +} + +/* Remove button padding in FF */ +button::-moz-focus-inner, +input[type="button"]::-moz-focus-inner, +input[type="submit"]::-moz-focus-inner { + border:0; + padding:0; +} + +form input[type=submit],a.link_button_green,a.link_button_green_large { +color:#FFF; +background-color: #8cc63f; /* fallback color if gradients are not supported */ +background-image: image-url('button-gradient.png'); +background-image: -webkit-linear-gradient(top, #8cc63f, #6b9731); /* For Chrome and Safari */ +background-image: -moz-linear-gradient(top, #8cc63f, #6b9731); /* For old Fx (3.6 to 15) */ +background-image: -ms-linear-gradient(top, #8cc63f, #6b9731); /* For pre-releases of IE 10*/ +background-image: -o-linear-gradient(top, #8cc63f, #6b9731); /* For old Opera (11.1 to 12.0) */ +background-image: linear-gradient(to bottom, #8cc63f, #6b9731); /* Standard syntax; must be last */ +text-decoration:none; +display:inline-block; +line-height:18px; +border:solid 1px #69952F; +border-radius:2px; +-moz-border-radius:2px; +text-shadow:1px 1px 0 #5B841D; +font-size:18px; +cursor:pointer; +padding:5px 6px; +} + +.feed_link a.link_button_green { + color:white; +} +a.link_button_green_large { +background-image: image-url('button-gradient-large.png'); +background-image: -webkit-linear-gradient(top, #8cc63f, #6b9731); /* For Chrome and Safari */ +background-image: -moz-linear-gradient(top, #8cc63f, #6b9731); /* For old Fx (3.6 to 15) */ +background-image: -ms-linear-gradient(top, #8cc63f, #6b9731); /* For pre-releases of IE 10*/ +background-image: -o-linear-gradient(top, #8cc63f, #6b9731); /* For old Opera (11.1 to 12.0) */ +background-image: linear-gradient(to bottom, #8cc63f, #6b9731); /* Standard syntax; must be last */ +font-size:2em; +line-height:22px; +padding-bottom:7px; +} + +form input[type=submit].small { +font-size:1.1em; +line-height:10px; +padding:4px 9px; +} + +ul.no_bullets { +list-style-type:none; +margin:0 0 30px; +padding:0; +} + +ul.no_bullets li { +margin-bottom:6px; +} + +#frontpage_search { +background:transparent; +} + +#public_body_list #right_column ul { +list-style:none; +margin:0; +padding:0 0 20px; +} + +div.blog_post p { +line-height:180%; +} + +div.frontpage-box { +background:image-url('stripes.png'); +border:1px solid #DEBEDD; +border-radius:5px; +-moz-border-radius:5px; +color:#DDD; +text-align:center; +width:255px; +height:210px; +float:left; +padding:15px 12px 0; +} + +#frontpage-box-1 { +margin-right:10px; +vertical-align:middle; +} + +#frontpage-box-2 { +margin-right:10px; +} + +#frontpage-box-3 #search_form input[type=text] { +width:10em; +} + +#frontpage_examples p,#frontpage_examples ul { +text-align:left; +} + +#examples_1 ul li { +border-bottom:1px solid #ddd; +padding:5px 0; +} + +#frontpage_examples .excerpt { +cursor:pointer; +background:image-url('quote-marks.png') no-repeat; +color:#444; +line-height:18px; +min-height:30px; +font-style:italic; +padding:0 0 0 40px; +} + +#set_photo { +background:image-url('defaultprofilepic.png'); +} + +div.correspondence,div.comment_in_request { +width:600px; +border-radius:6px; +-moz-border-radius:6px; +border-width:1px; +padding:4px 20px 0 9px; +} + +div.outgoing.correspondence { + background: #FFFFD2; +} + +div[id|="comment"] p { +} + +div.comment_in_request { +float:left; +overflow:auto; +width:550px; +border-color:#5F5F5F; +border-style:dotted; +border-width:1px; +margin:0 0 1em 50px; +padding:0 0.5em; +} + +.comment_in_request_text { +margin:0 0 0 10px; +} + +#frontpage_splash { +background:image-url('flying-computer.png') no-repeat 175px bottom; +height:375px; +margin-top:-12px; +margin-bottom:20px; +width:100%; +} + +#frontpage_splash #left_column { +line-height:40px; +margin-top:66px; +} + +#frontpage_splash h1 { +font-size:39px; +color:#222; +font-weight:400; +margin:0 0 20px; +} + +#frontpage_splash h1 strong { +font-size:54px; +color:#222; +font-weight:400; +} + +#frontpage_splash h1 span { +font-family:Georgia; +font-style:italic; +font-weight:400; +font-size:25px; +color:#444; +} + +#frontpage_splash h2 { +font-size:26px; +font-weight:400; +color:#222; +margin-bottom:10px; +line-height:28px; +} + +#frontpage_splash h2 strong { +font-size:31px; +color:#222; +} + +#frontpage_splash h2 span { +color:#333; +font-style:italic; +font-size:19px; +font-family:Georgia; +} + +#frontpage_splash #right_column { +width:265px; +} + +#frontpage_splash #right_column input[type=text] { +width:180px; +} + +#frontpage_splash #frontpage_search_box { +margin-bottom:30px; +margin-top:-10px; +} + +#frontpage_splash #frontpage_right_to_know p { +line-height:20px; +} + +body.front h3 { +font-size:28px; +} + +#ui-datepicker-div.ui-widget { +font-family:Arial, sans-serif; +color:#93278F; +} + +#ui-datepicker-div .ui-datepicker-header,#ui-datepicker-div .ui-widget-header { +background:none; +border:solid 0 #FFF; +color:#444; +font-size:17px; +font-weight:400; +line-height:1.5em !important; +} + +#ui-datepicker-div .ui-state-default { +background:#F2F2F2; +border:solid 0 #FFF; +border-radius:2px; +-moz-border-radius:2px; +} + +#ui-datepicker-div .ui-state-default:hover { +background:#222; +color:#FFF; +} + +#ui-datepicker-div .ui-state-active { +background:#222; +color:#FFF; +} + +#ui-datepicker-div .ui-icon-circle-triangle-w,#ui-datepicker-div .ui-icon-circle-triangle-e { +background-image:image-url('ui-icons-theme.png'); +} + +#ui-datepicker-div .ui-datepicker-prev-hover { +left:2px; +top:2px; +border:none; +background:#FFF; +cursor:pointer; +opacity:1; +} + +#ui-datepicker-div .ui-datepicker-next-hover { +right:2px; +top:2px; +border:none; +background:#FFF; +cursor:pointer; +opacity:1; +} + + +p.public-body-name-prefix { +color:#888; +margin-top:15px; +margin-bottom:-15px; +font-size: 1.2em; +} + +.close-button { +color:#FFF; +text-decoration:none; +display:inline-block; +border-radius:2px; +-moz-border-radius:2px; +cursor:pointer; +background:image-url('small-white-cross.png') no-repeat; +width:15px; +height:15px; +border:solid 0 #FFF; +text-indent:-999px; +overflow:hidden; +float:right; +padding:10px 0; +} + +#link_box { +position:absolute; +text-align:left; +background-color:#FFF; +z-index:999; +opacity:0.9; +border-radius:6px; +-moz-border-radius:6px; +border:1px solid #444; +display:none; +padding:5px; +} + +#link_box .close-button { +background-color:#444; +margin-left:15px; +padding:0; +} + +a.link_to_this { +display:inline-block; +width:20px; +letter-spacing:-1000em; +overflow:hidden; +background:image-url('link-icon.png') no-repeat; +} + +#to_public_body { +display:block; +margin-bottom:15px; +} + +.fieldWithErrors textarea,.fieldWithErrors input { +border:solid 1px Red !important; +} + +.errorExplanation { +border-radius:6px; +-moz-border-radius:6px; +font-weight:400; +width:554px; +margin:20px 0 30px; +} + +#notice,.describe_state_form,#other_recipients { +font-weight:400; +background:#E9FDD3 !important; +color:#517704; +border-radius:6px; +-moz-border-radius:6px; +border-color:#B0CA86; +margin:15px 0; +padding:10px 20px; +} + +.describe_state_form hr { +border-top:0; +border-color:#B0CA86; +border-style:dotted; +margin:20px 0; +} + +.describe_state_form { +color:#333; +} + +#notice p:first-child { +margin-top:0; +} + +#notice p:last-child { +margin-bottom:0; +} + +div.correspondence p.preview_subject { +font-size:1.2em !important; +margin-left:10px; +line-height:25px; +} + +div.correspondence p.preview_subject strong { +} + +#preview_form ul { +margin:0; +padding:1px 32px 10px; +} + +#preview_form ul li { +margin:10px 0; +} + +div.controller_help h1 a,#logged_in_bar a,#logged_in_bar a:visited,#stepwise_make_request strong { +} + +.request_left,#header_left { +width:575px; +padding-right:50px; +float:left; +} +.medium_column { +width:575px; +padding-right:50px; +} + +#authority_preview .request_left, +#authority_preview #header_left { + width: 100%; +} + +#request_sidebar h2,.list-filter-item { +margin-bottom:10px; +} + +div.ff-icon-printfix,.comment_in_request_text img.comment_quote,body.front #other-country-notice,#other-country-notice,#authority_preview .public-body-name-prefix,#authority_preview #list-filter,#authority_preview h2.foi_results,div#show_response_view p.event_actions, div.batch_public_body_toggle { +display:none; +} + +#ui-datepicker-div .ui-datepicker-prev,#ui-datepicker-div .ui-datepicker-next { +margin-top:2px; +opacity:0.5; +} + +div.controller_help dt:hover > a:hover,div.controller_help h1:hover > a:hover,div#help_unhappy h1:hover > a.hover_a:hover,h2 a:hover,.request_listing span.head a:hover,.user_listing span.head a:hover,.body_listing span.head a:hover,.request_listing .requester a,.feed_link a:hover,.act_link a:hover,#header_right > a:hover { +text-decoration:underline; +} + +.request_listing a,.body_listing a,.user_listing a,.request_short_listing a,h2 a,.feed_link a { +text-decoration:none; +} + +.big { + font-size: larger; +} + +.public-body-ranking { + margin-bottom: 40px; +} + +.public-body-ranking-title { + margin-top: 25px; + margin-bottom: 10px; +} + +.public-body-ranking table { + margin-top: 20px; + margin-left: 30px; +} + +.public-body-ranking td, th { + border: 0px; + padding: 5px; + padding-right: 20px; +} + +.public-body-ranking td.statistic { + text-align: center; +} + +.public-body-ranking .axisLabels { + /* Justification for using !important hereL the axis label color is + set in the style attribute in Flot's Javascript to the same + colour as the grid background. Changing this requires quite + invasive changes to the Javascript, and is likely to be + irrelevant in the next version of Flot anyway, which will have + core support for axis labels. So, just use !important to make + the axes black rather than transparent grey for the moment: */ + color: #000 !important; +} + + +#body_selection .body_list { + width: 45%; +} + +#body_selection .body_list input[type='submit'] { + margin: 10px 0; + width: 45%; +} + +#body_selection .body_list #body_select_all_button, +#body_selection .body_list #body_deselect_button{ + float: right; +} + +#body_selection #body_candidates { + float: left; +} + +#body_selection #body_selections { + float: right; +} + +#body_selection #body_submission input[type='submit'] { + margin: 10px 0; + width:100%; +} + +#body_selection .select_all_button { + display: none; +} + +#body_selection .body_select { + width: 100%; +} + +.batch_public_body_list { + margin-left: 110px; +} + +.batch_public_body_toggle { + color: #0000EE; + font-size: 0.9em; +} diff --git a/app/assets/stylesheets/print.css b/app/assets/stylesheets/print.css new file mode 100644 index 000000000..6f29c18da --- /dev/null +++ b/app/assets/stylesheets/print.css @@ -0,0 +1,49 @@ +div#content, div#left_column, div.entirebody div#wrapper { + width: 100%; + margin: 0; + float: none; +} +div#content { + padding-right: 0; + width: 96%; +} + +#wrapper { + width: auto; + padding: 0; + +} + +p.event_actions, +div#after_actions, +#right_column, +#banner, +.admin .navbar, +#header_right, +#describe_state_form_1, +#describe_state_form_2, +.attachment_image, +#footer { + display: none; +} + +div.correspondence { + background: none; + border: 1px solid #DDD; +} + +p#request_status { + page-break-after: avoid; +} +div.correspondence { + page-break-before: avoid; +} + +#other-country-notice { + display: none; +} + +.not-for-print { + display: none !IMPORTANT; +} + diff --git a/app/controllers/admin_censor_rule_controller.rb b/app/controllers/admin_censor_rule_controller.rb index 5381921bf..6f79b5ba1 100644 --- a/app/controllers/admin_censor_rule_controller.rb +++ b/app/controllers/admin_censor_rule_controller.rb @@ -2,7 +2,7 @@ # For modifying requests. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class AdminCensorRuleController < AdminController def new @@ -10,7 +10,7 @@ class AdminCensorRuleController < AdminController @info_request = InfoRequest.find(params[:info_request_id]) end if params[:user_id] - @user = User.find(params[:user_id]) + @censor_user = User.find(params[:user_id]) end end @@ -26,9 +26,9 @@ class AdminCensorRuleController < AdminController end flash[:notice] = 'CensorRule was successfully created.' if !@censor_rule.info_request.nil? - redirect_to admin_url('request/show/' + @censor_rule.info_request.id.to_s) + redirect_to admin_request_show_url(@censor_rule.info_request) elsif !@censor_rule.user.nil? - redirect_to admin_url('user/show/' + @censor_rule.user.id.to_s) + redirect_to admin_user_show_url(@censor_rule.user) else raise "internal error" end @@ -53,9 +53,9 @@ class AdminCensorRuleController < AdminController end flash[:notice] = 'CensorRule was successfully updated.' if !@censor_rule.info_request.nil? - redirect_to admin_url('request/show/' + @censor_rule.info_request.id.to_s) + redirect_to admin_request_show_url(@censor_rule.info_request) elsif !@censor_rule.user.nil? - redirect_to admin_url('user/show/' + @censor_rule.user.id.to_s) + redirect_to admin_user_show_url(@censor_rule.user) else raise "internal error" end @@ -79,9 +79,9 @@ class AdminCensorRuleController < AdminController flash[:notice] = "CensorRule was successfully destroyed." if !info_request.nil? - redirect_to admin_url('request/show/' + info_request.id.to_s) + redirect_to admin_request_show_url(info_request) elsif !user.nil? - redirect_to admin_url('user/show/' + user.id.to_s) + redirect_to admin_user_show_url(user) else raise "internal error" end diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index d93e68dab..8b606ea85 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -2,7 +2,7 @@ # All admin controllers are dervied from this. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'fileutils' @@ -17,7 +17,7 @@ class AdminController < ApplicationController end # Always give full stack trace for admin interface - def local_request? + def show_rails_exceptions? true end @@ -29,8 +29,7 @@ class AdminController < ApplicationController FileUtils.rm_rf(cache_subpath) # Remove any download zips - download_dir = request_download_zip_dir(info_request) - FileUtils.rm_rf(download_dir) + FileUtils.rm_rf(info_request.download_zip_dir) # Remove the database caches of body / attachment text (the attachment text # one is after privacy rules are applied) @@ -51,7 +50,7 @@ class AdminController < ApplicationController # For administration interface, return display name of authenticated user def admin_current_user - if Configuration::skip_admin_auth + if AlaveteliConfiguration::skip_admin_auth admin_http_auth_user else session[:admin_name] @@ -74,12 +73,12 @@ class AdminController < ApplicationController end def authenticate - if Configuration::skip_admin_auth + if AlaveteliConfiguration::skip_admin_auth session[:using_admin] = 1 return else if session[:using_admin].nil? || session[:admin_name].nil? - if params[:emergency].nil? + if params[:emergency].nil? || AlaveteliConfiguration::disable_emergency_user if authenticated?( :web => _("To log into the administrative interface"), :email => _("Then you can log into the administrative interface"), @@ -89,7 +88,6 @@ class AdminController < ApplicationController session[:using_admin] = 1 session[:admin_name] = @user.url_name else - session[:using_admin] = nil session[:user_id] = nil session[:admin_name] = nil @@ -98,7 +96,7 @@ class AdminController < ApplicationController end else authenticate_or_request_with_http_basic do |user_name, password| - if user_name == Configuration::admin_username && password == Configuration::admin_password + if user_name == AlaveteliConfiguration::admin_username && password == AlaveteliConfiguration::admin_password session[:using_admin] = 1 session[:admin_name] = user_name else diff --git a/app/controllers/admin_general_controller.rb b/app/controllers/admin_general_controller.rb index 9f4c398c1..753208c9a 100644 --- a/app/controllers/admin_general_controller.rb +++ b/app/controllers/admin_general_controller.rb @@ -2,9 +2,10 @@ # Controller for main admin pages. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class AdminGeneralController < AdminController + def index # ensure we have a trailing slash current_uri = request.env['REQUEST_URI'] @@ -26,13 +27,16 @@ class AdminGeneralController < AdminController @comment_count = Comment.count # Tasks to do - @requires_admin_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'requires_admin'"], :order => "last_event_time") - @error_message_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'error_message'"], :order => "last_event_time") - @attention_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'attention_requested'"], :order => "last_event_time") - @blank_contacts = PublicBody.find(:all, :conditions => ["request_email = ''"], :order => "updated_at") + @requires_admin_requests = InfoRequest.find_in_state('requires_admin') + @error_message_requests = InfoRequest.find_in_state('error_message') + @attention_requests = InfoRequest.find_in_state('attention_requested') + @blank_contacts = PublicBody.find(:all, :conditions => ["request_email = ''"], + :order => "updated_at") @old_unclassified = InfoRequest.find_old_unclassified(:limit => 20, - :conditions => ["prominence = 'normal'"]) + :conditions => ["prominence = 'normal'"]) @holding_pen_messages = InfoRequest.holding_pen_request.incoming_messages + @new_body_requests = PublicBodyChangeRequest.new_body_requests.open + @body_update_requests = PublicBodyChangeRequest.body_update_requests.open end def timeline @@ -115,18 +119,30 @@ class AdminGeneralController < AdminController end def stats + # Overview counts of things + @public_body_count = PublicBody.count + + @info_request_count = InfoRequest.count + @outgoing_message_count = OutgoingMessage.count + @incoming_message_count = IncomingMessage.count + + @user_count = User.count + @track_thing_count = TrackThing.count + + @comment_count = Comment.count @request_by_state = InfoRequest.count(:group => 'described_state') @tracks_by_type = TrackThing.count(:group => 'track_type') end def debug @admin_current_user = admin_current_user - @current_commit = `git log -1 --format="%H"` + @current_commit = alaveteli_git_commit @current_branch = `git branch | perl -ne 'print $1 if /^\\* (.*)/'` @current_version = `git describe --always --tags` repo = `git remote show origin -n | perl -ne 'print $1 if m{Fetch URL: .*github\\.com[:/](.*)\\.git}'` @github_origin = "https://github.com/#{repo}/tree/" @request_env = request.env end + end diff --git a/app/controllers/admin_incoming_message_controller.rb b/app/controllers/admin_incoming_message_controller.rb new file mode 100644 index 000000000..6b50d0e36 --- /dev/null +++ b/app/controllers/admin_incoming_message_controller.rb @@ -0,0 +1,80 @@ +class AdminIncomingMessageController < AdminController + + def edit + @incoming_message = IncomingMessage.find(params[:id]) + end + + def update + @incoming_message = IncomingMessage.find(params[:id]) + old_prominence = @incoming_message.prominence + old_prominence_reason = @incoming_message.prominence_reason + @incoming_message.prominence = params[:incoming_message][:prominence] + @incoming_message.prominence_reason = params[:incoming_message][:prominence_reason] + if @incoming_message.save + @incoming_message.info_request.log_event('edit_incoming', + :incoming_message_id => @incoming_message.id, + :editor => admin_current_user(), + :old_prominence => old_prominence, + :prominence => @incoming_message.prominence, + :old_prominence_reason => old_prominence_reason, + :prominence_reason => @incoming_message.prominence_reason) + expire_for_request(@incoming_message.info_request) + flash[:notice] = 'Incoming message successfully updated.' + redirect_to admin_request_show_url(@incoming_message.info_request) + else + render :action => 'edit' + end + end + + def destroy + @incoming_message = IncomingMessage.find(params[:incoming_message_id]) + @info_request = @incoming_message.info_request + incoming_message_id = @incoming_message.id + + @incoming_message.fully_destroy + @incoming_message.info_request.log_event("destroy_incoming", + { :editor => admin_current_user(), :deleted_incoming_message_id => incoming_message_id }) + # expire cached files + expire_for_request(@info_request) + flash[:notice] = 'Incoming message successfully destroyed.' + redirect_to admin_request_show_url(@info_request) + end + + def redeliver + incoming_message = IncomingMessage.find(params[:redeliver_incoming_message_id]) + message_ids = params[:url_title].split(",").each {|x| x.strip} + previous_request = incoming_message.info_request + destination_request = nil + ActiveRecord::Base.transaction do + for m in message_ids + if m.match(/^[0-9]+$/) + destination_request = InfoRequest.find_by_id(m.to_i) + else + destination_request = InfoRequest.find_by_url_title!(m) + end + if destination_request.nil? + flash[:error] = "Failed to find destination request '" + m + "'" + return redirect_to admin_request_show_url(previous_request) + end + + raw_email_data = incoming_message.raw_email.data + mail = MailHandler.mail_from_raw_email(raw_email_data) + destination_request.receive(mail, raw_email_data, true) + + incoming_message_id = incoming_message.id + incoming_message.info_request.log_event("redeliver_incoming", { + :editor => admin_current_user(), + :destination_request => destination_request.id, + :deleted_incoming_message_id => incoming_message_id + }) + + flash[:notice] = "Message has been moved to request(s). Showing the last one:" + end + # expire cached files + expire_for_request(previous_request) + incoming_message.fully_destroy + end + redirect_to admin_request_show_url(destination_request) + end + +end diff --git a/app/controllers/admin_outgoing_message_controller.rb b/app/controllers/admin_outgoing_message_controller.rb new file mode 100644 index 000000000..ec0981677 --- /dev/null +++ b/app/controllers/admin_outgoing_message_controller.rb @@ -0,0 +1,47 @@ +class AdminOutgoingMessageController < AdminController + + def edit + @outgoing_message = OutgoingMessage.find(params[:id]) + end + + def destroy + @outgoing_message = OutgoingMessage.find(params[:outgoing_message_id]) + @info_request = @outgoing_message.info_request + outgoing_message_id = @outgoing_message.id + + @outgoing_message.fully_destroy + @outgoing_message.info_request.log_event("destroy_outgoing", + { :editor => admin_current_user(), :deleted_outgoing_message_id => outgoing_message_id }) + + flash[:notice] = 'Outgoing message successfully destroyed.' + redirect_to admin_request_show_url(@info_request) + end + + def update + @outgoing_message = OutgoingMessage.find(params[:id]) + + old_body = @outgoing_message.body + old_prominence = @outgoing_message.prominence + old_prominence_reason = @outgoing_message.prominence_reason + @outgoing_message.prominence = params[:outgoing_message][:prominence] + @outgoing_message.prominence_reason = params[:outgoing_message][:prominence_reason] + @outgoing_message.body = params[:outgoing_message][:body] + if @outgoing_message.save + @outgoing_message.info_request.log_event("edit_outgoing", + { :outgoing_message_id => @outgoing_message.id, + :editor => admin_current_user(), + :old_body => old_body, + :body => @outgoing_message.body, + :old_prominence => old_prominence, + :old_prominence_reason => old_prominence_reason, + :prominence => @outgoing_message.prominence, + :prominence_reason => @outgoing_message.prominence_reason }) + flash[:notice] = 'Outgoing message successfully updated.' + expire_for_request(@outgoing_message.info_request) + redirect_to admin_request_show_url(@outgoing_message.info_request) + else + render :action => 'edit' + end + end + +end diff --git a/app/controllers/admin_public_body_change_requests_controller.rb b/app/controllers/admin_public_body_change_requests_controller.rb new file mode 100644 index 000000000..d76cdc0e5 --- /dev/null +++ b/app/controllers/admin_public_body_change_requests_controller.rb @@ -0,0 +1,15 @@ +class AdminPublicBodyChangeRequestsController < AdminController + + def edit + @change_request = PublicBodyChangeRequest.find(params[:id]) + end + + def update + @change_request = PublicBodyChangeRequest.find(params[:id]) + @change_request.close! + @change_request.send_response(params[:subject], params[:response]) + flash[:notice] = 'The change request has been closed and the user has been notified' + redirect_to admin_general_index_path + end + +end diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb index ac12e97b2..120419a27 100644 --- a/app/controllers/admin_public_body_controller.rb +++ b/app/controllers/admin_public_body_controller.rb @@ -2,7 +2,7 @@ # Controller for editing public bodies from the admin interface. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require "public_body_categories" @@ -14,7 +14,8 @@ class AdminPublicBodyController < AdminController def _lookup_query_internal @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + underscore_locale = @locale.gsub '-', '_' + I18n.with_locale(@locale) do @query = params[:query] if @query == "" @query = nil @@ -23,12 +24,10 @@ class AdminPublicBodyController < AdminController if @page == "" @page = nil end - @public_bodies = PublicBody.paginate :order => "public_body_translations.name", :page => @page, :per_page => 100, - :conditions => @query.nil? ? "public_body_translations.locale = '#{@locale}'" : + @public_bodies = PublicBody.joins(:translations).where(@query.nil? ? "public_body_translations.locale = '#{underscore_locale}'" : ["(lower(public_body_translations.name) like lower('%'||?||'%') or lower(public_body_translations.short_name) like lower('%'||?||'%') or - lower(public_body_translations.request_email) like lower('%'||?||'%' )) AND (public_body_translations.locale = '#{@locale}')", @query, @query, @query], - :joins => :translations + lower(public_body_translations.request_email) like lower('%'||?||'%' )) AND (public_body_translations.locale = '#{underscore_locale}')", @query, @query, @query]).paginate :order => "public_body_translations.name", :page => @page, :per_page => 100 end @public_bodies_by_tag = PublicBody.find_by_tag(@query) end @@ -75,24 +74,46 @@ class AdminPublicBodyController < AdminController def show @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do @public_body = PublicBody.find(params[:id]) + @info_requests = @public_body.info_requests.paginate :order => "created_at desc", + :page => params[:page], + :per_page => 100 render end end def new @public_body = PublicBody.new - render + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end + if @change_request + @change_request_user_response = render_to_string(:template => "admin_public_body_change_requests/add_accepted", + :formats => [:txt]) + @public_body.name = @change_request.public_body_name + @public_body.request_email = @change_request.public_body_email + @public_body.last_edit_comment = @change_request.comment_for_public_body + end + render :formats => [:html] end def create - PublicBody.with_locale(I18n.default_locale) do + I18n.with_locale(I18n.default_locale) do + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end params[:public_body][:last_edit_editor] = admin_current_user() @public_body = PublicBody.new(params[:public_body]) if @public_body.save + if @change_request + response_text = params[:response].gsub(_("[Authority URL will be inserted here]"), + public_body_url(@public_body, :only_path => false)) + @change_request.close! + @change_request.send_response(params[:subject], response_text) + end flash[:notice] = 'PublicBody was successfully created.' - redirect_to admin_url('body/show/' + @public_body.id.to_s) + redirect_to admin_body_show_url(@public_body) else render :action => 'new' end @@ -101,17 +122,34 @@ class AdminPublicBodyController < AdminController def edit @public_body = PublicBody.find(params[:id]) - @public_body.last_edit_comment = "" - render + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end + if @change_request + @change_request_user_response = render_to_string(:template => "admin_public_body_change_requests/update_accepted", + :formats => [:txt]) + @public_body.request_email = @change_request.public_body_email + @public_body.last_edit_comment = @change_request.comment_for_public_body + else + @public_body.last_edit_comment = "" + end + render :formats => [:html] end def update - PublicBody.with_locale(I18n.default_locale) do + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end + I18n.with_locale(I18n.default_locale) do params[:public_body][:last_edit_editor] = admin_current_user() @public_body = PublicBody.find(params[:id]) if @public_body.update_attributes(params[:public_body]) + if @change_request + @change_request.close! + @change_request.send_response(params[:subject], params[:response]) + end flash[:notice] = 'PublicBody was successfully updated.' - redirect_to admin_url('body/show/' + @public_body.id.to_s) + redirect_to admin_body_show_url(@public_body) else render :action => 'edit' end @@ -120,7 +158,7 @@ class AdminPublicBodyController < AdminController def destroy @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do public_body = PublicBody.find(params[:id]) if public_body.info_requests.size > 0 @@ -141,16 +179,18 @@ class AdminPublicBodyController < AdminController @errors = "" if request.post? dry_run_only = (params['commit'] == 'Upload' ? false : true) + # (FIXME: both of these cases could now be changed to use + # PublicBody.import_csv_from_file.) # Read file from params if params[:csv_file] csv_contents = params[:csv_file].read @original_csv_file = params[:csv_file].original_filename + csv_contents = normalize_string_to_utf8(csv_contents) # or from previous dry-run temporary file elsif params[:temporary_csv_file] && params[:original_csv_file] csv_contents = retrieve_csv_data(params[:temporary_csv_file]) @original_csv_file = params[:original_csv_file] end - if !csv_contents.nil? # Try with dry run first errors, notes = PublicBody.import_csv(csv_contents, diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb index e39d55c7c..fc291d998 100644 --- a/app/controllers/admin_request_controller.rb +++ b/app/controllers/admin_request_controller.rb @@ -2,7 +2,7 @@ # Controller for viewing FOI requests from the admin interface. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'ostruct' @@ -14,45 +14,32 @@ class AdminRequestController < AdminController def list @query = params[:query] - @info_requests = InfoRequest.paginate :order => "created_at desc", + if @query + info_requests = InfoRequest.where(["lower(title) like lower('%'||?||'%')", @query]) + else + info_requests = InfoRequest + end + @info_requests = info_requests.paginate :order => "created_at desc", :page => params[:page], - :per_page => 100, - :conditions => @query.nil? ? nil : ["lower(title) like lower('%'||?||'%')", @query] - end - - def list_old_unclassified - @info_requests = WillPaginate::Collection.create((params[:page] or 1), 50) do |pager| - info_requests = InfoRequest.find_old_unclassified(:conditions => ["prominence = 'normal'"], - :limit => pager.per_page, - :offset => pager.offset) - # inject the result array into the paginated collection: - pager.replace(info_requests) - - unless pager.total_entries - # the pager didn't manage to guess the total count, do it manually - pager.total_entries = InfoRequest.count_old_unclassified(:conditions => ["prominence = 'normal'"]) - end - end + :per_page => 100 end def show @info_request = InfoRequest.find(params[:id]) - # XXX is this *really* the only way to render a template to a - # variable, rather than to the response? - vars = OpenStruct.new(:name_to => @info_request.user_name, - :name_from => Configuration::contact_name, - :info_request => @info_request, :reason => params[:reason], - :info_request_url => 'http://' + Configuration::domain + request_url(@info_request), - :site_name => site_name) - template = File.read(File.join(File.dirname(__FILE__), "..", "views", "admin_request", "hidden_user_explanation.rhtml")) - @request_hidden_user_explanation = ERB.new(template).result(vars.instance_eval { binding }) + vars_for_explanation = {:reason => params[:reason], + :info_request => @info_request, + :name_to => @info_request.user_name, + :name_from => AlaveteliConfiguration::contact_name, + :info_request_url => request_url(@info_request, :only_path => false)} + @request_hidden_user_explanation = render_to_string(:template => "admin_request/hidden_user_explanation", + :locals => vars_for_explanation) end def resend @outgoing_message = OutgoingMessage.find(params[:outgoing_message_id]) @outgoing_message.resend_message flash[:notice] = "Outgoing message resent" - redirect_to request_admin_url(@outgoing_message.info_request) + redirect_to admin_request_show_url(@outgoing_message.info_request) end def edit @@ -73,9 +60,6 @@ class AdminRequestController < AdminController @info_request.title = params[:info_request][:title] @info_request.prominence = params[:info_request][:prominence] - if @info_request.described_state != params[:info_request][:described_state] - @info_request.set_described_state(params[:info_request][:described_state]) - end @info_request.awaiting_description = params[:info_request][:awaiting_description] == "true" ? true : false @info_request.allow_new_responses_from = params[:info_request][:allow_new_responses_from] @info_request.handle_rejected_responses = params[:info_request][:handle_rejected_responses] @@ -88,17 +72,20 @@ class AdminRequestController < AdminController { :editor => admin_current_user(), :old_title => old_title, :title => @info_request.title, :old_prominence => old_prominence, :prominence => @info_request.prominence, - :old_described_state => old_described_state, :described_state => @info_request.described_state, + :old_described_state => old_described_state, :described_state => params[:info_request][:described_state], :old_awaiting_description => old_awaiting_description, :awaiting_description => @info_request.awaiting_description, :old_allow_new_responses_from => old_allow_new_responses_from, :allow_new_responses_from => @info_request.allow_new_responses_from, :old_handle_rejected_responses => old_handle_rejected_responses, :handle_rejected_responses => @info_request.handle_rejected_responses, :old_tag_string => old_tag_string, :tag_string => @info_request.tag_string, :old_comments_allowed => old_comments_allowed, :comments_allowed => @info_request.comments_allowed }) + if @info_request.described_state != params[:info_request][:described_state] + @info_request.set_described_state(params[:info_request][:described_state]) + end # expire cached files expire_for_request(@info_request) flash[:notice] = 'Request successfully updated.' - redirect_to request_admin_url(@info_request) + redirect_to admin_request_show_url(@info_request) else render :action => 'edit' end @@ -114,40 +101,7 @@ class AdminRequestController < AdminController # expire cached files expire_for_request(@info_request) flash[:notice] = "Request #{url_title} has been completely destroyed. Email of user who made request: " + user.email - redirect_to admin_url('request/list') - end - - def edit_outgoing - @outgoing_message = OutgoingMessage.find(params[:id]) - end - - def destroy_outgoing - @outgoing_message = OutgoingMessage.find(params[:outgoing_message_id]) - @info_request = @outgoing_message.info_request - outgoing_message_id = @outgoing_message.id - - @outgoing_message.fully_destroy - @outgoing_message.info_request.log_event("destroy_outgoing", - { :editor => admin_current_user(), :deleted_outgoing_message_id => outgoing_message_id }) - - flash[:notice] = 'Outgoing message successfully destroyed.' - redirect_to request_admin_url(@info_request) - end - - def update_outgoing - @outgoing_message = OutgoingMessage.find(params[:id]) - - old_body = @outgoing_message.body - - if @outgoing_message.update_attributes(params[:outgoing_message]) - @outgoing_message.info_request.log_event("edit_outgoing", - { :outgoing_message_id => @outgoing_message.id, :editor => admin_current_user(), - :old_body => old_body, :body => @outgoing_message.body }) - flash[:notice] = 'Outgoing message successfully updated.' - redirect_to request_admin_url(@outgoing_message.info_request) - else - render :action => 'edit_outgoing' - end + redirect_to admin_request_list_url end def edit_comment @@ -168,64 +122,12 @@ class AdminRequestController < AdminController :old_visible => old_visible, :visible => @comment.visible, }) flash[:notice] = 'Comment successfully updated.' - redirect_to request_admin_url(@comment.info_request) + redirect_to admin_request_show_url(@comment.info_request) else render :action => 'edit_comment' end end - - def destroy_incoming - @incoming_message = IncomingMessage.find(params[:incoming_message_id]) - @info_request = @incoming_message.info_request - incoming_message_id = @incoming_message.id - - @incoming_message.fully_destroy - @incoming_message.info_request.log_event("destroy_incoming", - { :editor => admin_current_user(), :deleted_incoming_message_id => incoming_message_id }) - # expire cached files - expire_for_request(@info_request) - flash[:notice] = 'Incoming message successfully destroyed.' - redirect_to request_admin_url(@info_request) - end - - def redeliver_incoming - incoming_message = IncomingMessage.find(params[:redeliver_incoming_message_id]) - message_ids = params[:url_title].split(",").each {|x| x.strip} - previous_request = incoming_message.info_request - destination_request = nil - ActiveRecord::Base.transaction do - for m in message_ids - if m.match(/^[0-9]+$/) - destination_request = InfoRequest.find_by_id(m.to_i) - else - destination_request = InfoRequest.find_by_url_title!(m) - end - if destination_request.nil? - flash[:error] = "Failed to find destination request '" + m + "'" - return redirect_to request_admin_url(previous_request) - end - - raw_email_data = incoming_message.raw_email.data - mail = MailHandler.mail_from_raw_email(raw_email_data) - destination_request.receive(mail, raw_email_data, true) - - incoming_message_id = incoming_message.id - incoming_message.info_request.log_event("redeliver_incoming", { - :editor => admin_current_user(), - :destination_request => destination_request.id, - :deleted_incoming_message_id => incoming_message_id - }) - - flash[:notice] = "Message has been moved to request(s). Showing the last one:" - end - # expire cached files - expire_for_request(previous_request) - incoming_message.fully_destroy - end - redirect_to request_admin_url(destination_request) - end - # change user or public body of a request magically def move_request info_request = InfoRequest.find(params[:info_request_id]) @@ -246,7 +148,7 @@ class AdminRequestController < AdminController info_request.reindex_request_events flash[:notice] = "Message has been moved to new user" end - redirect_to request_admin_url(info_request) + redirect_to admin_request_show_url(info_request) elsif params[:commit] == 'Move request to authority' && !params[:public_body_url_name].blank? old_public_body = info_request.public_body destination_public_body = PublicBody.find_by_url_name(params[:public_body_url_name]) @@ -265,10 +167,10 @@ class AdminRequestController < AdminController flash[:notice] = "Request has been moved to new body" end - redirect_to request_admin_url(info_request) + redirect_to admin_request_show_url(info_request) else flash[:error] = "Please enter the user or authority to move the request to" - redirect_to request_admin_url(info_request) + redirect_to admin_request_show_url(info_request) end end @@ -292,20 +194,20 @@ class AdminRequestController < AdminController if !info_request.public_body.is_foi_officer?(user) flash[:notice] = user.email + " is not an email at the domain @" + info_request.public_body.foi_officer_domain_required + ", so won't be able to upload." - redirect_to request_admin_url(info_request) + redirect_to admin_request_show_url(info_request) return end # Bejeeps, look, sometimes a URL is something that belongs in a controller, jesus. - # XXX hammer this square peg into the round MVC hole - should be calling main_url(upload_response_url()) + # XXX hammer this square peg into the round MVC hole post_redirect = PostRedirect.new( - :uri => main_url(upload_response_url(:url_title => info_request.url_title, :only_path => true)), + :uri => upload_response_url(:url_title => info_request.url_title), :user_id => user.id) post_redirect.save! - url = main_url(confirm_url(:email_token => post_redirect.email_token, :only_path => true)) + url = confirm_url(:email_token => post_redirect.email_token) - flash[:notice] = 'Send "' + name + '" <<a href="mailto:' + email + '">' + email + '</a>> this URL: <a href="' + url + '">' + url + "</a> - it will log them in and let them upload a response to this request.".html_safe - redirect_to request_admin_url(info_request) + flash[:notice] = ("Send \"#{name}\" <<a href=\"mailto:#{email}\">#{email}</a>> this URL: <a href=\"#{url}\">#{url}</a> - it will log them in and let them upload a response to this request.").html_safe + redirect_to admin_request_show_url(info_request) end def show_raw_email @@ -355,7 +257,7 @@ class AdminRequestController < AdminController info_request_event.save! flash[:notice] = "Old response marked as having been a clarification" - redirect_to request_admin_url(info_request_event.info_request) + redirect_to admin_request_show_url(info_request_event.info_request) end def hide_request @@ -376,18 +278,19 @@ class AdminRequestController < AdminController info_request.save! if ! info_request.is_external? - ContactMailer.deliver_from_admin_message( - info_request.user, + ContactMailer.from_admin_message( + info_request.user.name, + info_request.user.email, subject, - params[:explanation] - ) + params[:explanation].strip.html_safe + ).deliver flash[:notice] = _("Your message to {{recipient_user_name}} has been sent",:recipient_user_name=>CGI.escapeHTML(info_request.user.name)) else flash[:notice] = _("This external request has been hidden") end # expire cached files expire_for_request(info_request) - redirect_to request_admin_url(info_request) + redirect_to admin_request_show_url(info_request) end end diff --git a/app/controllers/admin_track_controller.rb b/app/controllers/admin_track_controller.rb index 03217da45..085c9c6cc 100644 --- a/app/controllers/admin_track_controller.rb +++ b/app/controllers/admin_track_controller.rb @@ -2,13 +2,18 @@ # Show email alerts / RSS feeds from admin interface. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class AdminTrackController < AdminController def list @query = params[:query] - @admin_tracks = TrackThing.paginate :order => "created_at desc", :page => params[:page], :per_page => 100, - :conditions => @query.nil? ? nil : ["lower(track_query) like lower('%'||?||'%')", @query ] + if @query + track_things = TrackThing.where(["lower(track_query) like lower('%'||?||'%')", @query]) + else + track_things = TrackThing + end + @admin_tracks = track_things.paginate :order => "created_at desc", :page => params[:page], :per_page => 100 + @popular = ActiveRecord::Base.connection.select_all("select count(*) as count, title, info_request_id from track_things join info_requests on info_request_id = info_requests.id where info_request_id is not null group by info_request_id, title order by count desc limit 10;") end private diff --git a/app/controllers/admin_user_controller.rb b/app/controllers/admin_user_controller.rb index ed20ddcf4..940a5fe8f 100644 --- a/app/controllers/admin_user_controller.rb +++ b/app/controllers/admin_user_controller.rb @@ -2,7 +2,7 @@ # Controller for viewing user accounts from the admin interface. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class AdminUserController < AdminController def index @@ -12,9 +12,13 @@ class AdminUserController < AdminController def list @query = params[:query] - @admin_users = User.paginate :order => "name", :page => params[:page], :per_page => 100, - :conditions => @query.nil? ? nil : ["lower(name) like lower('%'||?||'%') or - lower(email) like lower('%'||?||'%')", @query, @query] + if @query + users = User.where(["lower(name) like lower('%'||?||'%') or + lower(email) like lower('%'||?||'%')", @query, @query]) + else + users = User + end + @admin_users = users.paginate :order => "name", :page => params[:page], :per_page => 100 end def list_banned @@ -44,11 +48,12 @@ class AdminUserController < AdminController @admin_user.ban_text = params[:admin_user][:ban_text] @admin_user.about_me = params[:admin_user][:about_me] @admin_user.no_limit = params[:admin_user][:no_limit] + @admin_user.can_make_batch_requests = params[:admin_user][:can_make_batch_requests] if @admin_user.valid? @admin_user.save! flash[:notice] = 'User successfully updated.' - redirect_to user_admin_url(@admin_user) + redirect_to admin_user_show_url(@admin_user) else render :action => 'edit' end @@ -58,7 +63,7 @@ class AdminUserController < AdminController track_thing = TrackThing.find(params[:track_id].to_i) track_thing.destroy flash[:notice] = 'Track destroyed' - redirect_to user_admin_url(track_thing.tracking_user) + redirect_to admin_user_show_url(track_thing.tracking_user) end def clear_bounce @@ -66,15 +71,15 @@ class AdminUserController < AdminController user.email_bounced_at = nil user.email_bounce_message = "" user.save! - redirect_to user_admin_url(user) + redirect_to admin_user_show_url(user) end def login_as @admin_user = User.find(params[:id]) # check user does exist - post_redirect = PostRedirect.new( :uri => main_url(user_url(@admin_user)), :user_id => @admin_user.id, :circumstance => "login_as" ) + post_redirect = PostRedirect.new( :uri => user_url(@admin_user), :user_id => @admin_user.id, :circumstance => "login_as" ) post_redirect.save! - url = main_url(confirm_url(:email_token => post_redirect.email_token, :only_path => true)) + url = confirm_url(:email_token => post_redirect.email_token) redirect_to url end @@ -91,7 +96,7 @@ class AdminUserController < AdminController end flash[:notice] = "Profile photo cleared" - redirect_to user_admin_url(@admin_user) + redirect_to admin_user_show_url(@admin_user) end private diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 15fb4f5f9..e6b0c121a 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -16,11 +16,14 @@ class ApiController < ApplicationController :status => @request.calculate_status, :public_body_url => make_url("body", @request.public_body.url_name), - :requestor_url => make_url("user", @request.user.url_name), + :request_email => @request.incoming_email, :request_text => @request.last_event_forming_initial_request.outgoing_message.body, } + if @request.user + @request_data[:requestor_url] = make_url("user", @request.user.url_name) + end render :json => @request_data end @@ -63,6 +66,8 @@ class ApiController < ApplicationController :smtp_message_id => nil ) + request.set_described_state('waiting_response') + # Return the URL and ID number. render :json => { 'url' => make_url("request", request.url_title), @@ -83,7 +88,7 @@ class ApiController < ApplicationController direction = json["direction"] body = json["body"] - sent_at_str = json["sent_at"] + sent_at = json["sent_at"] errors = [] @@ -107,12 +112,6 @@ class ApiController < ApplicationController errors << "The 'body' is empty" end - begin - sent_at = Time.iso8601(sent_at_str) - rescue ArgumentError - errors << "Failed to parse 'sent_at' field as ISO8601 time: #{sent_at_str}" - end - if direction == "request" && !attachments.nil? errors << "You cannot attach files to messages in the 'request' direction" end @@ -155,7 +154,8 @@ class ApiController < ApplicationController ) end - mail = RequestMailer.create_external_response(request, body, sent_at, attachment_hashes) + mail = RequestMailer.external_response(request, body, sent_at, attachment_hashes) + request.receive(mail, mail.encoded, true) end render :json => { @@ -203,7 +203,7 @@ class ApiController < ApplicationController ]) end if feed_type == "atom" - render :template => "api/request_events.atom", :layout => false + render :template => "api/request_events", :formats => ['atom'], :layout => false elsif feed_type == "json" # For the JSON feed, we take a "since" parameter that allows the client # to restrict to events more recent than a certain other event @@ -220,7 +220,7 @@ class ApiController < ApplicationController :event_id => event.id, :created_at => event.created_at.iso8601, :event_type => event.event_type, - :request_url => main_url(request_url(request)), + :request_url => request_url(request), :request_email => request.incoming_email, :title => request.title, :body => event.outgoing_message.body, @@ -228,7 +228,7 @@ class ApiController < ApplicationController :user_name => request.user_name, } if request.user - this_event[:user_url] = main_url(user_url(request.user)) + this_event[:user_url] = user_url(request.user) end @event_data.push(this_event) @@ -248,6 +248,6 @@ class ApiController < ApplicationController private def make_url(*args) - "http://" + Configuration::domain + "/" + args.join("/") + "http://" + AlaveteliConfiguration::domain + "/" + args.join("/") end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ed1523f75..370e8e15c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,30 +5,28 @@ # will be available for all controllers. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'open-uri' class ApplicationController < ActionController::Base class PermissionDenied < StandardError end + class RouteNotFound < StandardError + end + # assign our own handler method for non-local exceptions + rescue_from Exception, :with => :render_exception + # Standard headers, footers and navigation for whole site layout "default" include FastGettext::Translation # make functions like _, n_, N_ etc available) - # Send notification email on exceptions - include ExceptionNotification::Notifiable - # Note: a filter stops the chain if it redirects or renders something before_filter :authentication_check before_filter :set_gettext_locale before_filter :check_in_post_redirect before_filter :session_remember_me before_filter :set_vary_header - before_filter :set_popup_banner - - # scrub sensitive parameters from the logs - filter_parameter_logging :password def set_vary_header response.headers['Vary'] = 'Cookie' @@ -53,19 +51,22 @@ class ApplicationController < ActionController::Base anonymous_cache(24.hours) end + # This is an override of the method provided by gettext_i18n_rails - note the explicit + # setting of I18n.locale, required due to the I18nProxy used in Rails 3 to trigger the + # lookup_context and expire the template cache def set_gettext_locale - if Configuration::include_default_locale_in_urls == false + if AlaveteliConfiguration::include_default_locale_in_urls == false params_locale = params[:locale] ? params[:locale] : I18n.default_locale else params_locale = params[:locale] end - if Configuration::use_default_browser_language + if AlaveteliConfiguration::use_default_browser_language requested_locale = params_locale || session[:locale] || cookies[:locale] || request.env['HTTP_ACCEPT_LANGUAGE'] || I18n.default_locale else requested_locale = params_locale || session[:locale] || cookies[:locale] || I18n.default_locale end requested_locale = FastGettext.best_locale_in(requested_locale) - session[:locale] = FastGettext.set_locale(requested_locale) + session[:locale] = I18n.locale = FastGettext.set_locale(requested_locale) if !@user.nil? if @user.locale != requested_locale @user.locale = session[:locale] @@ -74,9 +75,6 @@ class ApplicationController < ActionController::Base end end - # scrub sensitive parameters from the logs - filter_parameter_logging :password - helper_method :locale_from_params # Help work out which request causes RAM spike. @@ -92,7 +90,7 @@ class ApplicationController < ActionController::Base # egrep "CONSUME MEMORY: [0-9]{7} KB" production.log around_filter :record_memory def record_memory - record_memory = Configuration::debug_record_memory + record_memory = AlaveteliConfiguration::debug_record_memory if record_memory logger.info "Processing request for #{request.url} with Rails process #{Process.pid}" File.read("/proc/#{Process.pid}/status").match(/VmRSS:\s+(\d+)/) @@ -120,61 +118,45 @@ class ApplicationController < ActionController::Base end end - # Override default error handler, for production sites. - def rescue_action_in_public(exception) - # Looks for before_filters called something like `set_view_paths_{themename}`. These - # are set by the themes. - # Normally, this is called by the theme itself in a - # :before_filter, but when there's an error, this doesn't - # happen. By calling it here, we can ensure error pages are - # still styled according to the theme. - ActionController::Base.before_filters.select{|f| f.to_s =~ /set_view_paths/}.each do |f| - self.send(f) + def render_exception(exception) + # In development or the admin interface let Rails handle the exception + # with its stack trace templates + if Rails.application.config.consider_all_requests_local || show_rails_exceptions? + raise exception end - # Make sure expiry time for session is set (before_filters are - # otherwise missed by this override) - session_remember_me - - # Make sure the locale is set correctly too - set_gettext_locale + @exception_backtrace = exception.backtrace.join("\n") + @exception_class = exception.class.to_s + @exception_message = exception.message case exception - when ActiveRecord::RecordNotFound, ActionController::UnknownAction, ActionController::RoutingError + when ActiveRecord::RecordNotFound, RouteNotFound @status = 404 when PermissionDenied @status = 403 else + message = "\n#{@exception_class} (#{@exception_message}):\n" + backtrace = Rails.backtrace_cleaner.clean(exception.backtrace, :silent) + message << " " << backtrace.join("\n ") + Rails.logger.fatal("#{message}\n\n") + if !AlaveteliConfiguration.exception_notifications_from.blank? && !AlaveteliConfiguration.exception_notifications_to.blank? + ExceptionNotifier::Notifier.exception_notification(request.env, exception).deliver + end @status = 500 - notify_about_exception exception end - # Display user appropriate error message - @exception_backtrace = exception.backtrace.join("\n") - @exception_class = exception.class.to_s - @exception_message = exception.message - render :template => "general/exception_caught.rhtml", :status => @status - end - - # For development sites. - alias original_rescue_action_locally rescue_action_locally - def rescue_action_locally(exception) - # Make sure expiry time for session is set (before_filters are - # otherwise missed by this override) - session_remember_me - - # Make sure the locale is set correctly too - set_gettext_locale - - # Display default, detailed error for developers - original_rescue_action_locally(exception) + respond_to do |format| + format.html{ render :template => "general/exception_caught", :status => @status } + format.any{ render :nothing => true, :status => @status } + end end - def local_request? + def show_rails_exceptions? false end # Called from test code, is a mimic of UserController.confirm, for use in following email # links when in controller tests (though we also have full integration tests that # can work over multiple controllers) + # TODO: Move this to the tests. It shouldn't be here def test_code_redirect_by_email_token(token, controller_example_group) post_redirect = PostRedirect.find_by_email_token(token) if post_redirect.nil? @@ -182,7 +164,7 @@ class ApplicationController < ActionController::Base end session[:user_id] = post_redirect.user.id session[:user_circumstance] = post_redirect.circumstance - params = controller_example_group.params_from(:get, post_redirect.local_part_uri) + params = Rails.application.routes.recognize_path(post_redirect.local_part_uri) params.merge(post_redirect.post_params) controller_example_group.get params[:action], params end @@ -231,19 +213,6 @@ class ApplicationController < ActionController::Base end end - def request_dirs(info_request) - first_three_digits = info_request.id.to_s()[0..2] - File.join(first_three_digits.to_s, info_request.id.to_s) - end - - def request_download_zip_dir(info_request) - File.join(download_zip_dir, "download", request_dirs(info_request)) - end - - def download_zip_dir() - File.join(Rails.root, '/cache/zips/') - end - # get the local locale def locale_from_params(*args) if params[:show_locale] @@ -258,7 +227,7 @@ class ApplicationController < ActionController::Base # Check the user is logged in def authenticated?(reason_params) unless session[:user_id] - post_redirect = PostRedirect.new(:uri => request.request_uri, :post_params => params, + post_redirect = PostRedirect.new(:uri => request.fullpath, :post_params => params, :reason_params => reason_params) post_redirect.save! # 'modal' controls whether the sign-in form will be displayed in the typical full-blown @@ -346,10 +315,10 @@ class ApplicationController < ActionController::Base # def check_read_only - if !Configuration::read_only.empty? + if !AlaveteliConfiguration::read_only.empty? flash[:notice] = _("<p>{{site_name}} is currently in maintenance. You can only view existing requests. You cannot make new ones, add followups or annotations, or otherwise change the database.</p> <p>{{read_only}}</p>", :site_name => site_name, - :read_only => Configuration::read_only) + :read_only => AlaveteliConfiguration::read_only) redirect_to frontpage_url end @@ -380,12 +349,15 @@ class ApplicationController < ActionController::Base # Peform the search @per_page = per_page - if this_page.nil? - @page = get_search_page_from_params - else - @page = this_page - end - result = InfoRequest.full_search(models, @query, order, ascending, collapse, @per_page, @page) + @page = this_page || get_search_page_from_params + + result = ActsAsXapian::Search.new(models, @query, + :offset => (@page - 1) * @per_page, + :limit => @per_page, + :sort_by_prefix => order, + :sort_by_ascending => ascending, + :collapse_by_prefix => collapse + ) result.results # Touch the results to load them, otherwise accessing them from the view # might fail later if the database has subsequently been reopened. return result @@ -397,9 +369,9 @@ class ApplicationController < ActionController::Base return page end - def perform_search_typeahead(query, model) + def perform_search_typeahead(query, model, per_page=25) @page = get_search_page_from_params - @per_page = 10 + @per_page = per_page query_words = query.split(/ +(?![-+]+)/) if query_words.last.nil? || query_words.last.strip.length < 3 xapian_requests = nil @@ -456,116 +428,19 @@ class ApplicationController < ActionController::Base end end - def param_exists(item) - return params[item] && !params[item].empty? - end - - def get_request_variety_from_params - query = "" - sortby = "newest" - varieties = [] - if params[:request_variety] && !(query =~ /variety:/) - if params[:request_variety].include? "sent" - varieties -= ['variety:sent', 'variety:followup_sent', 'variety:response', 'variety:comment'] - varieties << ['variety:sent', 'variety:followup_sent'] - end - if params[:request_variety].include? "response" - varieties << ['variety:response'] - end - if params[:request_variety].include? "comment" - varieties << ['variety:comment'] - end - end - if !varieties.empty? - query = " (#{varieties.join(' OR ')})" - end - return query - end - - def get_status_from_params - query = "" - if params[:latest_status] - statuses = [] - if params[:latest_status].class == String - params[:latest_status] = [params[:latest_status]] - end - if params[:latest_status].include?("recent") || params[:latest_status].include?("all") - query += " (variety:sent OR variety:followup_sent OR variety:response OR variety:comment)" - end - if params[:latest_status].include? "successful" - statuses << ['latest_status:successful', 'latest_status:partially_successful'] - end - if params[:latest_status].include? "unsuccessful" - statuses << ['latest_status:rejected', 'latest_status:not_held'] - end - if params[:latest_status].include? "awaiting" - statuses << ['latest_status:waiting_response', 'latest_status:waiting_clarification', 'waiting_classification:true', 'latest_status:internal_review','latest_status:gone_postal', 'latest_status:error_message', 'latest_status:requires_admin'] - end - if params[:latest_status].include? "internal_review" - statuses << ['status:internal_review'] - end - if params[:latest_status].include? "other" - statuses << ['latest_status:gone_postal', 'latest_status:error_message', 'latest_status:requires_admin', 'latest_status:user_withdrawn'] - end - if params[:latest_status].include? "gone_postal" - statuses << ['latest_status:gone_postal'] - end - if !statuses.empty? - query = " (#{statuses.join(' OR ')})" - end - end - return query - end - - def get_date_range_from_params - query = "" - if param_exists(:request_date_after) && !param_exists(:request_date_before) - params[:request_date_before] = Time.now.strftime("%d/%m/%Y") - query += " #{params[:request_date_after]}..#{params[:request_date_before]}" - elsif !param_exists(:request_date_after) && param_exists(:request_date_before) - params[:request_date_after] = "01/01/2001" - end - if param_exists(:request_date_after) - query = " #{params[:request_date_after]}..#{params[:request_date_before]}" - end - return query - end - - def get_tags_from_params - query = "" - tags = [] - if param_exists(:tags) - params[:tags].split().each do |tag| - tags << "tag:#{tag}" - end - end - if !tags.empty? - query = " (#{tags.join(' OR ')})" - end - return query - end - - def make_query_from_params - query = params[:query] || "" if query.nil? - query += get_date_range_from_params - query += get_request_variety_from_params - query += get_status_from_params - query += get_tags_from_params - return query - end - def country_from_ip country = "" - if !Configuration::gaze_url.empty? - country = quietly_try_to_open("#{Configuration::gaze_url}/gaze-rest?f=get_country_from_ip;ip=#{request.remote_ip}") + if !AlaveteliConfiguration::gaze_url.empty? + country = quietly_try_to_open("#{AlaveteliConfiguration::gaze_url}/gaze-rest?f=get_country_from_ip;ip=#{request.remote_ip}") end - country = Configuration::iso_country_code if country.empty? + country = AlaveteliConfiguration::iso_country_code if country.empty? return country end - def set_popup_banner - @popup_banner = render_to_string(:partial => "general/popup_banner").strip.html_safe + def alaveteli_git_commit + `git log -1 --format="%H"`.strip end + # URL generating functions are needed by all controllers (for redirects), # views (for links) and mailers (for use in emails), so include them into # all of all. diff --git a/app/controllers/comment_controller.rb b/app/controllers/comment_controller.rb index ed249d6cc..cda56a211 100644 --- a/app/controllers/comment_controller.rb +++ b/app/controllers/comment_controller.rb @@ -2,7 +2,7 @@ # Show annotations upon a request or other object. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class CommentController < ApplicationController before_filter :check_read_only, :only => [ :new ] @@ -38,7 +38,7 @@ class CommentController < ApplicationController if params[:comment] # XXX this check should theoretically be a validation rule in the model - @existing_comment = Comment.find_by_existing_comment(@info_request.id, params[:comment][:body]) + @existing_comment = Comment.find_existing(@info_request.id, params[:comment][:body]) else # Default to subscribing to request when first viewing form params[:subscribe_to_request] = true @@ -68,7 +68,7 @@ class CommentController < ApplicationController if params[:subscribe_to_request] @track_thing = TrackThing.create_track_for_request(@info_request) - @existing_track = TrackThing.find_by_existing_track(@user, @track_thing) + @existing_track = TrackThing.find_existing(@user, @track_thing) if @user && @info_request.user == @user # don't subscribe to own request! elsif !@existing_track diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb index 875e39494..6f0d29889 100644 --- a/app/controllers/general_controller.rb +++ b/app/controllers/general_controller.rb @@ -3,14 +3,7 @@ # particular model. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -begin - require 'xmlsimple' -rescue LoadError - # Debian maintainers put their xmlsimple in a different location :( - require 'lib/xmlsimple' -end +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'open-uri' @@ -19,59 +12,18 @@ class GeneralController < ApplicationController # New, improved front page! def frontpage medium_cache - # get some example searches and public bodies to display - # either from config, or based on a (slow!) query if not set - body_short_names = Configuration::frontpage_publicbody_examples.split(/\s*;\s*/).map{|s| "'%s'" % s.gsub(/'/, "''") }.join(", ") @locale = self.locale_from_params() - locale_condition = 'public_body_translations.locale = ?' - conditions = [locale_condition, @locale] - PublicBody.with_locale(@locale) do - if body_short_names.empty? - # This is too slow - @popular_bodies = PublicBody.visible.find(:all, - :order => "info_requests_count desc", - :limit => 32, - :conditions => conditions, - :joins => :translations - ) - else - conditions[0] += " and public_bodies.url_name in (" + body_short_names + ")" - @popular_bodies = PublicBody.find(:all, - :conditions => conditions, - :joins => :translations) - end - end - # Get some successful requests - begin - query = 'variety:response (status:successful OR status:partially_successful)' - sortby = "newest" - max_count = 5 - xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count) - @request_events = xapian_object.results.map { |r| r[:model] } - - # If there are not yet enough successful requests, fill out the list with - # other requests - if @request_events.count < max_count - @request_events_all_successful = false - query = 'variety:sent' - xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count-@request_events.count) - more_events = xapian_object.results.map { |r| r[:model] } - @request_events += more_events - # Overall we still want the list sorted with the newest first - @request_events.sort!{|e1,e2| e2.created_at <=> e1.created_at} - else - @request_events_all_successful = true - end - rescue - @request_events = [] - end end # Display blog entries def blog + if AlaveteliConfiguration::blog_feed.empty? + raise ActiveRecord::RecordNotFound.new("Page not enabled") + end + medium_cache @feed_autodetect = [] - @feed_url = Configuration::blog_feed + @feed_url = AlaveteliConfiguration::blog_feed separator = @feed_url.include?('?') ? '&' : '?' @feed_url = "#{@feed_url}#{separator}lang=#{self.locale_from_params()}" @blog_items = [] @@ -84,7 +36,7 @@ class GeneralController < ApplicationController @feed_autodetect = [{:url => @feed_url, :title => "#{site_name} blog"}] end end - @twitter_user = Configuration::twitter_username + @twitter_user = AlaveteliConfiguration::twitter_username end # Just does a redirect from ?query= search to /query @@ -109,7 +61,7 @@ class GeneralController < ApplicationController def search # XXX Why is this so complicated with arrays and stuff? Look at the route # in config/routes.rb for comments. - combined = params[:combined] + combined = params[:combined].split("/") @sortby = nil @bodies = @requests = @users = true if combined.size > 0 && (['advanced'].include?(combined[-1])) @@ -151,16 +103,15 @@ class GeneralController < ApplicationController params[:query] = @query end if @variety_postfix != "all" && @requests - @query, _ = make_query_from_params + @query = InfoRequestEvent.make_query_from_params(params) end @inputted_sortby = @sortby - @common_query = get_tags_from_params if @sortby.nil? # Parse query, so can work out if it has prefix terms only - if so then it is a # structured query which should show newest first, rather than a free text search # where we want most relevant as default. begin - dummy_query = ::ActsAsXapian::Search.new([InfoRequestEvent], @query, :limit => 1) + dummy_query = ActsAsXapian::Search.new([InfoRequestEvent], @query, :limit => 1) rescue => e flash[:error] = "Your query was not quite right. " + CGI.escapeHTML(e.to_str) redirect_to search_url("") @@ -176,10 +127,8 @@ class GeneralController < ApplicationController # Query each type separately for separate display (XXX we are calling # perform_search multiple times and it clobbers per_page for each one, # so set as separate var) - requests_per_page = 25 - if params[:requests_per_page] - requests_per_page = params[:requests_per_page].to_i - end + requests_per_page = params[:requests_per_page] ? params[:requests_per_page].to_i : 25 + @this_page_hits = @total_hits = @xapian_requests_hits = @xapian_bodies_hits = @xapian_users_hits = 0 if @requests @xapian_requests = perform_search([InfoRequestEvent], @query, @sortby, 'request_collapse', requests_per_page) @@ -188,6 +137,7 @@ class GeneralController < ApplicationController @xapian_requests_hits = @xapian_requests.results.size @xapian_requests_total_hits = @xapian_requests.matches_estimated @total_hits += @xapian_requests.matches_estimated + @request_for_spelling = @xapian_requests end if @bodies @xapian_bodies = perform_search([PublicBody], @query, @sortby, nil, 5) @@ -196,6 +146,7 @@ class GeneralController < ApplicationController @xapian_bodies_hits = @xapian_bodies.results.size @xapian_bodies_total_hits = @xapian_bodies.matches_estimated @total_hits += @xapian_bodies.matches_estimated + @request_for_spelling = @xapian_bodies end if @users @xapian_users = perform_search([User], @query, @sortby, nil, 5) @@ -204,32 +155,32 @@ class GeneralController < ApplicationController @xapian_users_hits = @xapian_users.results.size @xapian_users_total_hits = @xapian_users.matches_estimated @total_hits += @xapian_users.matches_estimated + @request_for_spelling = @xapian_users end # Spelling and highight words are same for all three queries - if !@xapian_requests.nil? - @highlight_words = @xapian_requests.words_to_highlight - if !(@xapian_requests.spelling_correction =~ /[a-z]+:/) - @spelling_correction = @xapian_requests.spelling_correction - end + @highlight_words = @request_for_spelling.words_to_highlight + if !(@request_for_spelling.spelling_correction =~ /[a-z]+:/) + @spelling_correction = @request_for_spelling.spelling_correction end @track_thing = TrackThing.create_track_for_search_query(@query, @variety_postfix) @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] end - # Jump to a random request - def random_request - info_request = InfoRequest.random - redirect_to request_url(info_request) + # Handle requests for non-existent URLs - will be handled by ApplicationController::render_exception + def not_found + raise RouteNotFound end - def custom_css - long_cache - @locale = self.locale_from_params() - render(:layout => false, :content_type => 'text/css') + def version + respond_to do |format| + format.json { render :json => { + :alaveteli_git_commit => alaveteli_git_commit, + :alaveteli_version => ALAVETELI_VERSION, + :ruby_version => RUBY_VERSION + }} + end end - - end diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index 573abac63..9959df6d8 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -2,7 +2,7 @@ # Show information about one particular request. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class HelpController < ApplicationController @@ -18,7 +18,7 @@ class HelpController < ApplicationController end def contact - @contact_email = Configuration::contact_email + @contact_email = AlaveteliConfiguration::contact_email # if they clicked remove for link to request/body, remove it if params[:remove] @@ -49,14 +49,14 @@ class HelpController < ApplicationController end @contact = ContactValidator.new(params[:contact]) if @contact.valid? && !params[:remove] - ContactMailer.deliver_to_admin_message( + ContactMailer.to_admin_message( params[:contact][:name], params[:contact][:email], params[:contact][:subject], params[:contact][:message], @user, @last_request, @last_body - ) + ).deliver flash[:notice] = _("Your message has been sent. Thank you for getting in touch! We'll get back to you soon.") redirect_to frontpage_url return diff --git a/app/controllers/holiday_controller.rb b/app/controllers/holiday_controller.rb index 3101c07e3..efc20701d 100644 --- a/app/controllers/holiday_controller.rb +++ b/app/controllers/holiday_controller.rb @@ -2,7 +2,7 @@ # Calculate dates # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class HolidayController < ApplicationController @@ -12,7 +12,7 @@ class HolidayController < ApplicationController def due_date if params[:holiday] @request_date = Date.strptime(params[:holiday]) or raise "Invalid date" - @due_date = Holiday.due_date_from(@request_date, Configuration::reply_late_after_days, Configuration::working_or_calendar_days) + @due_date = Holiday.due_date_from(@request_date, AlaveteliConfiguration::reply_late_after_days, AlaveteliConfiguration::working_or_calendar_days) @skipped = Holiday.all( :conditions => [ 'day >= ? AND day <= ?', @request_date.strftime("%F"), @due_date.strftime("%F") diff --git a/app/controllers/info_request_batch_controller.rb b/app/controllers/info_request_batch_controller.rb new file mode 100644 index 000000000..b66658757 --- /dev/null +++ b/app/controllers/info_request_batch_controller.rb @@ -0,0 +1,16 @@ +class InfoRequestBatchController < ApplicationController + + def show + @info_request_batch = InfoRequestBatch.find(params[:id]) + @per_page = 25 + @page = get_search_page_from_params + if @info_request_batch.sent_at + @info_requests = @info_request_batch.info_requests.visible.all(:offset => (@page - 1) * @per_page, + :limit => @per_page) + else + @public_bodies = @info_request_batch.public_bodies.all(:offset => (@page - 1) * @per_page, + :limit => @per_page) + end + end + +end diff --git a/app/controllers/public_body_change_requests_controller.rb b/app/controllers/public_body_change_requests_controller.rb new file mode 100644 index 000000000..4a6c5f5cb --- /dev/null +++ b/app/controllers/public_body_change_requests_controller.rb @@ -0,0 +1,28 @@ +class PublicBodyChangeRequestsController < ApplicationController + + def create + @change_request = PublicBodyChangeRequest.from_params(params[:public_body_change_request], @user) + if @change_request.save + @change_request.send_message + flash[:notice] = @change_request.thanks_notice + redirect_to frontpage_url + return + else + render :action => 'new' + end + end + + def new + @change_request = PublicBodyChangeRequest.new + if params[:body] + @change_request.public_body = PublicBody.find_by_url_name_with_historic(params[:body]) + end + if @change_request.public_body + @title = _('Ask us to update the email address for {{public_body_name}}', + :public_body_name => @change_request.public_body.name) + else + @title = _('Ask us to add an authority') + end + + end +end diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb index 8a4a65820..862f4b318 100644 --- a/app/controllers/public_body_controller.rb +++ b/app/controllers/public_body_controller.rb @@ -3,9 +3,11 @@ # Show information about a public body. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'fastercsv' +require 'confidence_intervals' +require 'tempfile' class PublicBodyController < ApplicationController # XXX tidy this up with better error messages, and a more standard infrastructure for the redirect to canonical URL @@ -16,7 +18,7 @@ class PublicBodyController < ApplicationController return end @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do @public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) raise ActiveRecord::RecordNotFound.new("None found") if @public_body.nil? if @public_body.url_name.nil? @@ -25,22 +27,20 @@ class PublicBodyController < ApplicationController end # If found by historic name, or alternate locale name, redirect to new name if @public_body.url_name != params[:url_name] - redirect_to show_public_body_url(:url_name => @public_body.url_name) + redirect_to :url_name => @public_body.url_name return end set_last_body(@public_body) - top_url = main_url("/") + top_url = frontpage_url @searched_to_send_request = false referrer = request.env['HTTP_REFERER'] if !referrer.nil? && referrer.match(%r{^#{top_url}search/.*/bodies$}) @searched_to_send_request = true end @view = params[:view] - params[:latest_status] = @view - - query = make_query_from_params + query = InfoRequestEvent.make_query_from_params(params.merge(:latest_status => @view)) query += " requested_from:#{@public_body.url_name}" # Use search query for this so can collapse and paginate easily # XXX really should just use SQL query here rather than Xapian. @@ -71,7 +71,7 @@ class PublicBodyController < ApplicationController @public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) raise ActiveRecord::RecordNotFound.new("None found") if @public_body.nil? - PublicBody.with_locale(self.locale_from_params()) do + I18n.with_locale(self.locale_from_params()) do if params[:submitted_view_email] if verify_recaptcha flash.discard(:error) @@ -87,34 +87,45 @@ class PublicBodyController < ApplicationController def list long_cache # XXX move some of these tag SQL queries into has_tag_string.rb - @query = "%#{params[:public_body_query].nil? ? "" : params[:public_body_query]}%" + + like_query = params[:public_body_query] + like_query = "" if like_query.nil? + like_query = "%#{like_query}%" + @tag = params[:tag] - @locale = self.locale_from_params() - default_locale = I18n.default_locale.to_s - locale_condition = "(upper(public_body_translations.name) LIKE upper(?) - OR upper(public_body_translations.notes) LIKE upper (?)) - AND public_body_translations.locale = ? - AND public_bodies.id <> #{PublicBody.internal_admin_body.id}" + + @locale = self.locale_from_params + underscore_locale = @locale.gsub '-', '_' + underscore_default_locale = I18n.default_locale.to_s.gsub '-', '_' + + where_condition = "public_bodies.id <> #{PublicBody.internal_admin_body.id}" + where_parameters = [] + + first_letter = false + + base_tag_condition = " AND (SELECT count(*) FROM has_tag_string_tags" \ + " WHERE has_tag_string_tags.model_id = public_bodies.id" \ + " AND has_tag_string_tags.model = 'PublicBody'" + + # Restrict the public bodies shown according to the tag + # parameter supplied in the URL: if @tag.nil? or @tag == "all" @tag = "all" - conditions = [locale_condition, @query, @query, default_locale] elsif @tag == 'other' category_list = PublicBodyCategories::get().tags().map{|c| "'"+c+"'"}.join(",") - conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id - and has_tag_string_tags.model = \'PublicBody\' - and has_tag_string_tags.name in (' + category_list + ')) = 0', @query, @query, default_locale] - elsif @tag.size == 1 - @tag.upcase! - conditions = [locale_condition + ' AND public_body_translations.first_letter = ?', @query, @query, default_locale, @tag] + where_condition += base_tag_condition + " AND has_tag_string_tags.name in (#{category_list})) = 0" + elsif @tag.scan(/./mu).size == 1 + @tag = Unicode.upcase @tag + # The first letter queries have to be done on + # translations, so just indicate to add that later: + first_letter = true elsif @tag.include?(":") name, value = HasTagString::HasTagStringTag.split_tag_into_name_value(@tag) - conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id - and has_tag_string_tags.model = \'PublicBody\' - and has_tag_string_tags.name = ? and has_tag_string_tags.value = ?) > 0', @query, @query, default_locale, name, value] + where_condition += base_tag_condition + " AND has_tag_string_tags.name = ? AND has_tag_string_tags.value = ?) > 0" + where_parameters.concat [name, value] else - conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id - and has_tag_string_tags.model = \'PublicBody\' - and has_tag_string_tags.name = ?) > 0', @query, @query, default_locale, @tag] + where_condition += base_tag_condition + " AND has_tag_string_tags.name = ?) > 0" + where_parameters.concat [@tag] end if @tag == "all" @@ -129,13 +140,48 @@ class PublicBodyController < ApplicationController @description = _("in the category ‘{{category_name}}’", :category_name=>category_name) end end - PublicBody.with_locale(@locale) do - @public_bodies = PublicBody.paginate( - :order => "public_body_translations.name", :page => params[:page], :per_page => 100, - :conditions => conditions, - :joins => :translations - ) - render :template => "public_body/list" + + I18n.with_locale(@locale) do + + if AlaveteliConfiguration::public_body_list_fallback_to_default_locale + # Unfortunately, when we might fall back to the + # default locale, this is a rather complex query: + query = %Q{ + SELECT public_bodies.*, COALESCE(current_locale.name, default_locale.name) AS display_name + FROM public_bodies + LEFT OUTER JOIN public_body_translations as current_locale + ON (public_bodies.id = current_locale.public_body_id + AND current_locale.locale = ? AND #{get_public_body_list_translated_condition 'current_locale', first_letter}) + LEFT OUTER JOIN public_body_translations as default_locale + ON (public_bodies.id = default_locale.public_body_id + AND default_locale.locale = ? AND #{get_public_body_list_translated_condition 'default_locale', first_letter}) + WHERE #{where_condition} AND COALESCE(current_locale.name, default_locale.name) IS NOT NULL + ORDER BY display_name} + sql = [query, underscore_locale, like_query, like_query] + sql.push @tag if first_letter + sql += [underscore_default_locale, like_query, like_query] + sql.push @tag if first_letter + sql += where_parameters + @public_bodies = PublicBody.paginate_by_sql( + sql, + :page => params[:page], + :per_page => 100) + else + # The simpler case where we're just searching in the current locale: + where_condition = get_public_body_list_translated_condition('public_body_translations', first_letter, true) + + ' AND ' + where_condition + where_sql = [where_condition, like_query, like_query] + where_sql.push @tag if first_letter + where_sql += [underscore_locale] + where_parameters + @public_bodies = PublicBody.where(where_sql) \ + .joins(:translations) \ + .order("public_body_translations.name") \ + .paginate(:page => params[:page], :per_page => 100) + end + + respond_to do |format| + format.html { render :template => "public_body/list" } + end end end @@ -146,9 +192,146 @@ class PublicBodyController < ApplicationController end def list_all_csv - send_data(PublicBody.export_csv, :type=> 'text/csv; charset=utf-8; header=present', + # FIXME: this is just using the download directory for zip + # archives, since we know that is allowed for X-Sendfile and + # the filename can't clash with the numeric subdirectory names + # used for the zips. However, really there should be a + # generically named downloads directory that contains all + # kinds of downloadable assets. + download_directory = File.join(InfoRequest.download_zip_dir(), + 'download') + FileUtils.mkdir_p download_directory + output_leafname = 'all-authorities.csv' + output_filename = File.join download_directory, output_leafname + # Create a temporary file in the same directory, so we can + # rename it atomically to the intended filename: + tmp = Tempfile.new output_leafname, download_directory + tmp.close + # Export all the public bodies to that temporary path and make + # it readable: + PublicBody.export_csv tmp.path + FileUtils.chmod 0644, tmp.path + # Rename into place and send the file: + File.rename tmp.path, output_filename + send_file(output_filename, + :type => 'text/csv; charset=utf-8; header=present', :filename => 'all-authorities.csv', - :disposition =>'attachment', :encoding => 'utf8') + :disposition =>'attachment', + :encoding => 'utf8') + end + + + # This is a helper method to take data returned by the PublicBody + # model's statistics-generating methods, and converting them to + # simpler data structure that can be rendered by a Javascript + # graph library. (This could be a class method except that we need + # access to the URL helper public_body_path.) + def simplify_stats_for_graphs(data, + column, + percentages, + graph_properties) + # Copy the data, only taking known-to-be-safe keys: + result = Hash.new { |h, k| h[k] = [] } + result.update Hash[data.select do |key, value| + ['y_values', + 'y_max', + 'totals', + 'cis_below', + 'cis_above'].include? key + end] + + # Extract data about the public bodies for the x-axis, + # tooltips, and so on: + data['public_bodies'].each_with_index do |pb, i| + result['x_values'] << i + result['x_ticks'] << [i, pb.name] + result['tooltips'] << "#{pb.name} (#{result['totals'][i]})" + result['public_bodies'] << { + 'name' => pb.name, + 'url' => public_body_path(pb) + } + end + + # Set graph metadata properties, like the title, axis labels, etc. + graph_id = "#{column}-" + graph_id += graph_properties[:highest] ? 'highest' : 'lowest' + result.update({ + 'id' => graph_id, + 'x_axis' => _('Public Bodies'), + 'y_axis' => graph_properties[:y_axis], + 'errorbars' => percentages, + 'title' => graph_properties[:title] + }) + end + + def statistics + unless AlaveteliConfiguration::public_body_statistics_page + raise ActiveRecord::RecordNotFound.new("Page not enabled") + end + + per_graph = 10 + minimum_requests = AlaveteliConfiguration::minimum_requests_for_statistics + # Make sure minimum_requests is > 0 to avoid division-by-zero + minimum_requests = [minimum_requests, 1].max + total_column = 'info_requests_count' + + @graph_list = [] + + [[total_column, + [{ + :title => _('Public bodies with the most requests'), + :y_axis => _('Number of requests'), + :highest => true}]], + ['info_requests_successful_count', + [{ + :title => _('Public bodies with the most successful requests'), + :y_axis => _('Percentage of total requests'), + :highest => true}, + { + :title => _('Public bodies with the fewest successful requests'), + :y_axis => _('Percentage of total requests'), + :highest => false}]], + ['info_requests_overdue_count', + [{ + :title => _('Public bodies with most overdue requests'), + :y_axis => _('Percentage of requests that are overdue'), + :highest => true}]], + ['info_requests_not_held_count', + [{ + :title => _('Public bodies that most frequently replied with "Not Held"'), + :y_axis => _('Percentage of total requests'), + :highest => true}]]].each do |column, graphs_properties| + + graphs_properties.each do |graph_properties| + + percentages = (column != total_column) + highest = graph_properties[:highest] + + data = nil + if percentages + data = PublicBody.get_request_percentages(column, + per_graph, + highest, + minimum_requests) + else + data = PublicBody.get_request_totals(per_graph, + highest, + minimum_requests) + end + + if data + @graph_list.push simplify_stats_for_graphs(data, + column, + percentages, + graph_properties) + end + end + end + + respond_to do |format| + format.html { render :template => "public_body/statistics" } + format.json { render :json => @graph_list } + end end # Type ahead search @@ -159,5 +342,18 @@ class PublicBodyController < ApplicationController @xapian_requests = perform_search_typeahead(query, PublicBody) render :partial => "public_body/search_ahead" end -end + private + def get_public_body_list_translated_condition(table, first_letter=false, locale=nil) + result = "(upper(#{table}.name) LIKE upper(?)" \ + " OR upper(#{table}.notes) LIKE upper (?))" + if first_letter + result += " AND #{table}.first_letter = ?" + end + if locale + result += " AND #{table}.locale = ?" + end + result + end + +end diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb new file mode 100644 index 000000000..a1dd53125 --- /dev/null +++ b/app/controllers/reports_controller.rb @@ -0,0 +1,31 @@ +class ReportsController < ApplicationController + def create + @info_request = InfoRequest.find_by_url_title!(params[:request_id]) + @reason = params[:reason] + @message = params[:message] + if @reason.empty? + flash[:error] = _("Please choose a reason") + render "new" + return + end + + if !authenticated_user + flash[:notice] = _("You need to be logged in to report a request for administrator attention") + elsif @info_request.attention_requested + flash[:notice] = _("This request has already been reported for administrator attention") + else + @info_request.report!(@reason, @message, @user) + flash[:notice] = _("This request has been reported for administrator attention") + end + redirect_to request_url(@info_request) + end + + def new + @info_request = InfoRequest.find_by_url_title!(params[:request_id]) + if authenticated?( + :web => _("To report this request"), + :email => _("Then you can report the request '{{title}}'", :title => @info_request.title), + :email_subject => _("Report an offensive or unsuitable request")) + end + end +end diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index dfa3a4834..a94461758 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -1,23 +1,23 @@ +# encoding: UTF-8 # app/controllers/request_controller.rb: # Show information about one particular request. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -require 'alaveteli_file_types' require 'zip/zip' require 'open-uri' class RequestController < ApplicationController before_filter :check_read_only, :only => [ :new, :show_response, :describe_state, :upload_response ] protect_from_forgery :only => [ :new, :show_response, :describe_state, :upload_response ] # See ActionController::RequestForgeryProtection for details - + before_filter :check_batch_requests_and_user_allowed, :only => [ :select_authorities, :new_batch ] MAX_RESULTS = 500 PER_PAGE = 25 @@custom_states_loaded = false begin - if ENV["RAILS_ENV"] != "test" + if !Rails.env.test? require 'customstates' include RequestControllerCustomStates @@custom_states_loaded = true @@ -28,7 +28,7 @@ class RequestController < ApplicationController def select_authority # Check whether we force the user to sign in right at the start, or we allow her # to start filling the request anonymously - if Configuration::force_registration_on_new_request && !authenticated?( + if AlaveteliConfiguration::force_registration_on_new_request && !authenticated?( :web => _("To send your FOI request"), :email => _("Then you'll be allowed to send FOI requests."), :email_subject => _("Confirm your email address") @@ -43,8 +43,34 @@ class RequestController < ApplicationController medium_cache end + def select_authorities + if !params[:public_body_query].nil? + @search_bodies = perform_search_typeahead(params[:public_body_query], PublicBody, 1000) + end + respond_to do |format| + format.html do + if !params[:public_body_ids].nil? + if !params[:remove_public_body_ids].nil? + body_ids = params[:public_body_ids] - params[:remove_public_body_ids] + else + body_ids = params[:public_body_ids] + end + @public_bodies = PublicBody.where({:id => body_ids}).all + end + end + format.json do + if @search_bodies + render :json => @search_bodies.results.map{ |result| {:name => result[:model].name, + :id => result[:model].id } } + else + render :json => [] + end + end + end + end + def show - if !Configuration::varnish_host.blank? + if !AlaveteliConfiguration::varnish_host.blank? # If varnish is set up to accept PURGEs, then cache for a # long time long_cache @@ -52,7 +78,7 @@ class RequestController < ApplicationController medium_cache end @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do # Look up by old style numeric identifiers if params[:url_title].match(/^[0-9]+$/) @@ -63,27 +89,24 @@ class RequestController < ApplicationController # Look up by new style text names @info_request = InfoRequest.find_by_url_title!(params[:url_title]) - set_last_request(@info_request) # Test for whole request being hidden if !@info_request.user_can_view?(authenticated_user) - render :template => 'request/hidden', :status => 410 # gone - return + return render_hidden end - # Other parameters - @info_request_events = @info_request.info_request_events - @status = @info_request.calculate_status - @collapse_quotes = params[:unfold] ? false : true + set_last_request(@info_request) + # assign variables from request parameters + @collapse_quotes = params[:unfold] ? false : true # Don't allow status update on external requests, otherwise accept param if @info_request.is_external? @update_status = false else @update_status = params[:update_status] ? true : false end - @old_unclassified = @info_request.is_old_unclassified? && !authenticated_user.nil? - @is_owning_user = @info_request.is_owning_user?(authenticated_user) + + assign_variables_for_show_template(@info_request) if @update_status return if !@is_owning_user && !authenticated_as_user?(@info_request.user, @@ -93,27 +116,15 @@ class RequestController < ApplicationController ) end - - @last_info_request_event_id = @info_request.last_event_id_needing_description - @new_responses_count = @info_request.events_needing_description.select {|i| i.event_type == 'response'}.size - # Sidebar stuff - # ... requests that have similar imporant terms - begin - limit = 10 - @xapian_similar = ::ActsAsXapian::Similar.new([InfoRequestEvent], @info_request.info_request_events, - :limit => limit, :collapse_by_prefix => 'request_collapse') - @xapian_similar_more = (@xapian_similar.matches_estimated > limit) - rescue - @xapian_similar = nil - end + @sidebar = true + @similar_cache_key = cache_key_for_similar_requests(@info_request, @locale) # Track corresponding to this page @track_thing = TrackThing.create_track_for_request(@info_request) @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] - # For send followup link at bottom - @last_response = @info_request.get_last_response + respond_to do |format| format.html { @has_json = true; render :template => 'request/show'} format.json { render :json => @info_request.json_for_api(true) } @@ -126,8 +137,7 @@ class RequestController < ApplicationController long_cache @info_request = InfoRequest.find_by_url_title!(params[:url_title]) if !@info_request.user_can_view?(authenticated_user) - render :template => 'request/hidden', :status => 410 # gone - return + return render_hidden end @columns = ['id', 'event_type', 'created_at', 'described_state', 'last_described_at', 'calculated_state' ] end @@ -146,27 +156,23 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new("Request not found") if @info_request.nil? if !@info_request.user_can_view?(authenticated_user) - render :template => 'request/hidden', :status => 410 # gone - return + return render_hidden end - @xapian_object = ::ActsAsXapian::Similar.new([InfoRequestEvent], @info_request.info_request_events, + @xapian_object = ActsAsXapian::Similar.new([InfoRequestEvent], @info_request.info_request_events, :offset => (@page - 1) * @per_page, :limit => @per_page, :collapse_by_prefix => 'request_collapse') @matches_estimated = @xapian_object.matches_estimated @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated - - if (@page > 1) - @page_desc = " (page " + @page.to_s + ")" - else - @page_desc = "" - end end def list medium_cache @view = params[:view] + @locale = self.locale_from_params() @page = get_search_page_from_params if !@page # used in cache case, as perform_search sets @page as side effect + @per_page = PER_PAGE + @max_results = MAX_RESULTS if @view == "recent" - return redirect_to request_list_all_path(:action => "list", :view => "all", :page => @page), :status => :moved_permanently + return redirect_to request_list_all_url(:action => "list", :view => "all", :page => @page), :status => :moved_permanently end # Later pages are very expensive to load @@ -174,17 +180,11 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new("Sorry. No pages after #{MAX_RESULTS / PER_PAGE}.") end - params[:latest_status] = @view - query = make_query_from_params + @filters = params.merge(:latest_status => @view) @title = _("View and search requests") - sortby = "newest" - xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_collapse') - @list_results = xapian_object.results.map { |r| r[:model] } - @matches_estimated = xapian_object.matches_estimated - @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated @title = @title + " (page " + @page.to_s + ")" if (@page > 1) - @track_thing = TrackThing.create_track_for_search_query(query) + @track_thing = TrackThing.create_track_for_search_query(InfoRequestEvent.make_query_from_params(@filters)) @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] # Don't let robots go more than 20 pages in @@ -193,6 +193,69 @@ class RequestController < ApplicationController end end + def new_batch + if params[:public_body_ids].blank? + redirect_to select_authorities_path and return + end + + # TODO: Decide if we make batch requesters describe their undescribed requests + # before being able to make a new batch request + + if !authenticated_user.can_file_requests? + @details = authenticated_user.can_fail_html + render :template => 'user/banned' and return + end + + @batch = true + + I18n.with_locale(@locale) do + @public_bodies = PublicBody.where({:id => params[:public_body_ids]}). + includes(:translations). + order('public_body_translations.name').all + end + if params[:submitted_new_request].nil? || params[:reedit] + return render_new_compose(batch=true) + end + + # Check for double submission of batch + @existing_batch = InfoRequestBatch.find_existing(authenticated_user, + params[:info_request][:title], + params[:outgoing_message][:body], + params[:public_body_ids]) + + @info_request = InfoRequest.create_from_attributes(params[:info_request], + params[:outgoing_message], + authenticated_user) + @outgoing_message = @info_request.outgoing_messages.first + @info_request.is_batch_request_template = true + if !@existing_batch.nil? || !@info_request.valid? + # We don't want the error "Outgoing messages is invalid", as in this + # case the list of errors will also contain a more specific error + # describing the reason it is invalid. + @info_request.errors.delete(:outgoing_messages) + render :action => 'new' + return + end + + # Show preview page, if it is a preview + if params[:preview].to_i == 1 + return render_new_preview + end + + @info_request_batch = InfoRequestBatch.create!(:title => params[:info_request][:title], + :body => params[:outgoing_message][:body], + :public_bodies => @public_bodies, + :user => authenticated_user) + flash[:notice] = _("<p>Your {{law_used_full}} requests will be <strong>sent</strong> shortly!</p> + <p><strong>We will email you</strong> when they have been sent. + We will also email you when there is a response to any of them, or after {{late_number_of_days}} working days if the authorities still haven't + replied by then.</p> + <p>If you write about these requests (for example in a forum or a blog) please link to this page.</p>", + :law_used_full=>@info_request.law_used_full, + :late_number_of_days => AlaveteliConfiguration::reply_late_after_days) + redirect_to info_request_batch_path(@info_request_batch) + end + # Page new form posts to def new # All new requests are of normal_sort @@ -237,68 +300,19 @@ class RequestController < ApplicationController render :template => 'user/rate_limited' return end - - params[:info_request] = { } if !params[:info_request] - - # Read parameters in - first the public body (by URL name or id) - if params[:url_name] - if params[:url_name].match(/^[0-9]+$/) - params[:info_request][:public_body_id] = params[:url_name] - else - public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) - raise ActiveRecord::RecordNotFound.new("None found") if public_body.nil? # XXX proper 404 - params[:info_request][:public_body_id] = public_body.id - end - elsif params[:public_body_id] - params[:info_request][:public_body_id] = params[:public_body_id] - end - if !params[:info_request][:public_body_id] - # compulsory to have a body by here, or go to front page which is start of process - redirect_to frontpage_url - return - end - - # ... next any tags or other things - params[:info_request][:title] = params[:title] if params[:title] - params[:info_request][:tag_string] = params[:tags] if params[:tags] - - @info_request = InfoRequest.new(params[:info_request]) - params[:info_request_id] = @info_request.id - params[:outgoing_message] = {} if !params[:outgoing_message] - params[:outgoing_message][:body] = params[:body] if params[:body] - params[:outgoing_message][:default_letter] = params[:default_letter] if params[:default_letter] - params[:outgoing_message][:info_request] = @info_request - @outgoing_message = OutgoingMessage.new(params[:outgoing_message]) - @outgoing_message.set_signature_name(@user.name) if !@user.nil? - - if @info_request.public_body.is_requestable? - render :action => 'new' - else - if @info_request.public_body.not_requestable_reason == 'bad_contact' - render :action => 'new_bad_contact' - else - # if not requestable because defunct or not_apply, redirect to main page - # (which doesn't link to the /new/ URL) - redirect_to public_body_url(@info_request.public_body) - end - end - return + return render_new_compose(batch=false) end # See if the exact same request has already been submitted # XXX this check should theoretically be a validation rule in the # model, except we really want to pass @existing_request to the view so # it can link to it. - @existing_request = InfoRequest.find_by_existing_request(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body]) + @existing_request = InfoRequest.find_existing(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body]) # Create both FOI request and the first request message - @info_request = InfoRequest.new(params[:info_request]) - @outgoing_message = OutgoingMessage.new(params[:outgoing_message].merge({ - :status => 'ready', - :message_type => 'initial_request' - })) - @info_request.outgoing_messages << @outgoing_message - @outgoing_message.info_request = @info_request + @info_request = InfoRequest.create_from_attributes(params[:info_request], + params[:outgoing_message]) + @outgoing_message = @info_request.outgoing_messages.first # Maybe we lost the address while they're writing it if !@info_request.public_body.is_requestable? @@ -311,7 +325,7 @@ class RequestController < ApplicationController # We don't want the error "Outgoing messages is invalid", as in this # case the list of errors will also contain a more specific error # describing the reason it is invalid. - @info_request.errors.delete("outgoing_messages") + @info_request.errors.delete(:outgoing_messages) render :action => 'new' return @@ -319,24 +333,7 @@ class RequestController < ApplicationController # Show preview page, if it is a preview if params[:preview].to_i == 1 - message = "" - if @outgoing_message.contains_email? - if @user.nil? - message += (_("<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"%s\">details</a>).</p>") % [help_privacy_path+"#email_address"]).html_safe; - else - message += (_("<p>You do not need to include your email in the request in order to get a reply (<a href=\"%s\">details</a>).</p>") % [help_privacy_path+"#email_address"]).html_safe; - end - message += _("<p>We recommend that you edit your request and remove the email address. - If you leave it, the email address will be sent to the authority, but will not be displayed on the site.</p>") - end - if @outgoing_message.contains_postcode? - message += _("<p>Your request contains a <strong>postcode</strong>. Unless it directly relates to the subject of your request, please remove any address as it will <strong>appear publicly on the Internet</strong>.</p>"); - end - if not message.empty? - flash.now[:error] = message.html_safe - end - render :action => 'preview' - return + return render_new_preview end if user_exceeded_limit @@ -345,9 +342,9 @@ class RequestController < ApplicationController end if !authenticated?( - :web => _("To send your FOI request"), + :web => _("To send your FOI request").to_str, :email => _("Then your FOI request to {{public_body_name}} will be sent.",:public_body_name=>@info_request.public_body.name), - :email_subject => _("Confirm your FOI request to ") + @info_request.public_body.name + :email_subject => _("Confirm your FOI request to {{public_body_name}}",:public_body_name=>@info_request.public_body.name) ) # do nothing - as "authenticated?" has done the redirect to signin page for us return @@ -369,99 +366,94 @@ class RequestController < ApplicationController replied by then.</p> <p>If you write about this request (for example in a forum or a blog) please link to this page, and add an annotation below telling people about your writing.</p>",:law_used_full=>@info_request.law_used_full, - :late_number_of_days => Configuration::reply_late_after_days) + :late_number_of_days => AlaveteliConfiguration::reply_late_after_days) redirect_to show_new_request_path(:url_title => @info_request.url_title) end # Submitted to the describing state of messages form def describe_state - @info_request = InfoRequest.find(params[:id].to_i) - set_last_request(@info_request) - - # If this isn't a form submit, go to the request page - if params[:submitted_describe_state].nil? - redirect_to request_url(@info_request) - return - end + info_request = InfoRequest.find(params[:id].to_i) + set_last_request(info_request) # If this is an external request, go to the request page - we don't allow # state change from the front end interface. - if @info_request.is_external? - redirect_to request_url(@info_request) + if info_request.is_external? + redirect_to request_url(info_request) return end - @is_owning_user = @info_request.is_owning_user?(authenticated_user) - @last_info_request_event_id = @info_request.last_event_id_needing_description - @old_unclassified = @info_request.is_old_unclassified? && !authenticated_user.nil? - - # Check authenticated, and parameters set. We check is_owning_user - # to get admin overrides (see is_owning_user? above) - if !@old_unclassified && !@is_owning_user && !authenticated_as_user?(@info_request.user, + # Check authenticated, and parameters set. + unless Ability::can_update_request_state?(authenticated_user, info_request) + authenticated_as_user?(info_request.user, :web => _("To classify the response to this FOI request"), - :email => _("Then you can classify the FOI response you have got from ") + @info_request.public_body.name + ".", - :email_subject => _("Classify an FOI response from ") + @info_request.public_body.name - ) + :email => _("Then you can classify the FOI response you have got from ") + info_request.public_body.name + ".", + :email_subject => _("Classify an FOI response from ") + info_request.public_body.name) # do nothing - as "authenticated?" has done the redirect to signin page for us return end if !params[:incoming_message] flash[:error] = _("Please choose whether or not you got some of the information that you wanted.") - redirect_to request_url(@info_request) + redirect_to request_url(info_request) return end - if params[:last_info_request_event_id].to_i != @last_info_request_event_id + if params[:last_info_request_event_id].to_i != info_request.last_event_id_needing_description flash[:error] = _("The request has been updated since you originally loaded this page. Please check for any new incoming messages below, and try again.") - redirect_to request_url(@info_request) + redirect_to request_url(info_request) + return + end + + described_state = params[:incoming_message][:described_state] + message = params[:incoming_message][:message] + # For requires_admin and error_message states we ask for an extra message to send to + # the administrators. + # If this message hasn't been included then ask for it + if ["error_message", "requires_admin"].include?(described_state) && message.nil? + redirect_to describe_state_message_url(:url_title => info_request.url_title, :described_state => described_state) return end # Make the state change - old_described_state = @info_request.described_state - @info_request.set_described_state(params[:incoming_message][:described_state]) + event = info_request.log_event("status_update", + { :user_id => authenticated_user.id, + :old_described_state => info_request.described_state, + :described_state => described_state, + }) + + info_request.set_described_state(described_state, authenticated_user, message) # If you're not the *actual* requester. e.g. you are playing the # classification game, or you're doing this just because you are an # admin user (not because you also own the request). - if !@info_request.is_actual_owning_user?(authenticated_user) - # Log the status change by someone other than the requester - event = @info_request.log_event("status_update", - { :user_id => authenticated_user.id, - :old_described_state => old_described_state, - :described_state => @info_request.described_state, - }) + if !info_request.is_actual_owning_user?(authenticated_user) # Create a classification event for league tables RequestClassification.create!(:user_id => authenticated_user.id, :info_request_event_id => event.id) # Don't give advice on what to do next, as it isn't their request - RequestMailer.deliver_old_unclassified_updated(@info_request) if !@info_request.is_external? if session[:request_game] - flash[:notice] = _('Thank you for updating the status of the request \'<a href="{{url}}">{{info_request_title}}</a>\'. There are some more requests below for you to classify.',:info_request_title=>CGI.escapeHTML(@info_request.title), :url=>CGI.escapeHTML(request_url(@info_request))) - redirect_to play_url + flash[:notice] = _('Thank you for updating the status of the request \'<a href="{{url}}">{{info_request_title}}</a>\'. There are some more requests below for you to classify.',:info_request_title=>CGI.escapeHTML(info_request.title), :url=>CGI.escapeHTML(request_path(info_request))) + redirect_to categorise_play_url else flash[:notice] = _('Thank you for updating this request!') - redirect_to request_url(@info_request) + redirect_to request_url(info_request) end return end - calculated_status = @info_request.calculate_status + calculated_status = info_request.calculate_status # Display advice for requester on what to do next, as appropriate - if calculated_status == 'waiting_response' - flash[:notice] = _("<p>Thank you! Hopefully your wait isn't too long.</p> <p>By law, you should get a response promptly, and normally before the end of <strong> -{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(@info_request.date_response_required_by)) - redirect_to request_url(@info_request) - elsif calculated_status == 'waiting_response_overdue' - flash[:notice] = _("<p>Thank you! Hope you don't have to wait much longer.</p> <p>By law, you should have got a response promptly, and normally before the end of <strong>{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(@info_request.date_response_required_by)) - redirect_to request_url(@info_request) - elsif calculated_status == 'waiting_response_very_overdue' - flash[:notice] = _("<p>Thank you! Your request is long overdue, by more than {{very_late_number_of_days}} working days. Most requests should be answered within {{late_number_of_days}} working days. You might like to complain about this, see below.</p>", :very_late_number_of_days => Configuration::reply_very_late_after_days, :late_number_of_days => Configuration::reply_late_after_days) - redirect_to unhappy_url(@info_request) - elsif calculated_status == 'not_held' - flash[:notice] = _("<p>Thank you! Here are some ideas on what to do next:</p> + flash[:notice] = case info_request.calculate_status + when 'waiting_response' + _("<p>Thank you! Hopefully your wait isn't too long.</p> <p>By law, you should get a response promptly, and normally before the end of <strong> +{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(info_request.date_response_required_by)) + when 'waiting_response_overdue' + _("<p>Thank you! Hope you don't have to wait much longer.</p> <p>By law, you should have got a response promptly, and normally before the end of <strong>{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(info_request.date_response_required_by)) + when 'waiting_response_very_overdue' + _("<p>Thank you! Your request is long overdue, by more than {{very_late_number_of_days}} working days. Most requests should be answered within {{late_number_of_days}} working days. You might like to complain about this, see below.</p>", :very_late_number_of_days => AlaveteliConfiguration::reply_very_late_after_days, :late_number_of_days => AlaveteliConfiguration::reply_late_after_days) + when 'not_held' + _("<p>Thank you! Here are some ideas on what to do next:</p> <ul> <li>To send your request to another authority, first copy the text of your request below, then <a href=\"{{find_authority_url}}\">find the other authority</a>.</li> <li>If you would like to contest the authority's claim that they do not hold the information, here is @@ -472,44 +464,70 @@ class RequestController < ApplicationController </li> </ul>", :find_authority_url => "/new", - :complain_url => CGI.escapeHTML(unhappy_url(@info_request)), - :other_means_url => CGI.escapeHTML(unhappy_url(@info_request)) + "#other_means") - redirect_to request_url(@info_request) - elsif calculated_status == 'rejected' - flash[:notice] = _("Oh no! Sorry to hear that your request was refused. Here is what to do now.") - redirect_to unhappy_url(@info_request) - elsif calculated_status == 'successful' - flash[:notice] = _("<p>We're glad you got all the information that you wanted. If you write about or make use of the information, please come back and add an annotation below saying what you did.</p><p>If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p>", :site_name=>site_name, :donation_url => "http://www.mysociety.org/donate/") - redirect_to request_url(@info_request) - elsif calculated_status == 'partially_successful' - flash[:notice] = _("<p>We're glad you got some of the information that you wanted. If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p><p>If you want to try and get the rest of the information, here's what to do now.</p>", :site_name=>site_name, :donation_url=>"http://www.mysociety.org/donate/") - redirect_to unhappy_url(@info_request) - elsif calculated_status == 'waiting_clarification' - flash[:notice] = _("Please write your follow up message containing the necessary clarifications below.") - redirect_to respond_to_last_url(@info_request) - elsif calculated_status == 'gone_postal' - redirect_to respond_to_last_url(@info_request) + "?gone_postal=1" - elsif calculated_status == 'internal_review' - flash[:notice] = _("<p>Thank you! Hopefully your wait isn't too long.</p><p>You should get a response within {{late_number_of_days}} days, or be told if it will take longer (<a href=\"{{review_url}}\">details</a>).</p>",:late_number_of_days => Configuration.reply_late_after_days, :review_url => unhappy_url(@info_request) + "#internal_review") - redirect_to request_url(@info_request) - elsif calculated_status == 'error_message' - flash[:notice] = _("<p>Thank you! We'll look into what happened and try and fix it up.</p><p>If the error was a delivery failure, and you can find an up to date FOI email address for the authority, please tell us using the form below.</p>") - redirect_to help_general_url(:action => 'contact') - elsif calculated_status == 'requires_admin' - flash[:notice] = _("Please use the form below to tell us more.") - redirect_to help_general_url(:action => 'contact') - elsif calculated_status == 'user_withdrawn' - flash[:notice] = _("If you have not done so already, please write a message below telling the authority that you have withdrawn your request. Otherwise they will not know it has been withdrawn.") - redirect_to respond_to_last_url(@info_request) + :complain_url => CGI.escapeHTML(unhappy_url(info_request)), + :other_means_url => CGI.escapeHTML(unhappy_url(info_request)) + "#other_means") + when 'rejected' + _("Oh no! Sorry to hear that your request was refused. Here is what to do now.") + when 'successful' + if AlaveteliConfiguration::donation_url.blank? + _("<p>We're glad you got all the information that you wanted. If you write about or make use of the information, please come back and add an annotation below saying what you did.</p>") + else + _("<p>We're glad you got all the information that you wanted. If you write about or make use of the information, please come back and add an annotation below saying what you did.</p><p>If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p>", + :site_name => site_name, :donation_url => AlaveteliConfiguration::donation_url) + end + when 'partially_successful' + if AlaveteliConfiguration::donation_url.blank? + _("<p>We're glad you got some of the information that you wanted.</p><p>If you want to try and get the rest of the information, here's what to do now.</p>") + else + _("<p>We're glad you got some of the information that you wanted. If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p><p>If you want to try and get the rest of the information, here's what to do now.</p>", + :site_name => site_name, :donation_url => AlaveteliConfiguration::donation_url) + end + when 'waiting_clarification' + _("Please write your follow up message containing the necessary clarifications below.") + when 'gone_postal' + nil + when 'internal_review' + _("<p>Thank you! Hopefully your wait isn't too long.</p><p>You should get a response within {{late_number_of_days}} days, or be told if it will take longer (<a href=\"{{review_url}}\">details</a>).</p>",:late_number_of_days => AlaveteliConfiguration.reply_late_after_days, :review_url => unhappy_url(info_request) + "#internal_review") + when 'error_message', 'requires_admin' + _("Thank you! We'll look into what happened and try and fix it up.") + when 'user_withdrawn' + _("If you have not done so already, please write a message below telling the authority that you have withdrawn your request. Otherwise they will not know it has been withdrawn.") + end + + case info_request.calculate_status + when 'waiting_response', 'waiting_response_overdue', 'not_held', 'successful', + 'internal_review', 'error_message', 'requires_admin' + redirect_to request_url(info_request) + when 'waiting_response_very_overdue', 'rejected', 'partially_successful' + redirect_to unhappy_url(info_request) + when 'waiting_clarification', 'user_withdrawn' + redirect_to respond_to_last_url(info_request) + when 'gone_postal' + redirect_to respond_to_last_url(info_request) + "?gone_postal=1" else if @@custom_states_loaded - return self.theme_describe_state(@info_request) + return self.theme_describe_state(info_request) else - raise "unknown calculate_status " + calculated_status + raise "unknown calculate_status #{info_request.calculate_status}" end end end + # Collect a message to include with the change of state + def describe_state_message + @info_request = InfoRequest.find_by_url_title!(params[:url_title]) + @described_state = params[:described_state] + @last_info_request_event_id = @info_request.last_event_id_needing_description + @title = case @described_state + when "error_message" + _("I've received an error message") + when "requires_admin" + _("This request requires administrator attention") + else + raise "Unsupported state" + end + end + # Used for links from polymorphic URLs e.g. in Atom feeds - just redirect to # proper URL for the message the event refers to def show_request_event @@ -561,10 +579,7 @@ class RequestController < ApplicationController end - params_outgoing_message = params[:outgoing_message] - if params_outgoing_message.nil? - params_outgoing_message = {} - end + params_outgoing_message = params[:outgoing_message] ? params[:outgoing_message].clone : {} params_outgoing_message.merge!({ :status => 'ready', :message_type => 'followup', @@ -582,13 +597,12 @@ class RequestController < ApplicationController @outgoing_message.set_signature_name(@user.name) if !@user.nil? if (not @incoming_message.nil?) and @info_request != @incoming_message.info_request - raise sprintf("Incoming message %d does not belong to request %d", @incoming_message.info_request_id, @info_request.id) + raise ActiveRecord::RecordNotFound.new("Incoming message #{@incoming_message.id} does not belong to request #{@info_request.id}") end # Test for hidden requests if !authenticated_user.nil? && !@info_request.user_can_view?(authenticated_user) - render :template => 'request/hidden', :status => 410 # gone - return + return render_hidden end # Check address is good @@ -625,7 +639,7 @@ class RequestController < ApplicationController if !params[:submitted_followup].nil? && !params[:reedit] if @info_request.allow_new_responses_from == 'nobody' - flash[:error] = (_('Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href="%s">contact us</a> if you really want to send a follow up message.') % [help_contact_path]).html_safe + flash[:error] = _('Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href="{{url}}">contact us</a> if you really want to send a follow up message.', :url => help_contact_path.html_safe) else if @info_request.find_existing_outgoing_message(params[:outgoing_message][:body]) flash[:error] = _('You previously submitted that exact follow up message for this request.') @@ -671,34 +685,19 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new("Message not found") if incoming_message.nil? if !incoming_message.info_request.user_can_view?(authenticated_user) @info_request = incoming_message.info_request # used by view - render :template => 'request/hidden', :status => 410 # gone + return render_hidden + end + if !incoming_message.user_can_view?(authenticated_user) + @incoming_message = incoming_message # used by view + return render_hidden('request/hidden_correspondence') end # Is this a completely public request that we can cache attachments for # to be served up without authentication? - if incoming_message.info_request.all_can_view? + if incoming_message.info_request.all_can_view? && incoming_message.all_can_view? @files_can_be_cached = true end end - def report_request - info_request = InfoRequest.find_by_url_title!(params[:url_title]) - return if !authenticated?( - :web => _("To report this FOI request"), - :email => _("Then you can report the request '{{title}}'", :title => info_request.title), - :email_subject => _("Report an offensive or unsuitable request") - ) - - if !info_request.attention_requested - info_request.set_described_state('attention_requested', @user) - info_request.attention_requested = true # tells us if attention has ever been requested - info_request.save! - flash[:notice] = _("This request has been reported for administrator attention") - else - flash[:notice] = _("This request has already been reported for administrator attention") - end - redirect_to request_url(info_request) - end - # special caching code so mime types are handled right around_filter :cache_attachments, :only => [ :get_attachment, :get_attachment_as_html ] def cache_attachments @@ -709,16 +708,19 @@ class RequestController < ApplicationController key_path = foi_fragment_cache_path(key) if foi_fragment_cache_exists?(key_path) logger.info("Reading cache for #{key_path}") - raise PermissionDenied.new("Directory listing not allowed") if File.directory?(key_path) - cached = foi_fragment_cache_read(key_path) - response.content_type = AlaveteliFileTypes.filename_to_mimetype(params[:file_name].join("/")) || 'application/octet-stream' - render_for_text(cached) + + if File.directory?(key_path) + render :text => "Directory listing not allowed", :status => 403 + else + render :text => foi_fragment_cache_read(key_path), + :content_type => (AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || 'application/octet-stream') + end return end yield - if params[:skip_cache].nil? + if params[:skip_cache].nil? && response.status == 200 # write it to the fileystem ourselves, so is just a plain file. (The # various fragment cache functions using Ruby Marshall to write the file # which adds a header, so isnt compatible with images that have been @@ -733,13 +735,14 @@ class RequestController < ApplicationController def get_attachment get_attachment_internal(false) + return unless @attachment # Prevent spam to magic request address. Note that the binary # subsitution method used depends on the content type @incoming_message.binary_mask_stuff!(@attachment.body, @attachment.content_type) # we don't use @attachment.content_type here, as we want same mime type when cached in cache_attachments above - response.content_type = AlaveteliFileTypes.filename_to_mimetype(params[:file_name].join("/")) || 'application/octet-stream' + response.content_type = AlaveteliFileTypes.filename_to_mimetype(params[:file_name]) || 'application/octet-stream' render :text => @attachment.body end @@ -752,6 +755,7 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new("Attachment HTML not found.") end get_attachment_internal(true) + return unless @attachment # images made during conversion (e.g. images in PDF files) are put in the cache directory, so # the same cache code in cache_attachments above will display them. @@ -789,7 +793,7 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new(message) end @part_number = params[:part].to_i - @filename = params[:file_name].join("/") + @filename = params[:file_name] if html_conversion @original_filename = @filename.gsub(/\.html$/, "") else @@ -798,8 +802,11 @@ class RequestController < ApplicationController # check permissions raise "internal error, pre-auth filter should have caught this" if !@info_request.user_can_view?(authenticated_user) - @attachment = IncomingMessage.get_attachment_by_url_part_number(@incoming_message.get_attachments_for_display, @part_number) - raise ActiveRecord::RecordNotFound.new("attachment not found part number " + @part_number.to_s + " incoming_message " + @incoming_message.id.to_s) if @attachment.nil? + @attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(@incoming_message.get_attachments_for_display, @part_number, @original_filename) + # If we can't find the right attachment, redirect to the incoming message: + unless @attachment + return redirect_to incoming_message_url(@incoming_message), :status => 303 + end # check filename in URL matches that in database (use a censor rule if you want to change a filename) raise ActiveRecord::RecordNotFound.new("please use same filename as original file has, display: '" + @attachment.display_filename + "' old_display: '" + @attachment.old_display_filename + "' original: '" + @original_filename + "'") if @attachment.display_filename != @original_filename && @attachment.old_display_filename != @original_filename @@ -812,7 +819,7 @@ class RequestController < ApplicationController # FOI officers can upload a response def upload_response @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do @info_request = InfoRequest.find_by_url_title!(params[:url_title]) @reason_params = { @@ -850,7 +857,8 @@ class RequestController < ApplicationController return end - mail = RequestMailer.create_fake_response(@info_request, @user, body, file_name, file_content) + mail = RequestMailer.fake_response(@info_request, @user, body, file_name, file_content) + @info_request.receive(mail, mail.encoded, true) flash[:notice] = _("Thank you for responding to this FOI request! Your response has been published below, and a link to your response has been emailed to ") + CGI.escapeHTML(@info_request.user.name) + "." redirect_to request_url(@info_request) @@ -864,18 +872,13 @@ class RequestController < ApplicationController # by making the last work a wildcard, which is quite the same query = params[:q] @xapian_requests = perform_search_typeahead(query, InfoRequestEvent) - render :partial => "request/search_ahead.rhtml" + render :partial => "request/search_ahead" end def download_entire_request @locale = self.locale_from_params() - PublicBody.with_locale(@locale) do + I18n.with_locale(@locale) do @info_request = InfoRequest.find_by_url_title!(params[:url_title]) - # Test for whole request being hidden or requester-only - if !@info_request.all_can_view? - render :template => 'request/hidden', :status => 410 # gone - return - end if authenticated?( :web => _("To download the zip file"), :email => _("Then you can download a zip file of {{info_request_title}}.", @@ -883,56 +886,195 @@ class RequestController < ApplicationController :email_subject => _("Log in to download a zip file of {{info_request_title}}", :info_request_title=>@info_request.title) ) - updated = Digest::SHA1.hexdigest(@info_request.get_last_event.created_at.to_i.to_s + @info_request.updated_at.to_i.to_s) - @url_path = File.join("/download", - request_dirs(@info_request), - updated, - "#{params[:url_title]}.zip") - file_path = File.expand_path(File.join(download_zip_dir(), @url_path)) - if !File.exists?(file_path) - FileUtils.mkdir_p(File.dirname(file_path)) - Zip::ZipFile.open(file_path, Zip::ZipFile::CREATE) { |zipfile| - convert_command = Configuration::html_to_pdf_command - done = false - if !convert_command.blank? && File.exists?(convert_command) - url = "http://#{Configuration::domain}#{request_url(@info_request)}?print_stylesheet=1" - tempfile = Tempfile.new('foihtml2pdf') - output = AlaveteliExternalCommand.run(convert_command, url, tempfile.path) - if !output.nil? - zipfile.get_output_stream("correspondence.pdf") { |f| - f.puts(File.open(tempfile.path).read) - } - done = true - else - logger.error("Could not convert info request #{@info_request.id} to PDF with command '#{convert_command} #{url} #{tempfile.path}'") - end - tempfile.close - else - logger.warn("No HTML -> PDF converter found at #{convert_command}") - end - if !done - @info_request_events = @info_request.info_request_events - template = File.read(File.join(File.dirname(__FILE__), "..", "views", "request", "simple_correspondence.rhtml")) - output = ERB.new(template).result(binding) - zipfile.get_output_stream("correspondence.txt") { |f| - f.puts(output) - } - end - for message in @info_request.incoming_messages - attachments = message.get_attachments_for_display - for attachment in attachments - filename = "#{attachment.url_part_number}_#{attachment.display_filename}" - zipfile.get_output_stream(filename) { |f| - f.puts(attachment.body) - } - end - end - } - File.chmod(0644, file_path) + # Test for whole request being hidden or requester-only + if !@info_request.user_can_view?(@user) + return render_hidden + end + cache_file_path = @info_request.make_zip_cache_path(@user) + if !File.exists?(cache_file_path) + FileUtils.mkdir_p(File.dirname(cache_file_path)) + make_request_zip(@info_request, cache_file_path) + File.chmod(0644, cache_file_path) + end + send_file(cache_file_path, :filename => "#{@info_request.url_title}.zip") + end + end + end + + private + + def render_hidden(template='request/hidden') + respond_to do |format| + response_code = 403 # forbidden + format.html{ render :template => template, :status => response_code } + format.any{ render :nothing => true, :status => response_code } + end + false + end + + def assign_variables_for_show_template(info_request) + @info_request = info_request + @info_request_events = info_request.info_request_events + @status = info_request.calculate_status + @old_unclassified = info_request.is_old_unclassified? && !authenticated_user.nil? + @is_owning_user = info_request.is_owning_user?(authenticated_user) + @last_info_request_event_id = info_request.last_event_id_needing_description + @new_responses_count = info_request.events_needing_description.select {|i| i.event_type == 'response'}.size + # For send followup link at bottom + @last_response = info_request.get_last_public_response + end + + def make_request_zip(info_request, file_path) + Zip::ZipFile.open(file_path, Zip::ZipFile::CREATE) do |zipfile| + file_info = make_request_summary_file(info_request) + zipfile.get_output_stream(file_info[:filename]) { |f| f.puts(file_info[:data]) } + message_index = 0 + info_request.incoming_messages.each do |message| + next unless message.user_can_view?(authenticated_user) + message_index += 1 + message.get_attachments_for_display.each do |attachment| + filename = "#{message_index}_#{attachment.url_part_number}_#{attachment.display_filename}" + zipfile.get_output_stream(filename) { |f| f.puts(attachment.body) } end - redirect_to @url_path end end end + + def make_request_summary_file(info_request) + done = false + convert_command = AlaveteliConfiguration::html_to_pdf_command + assign_variables_for_show_template(info_request) + if !convert_command.blank? && File.exists?(convert_command) + @render_to_file = true + html_output = render_to_string(:template => 'request/show') + tmp_input = Tempfile.new(['foihtml2pdf-input', '.html']) + tmp_input.write(html_output) + tmp_input.close + tmp_output = Tempfile.new('foihtml2pdf-output') + output = AlaveteliExternalCommand.run(convert_command, tmp_input.path, tmp_output.path) + if !output.nil? + file_info = { :filename => 'correspondence.pdf', + :data => File.open(tmp_output.path).read } + done = true + else + logger.error("Could not convert info request #{info_request.id} to PDF with command '#{convert_command} #{tmp_input.path} #{tmp_output.path}'") + end + tmp_output.close + tmp_input.delete + tmp_output.delete + else + logger.warn("No HTML -> PDF converter found at #{convert_command}") + end + if !done + file_info = { :filename => 'correspondence.txt', + :data => render_to_string(:template => 'request/show', + :layout => false, + :formats => [:text]) } + end + file_info + end + + def cache_key_for_similar_requests(info_request, locale) + "request/similar/#{info_request.id}/#{locale}" + end + + def check_batch_requests_and_user_allowed + if !AlaveteliConfiguration::allow_batch_requests + raise RouteNotFound.new("Page not enabled") + end + if !authenticated?( + :web => _("To make a batch request"), + :email => _("Then you can make a batch request"), + :email_subject => _("Make a batch request"), + :user_name => "a user who has been authorised to make batch requests") + # do nothing - as "authenticated?" has done the redirect to signin page for us + return + end + if !@user.can_make_batch_requests? + return render_hidden('request/batch_not_allowed') + end + end + + def render_new_compose(batch) + + params[:info_request] = { } if !params[:info_request] + + # Read parameters in + unless batch + # first the public body (by URL name or id) + if params[:url_name] + if params[:url_name].match(/^[0-9]+$/) + params[:info_request][:public_body] = PublicBody.find(params[:url_name]) + else + public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) + raise ActiveRecord::RecordNotFound.new("None found") if public_body.nil? # XXX proper 404 + params[:info_request][:public_body] = public_body + end + elsif params[:public_body_id] + params[:info_request][:public_body] = PublicBody.find(params[:public_body_id]) + # Explicitly load the association as this isn't done automatically in newer Rails versions + elsif params[:info_request][:public_body_id] + params[:info_request][:public_body] = PublicBody.find(params[:info_request][:public_body_id]) + end + if !params[:info_request][:public_body] + # compulsory to have a body by here, or go to front page which is start of process + redirect_to frontpage_url + return + end + end + + # ... next any tags or other things + params[:info_request][:title] = params[:title] if params[:title] + params[:info_request][:tag_string] = params[:tags] if params[:tags] + + @info_request = InfoRequest.new(params[:info_request]) + if batch + @info_request.is_batch_request_template = true + end + params[:info_request_id] = @info_request.id + params[:outgoing_message] = {} if !params[:outgoing_message] + params[:outgoing_message][:body] = params[:body] if params[:body] + params[:outgoing_message][:default_letter] = params[:default_letter] if params[:default_letter] + params[:outgoing_message][:info_request] = @info_request + @outgoing_message = OutgoingMessage.new(params[:outgoing_message]) + @outgoing_message.set_signature_name(@user.name) if !@user.nil? + + if batch + render :action => 'new' + else + if @info_request.public_body.is_requestable? + render :action => 'new' + else + if @info_request.public_body.not_requestable_reason == 'bad_contact' + render :action => 'new_bad_contact' + else + # if not requestable because defunct or not_apply, redirect to main page + # (which doesn't link to the /new/ URL) + redirect_to public_body_url(@info_request.public_body) + end + end + end + return + end + + def render_new_preview + message = "" + if @outgoing_message.contains_email? + if @user.nil? + message += _("<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); + else + message += _("<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); + end + message += _("<p>We recommend that you edit your request and remove the email address. + If you leave it, the email address will be sent to the authority, but will not be displayed on the site.</p>") + end + if @outgoing_message.contains_postcode? + message += _("<p>Your request contains a <strong>postcode</strong>. Unless it directly relates to the subject of your request, please remove any address as it will <strong>appear publicly on the Internet</strong>.</p>"); + end + if not message.empty? + flash.now[:error] = message.html_safe + end + render :action => 'preview' + end end diff --git a/app/controllers/request_game_controller.rb b/app/controllers/request_game_controller.rb index 4b6f02970..298818bc7 100644 --- a/app/controllers/request_game_controller.rb +++ b/app/controllers/request_game_controller.rb @@ -2,7 +2,7 @@ # The 'categorise old requests' game # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class RequestGameController < ApplicationController @@ -13,8 +13,7 @@ class RequestGameController < ApplicationController @total = InfoRequest.count @done = @total - @missing @percentage = (@done.to_f / @total.to_f * 10000).round / 100.0 - - @requests = InfoRequest.get_random_old_unclassified(3) + @requests = InfoRequest.get_random_old_unclassified(3, :conditions => ["prominence = 'normal'"]) if @missing == 0 flash[:notice] = _('<p>All done! Thank you very much for your help.</p><p>There are <a href="{{helpus_url}}">more things you can do</a> to help {{site_name}}.</p>', diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb index 1db5348c7..78c494dba 100644 --- a/app/controllers/services_controller.rb +++ b/app/controllers/services_controller.rb @@ -6,7 +6,7 @@ class ServicesController < ApplicationController def other_country_message text = "" - iso_country_code = Configuration::iso_country_code.downcase + iso_country_code = AlaveteliConfiguration::iso_country_code.downcase if country_from_ip.downcase != iso_country_code found_country = WorldFOIWebsites.by_code(country_from_ip) @@ -15,7 +15,7 @@ class ServicesController < ApplicationController FastGettext.locale = FastGettext.best_locale_in(request.env['HTTP_ACCEPT_LANGUAGE']) if found_country && found_country[:country_name] && found_country[:url] && found_country[:name] text = _("Hello! You can make Freedom of Information requests within {{country_name}} at {{link_to_website}}", - :country_name => found_country[:country_name], :link_to_website => "<a href=\"#{found_country[:url]}\">#{found_country[:name]}</a>") + :country_name => found_country[:country_name], :link_to_website => "<a href=\"#{found_country[:url]}\">#{found_country[:name]}</a>".html_safe) else current_country = WorldFOIWebsites.by_code(iso_country_code)[:country_name] text = _("Hello! We have an <a href=\"/help/alaveteli?country_name=#{CGI.escape(current_country)}\">important message</a> for visitors outside {{country_name}}", :country_name => current_country) @@ -24,9 +24,6 @@ class ServicesController < ApplicationController FastGettext.locale = old_fgt_locale end end - if !text.empty? - text += ' <span class="close-button">X</span>'.html_safe - end render :text => text, :content_type => "text/plain" # XXX workaround the HTML validation in test suite end @@ -36,9 +33,9 @@ class ServicesController < ApplicationController :content_type => "text/plain", :layout => false, :locals => {:name_to => info_request.user_name, - :name_from => Configuration::contact_name, + :name_from => AlaveteliConfiguration::contact_name, :info_request => info_request, :reason => params[:reason], - :info_request_url => 'http://' + Configuration::domain + request_url(info_request), + :info_request_url => 'http://' + AlaveteliConfiguration::domain + request_path(info_request), :site_name => site_name} end diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb index 15da7f327..83e05ebbc 100644 --- a/app/controllers/track_controller.rb +++ b/app/controllers/track_controller.rb @@ -3,7 +3,7 @@ # social bookmarking. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackController < ApplicationController @@ -80,10 +80,7 @@ class TrackController < ApplicationController # Track a search term def track_search_query - # XXX should be better thing in rails routes than having to do this - # join just to get / and . to work in a query. - query_array = params[:query_array] - @query = query_array.join("/") + @query = params[:query_array] # XXX more hackery to make alternate formats still work with query_array if /^(.*)\.json$/.match(@query) @@ -119,7 +116,7 @@ class TrackController < ApplicationController # Generic request tracker - set @track_thing before calling def track_set if @user - @existing_track = TrackThing.find_by_existing_track(@user, @track_thing) + @existing_track = TrackThing.find_existing(@user, @track_thing) if @existing_track flash[:notice] = _("You are already following updates about {{track_description}}", :track_description => @track_thing.params[:list_description]) return true @@ -156,11 +153,17 @@ class TrackController < ApplicationController def atom_feed_internal @xapian_object = perform_search([InfoRequestEvent], @track_thing.track_query, @track_thing.params[:feed_sortby], nil, 25, 1) + # We're assuming that a request to a feed url with no format suffix wants atom/xml + # so set that as the default, regardless of content negotiation + request.format = 'xml' unless params[:format] respond_to do |format| - format.atom { render :template => 'track/atom_feed', :content_type => "application/atom+xml" } format.json { render :json => @xapian_object.results.map { |r| r[:model].json_for_api(true, - lambda { |t| @template.highlight_and_excerpt(t, @xapian_object.words_to_highlight, 150) } + lambda { |t| view_context.highlight_and_excerpt(t, @xapian_object.words_to_highlight, 150) } ) } } + format.any { render :template => 'track/atom_feed', + :formats => ['atom'], + :layout => false, + :content_type => 'application/atom+xml' } end end @@ -181,7 +184,8 @@ class TrackController < ApplicationController if new_medium == 'delete' track_thing.destroy flash[:notice] = _("You are no longer following {{track_description}}.", :track_description => track_thing.params[:list_description]) - redirect_to params[:r] + redirect_to URI.parse(params[:r]).path + # Reuse code like this if we let medium change again. #elsif new_medium == 'email_daily' # track_thing.track_medium = new_medium diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 4ee527bae..8d6522923 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -2,7 +2,7 @@ # Show information about a user. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'set' @@ -19,19 +19,22 @@ class UserController < ApplicationController # Show page about a user def show long_cache - if MySociety::Format.simplify_url_part(params[:url_name], 'user', 32) != params[:url_name] - redirect_to :url_name => MySociety::Format.simplify_url_part(params[:url_name], 'user', 32), :status => :moved_permanently + if MySociety::Format.simplify_url_part(params[:url_name], 'user') != params[:url_name] + redirect_to :url_name => MySociety::Format.simplify_url_part(params[:url_name], 'user'), :status => :moved_permanently return end if params[:view].nil? @show_requests = true @show_profile = true + @show_batches = false elsif params[:view] == 'profile' @show_profile = true @show_requests = false + @show_batches = false elsif params[:view] == 'requests' @show_profile = false @show_requests = true + @show_batches = true end @display_user = User.find(:first, :conditions => [ "url_name = ? and email_confirmed = ?", params[:url_name], true ]) @@ -119,7 +122,11 @@ class UserController < ApplicationController @track_things = TrackThing.find(:all, :conditions => ["tracking_user_id = ? and track_medium = ?", @display_user.id, 'email_daily'], :order => 'created_at desc') for track_thing in @track_things # XXX factor out of track_mailer.rb - xapian_object = InfoRequest.full_search([InfoRequestEvent], track_thing.track_query, 'described_at', true, nil, 20, 1) + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], track_thing.track_query, + :sort_by_prefix => 'described_at', + :sort_by_ascending => true, + :collapse_by_prefix => nil, + :limit => 20) feed_results += xapian_object.results.map {|x| x[:model]} end end @@ -136,7 +143,7 @@ class UserController < ApplicationController # Login form def signin work_out_post_redirect - @request_from_foreign_country = country_from_ip != Configuration::iso_country_code + @request_from_foreign_country = country_from_ip != AlaveteliConfiguration::iso_country_code # make sure we have cookies if session.instance_variable_get(:@dbman) if not session.instance_variable_get(:@dbman).instance_variable_get(:@original) @@ -190,7 +197,7 @@ class UserController < ApplicationController # Create new account form def signup work_out_post_redirect - @request_from_foreign_country = country_from_ip != Configuration::iso_country_code + @request_from_foreign_country = country_from_ip != AlaveteliConfiguration::iso_country_code # Make the user and try to save it @user_signup = User.new(params[:user_signup]) error = false @@ -222,7 +229,7 @@ class UserController < ApplicationController post_redirect = PostRedirect.find_by_email_token(params[:email_token]) if post_redirect.nil? - render :template => 'user/bad_token.rhtml' + render :template => 'user/bad_token' return end @@ -288,7 +295,7 @@ class UserController < ApplicationController post_redirect.user = user_signchangepassword post_redirect.save! url = confirm_url(:email_token => post_redirect.email_token) - UserMailer.deliver_confirm_login(user_signchangepassword, post_redirect.reason_params, url) + UserMailer.confirm_login(user_signchangepassword, post_redirect.reason_params, url).deliver else # User not found, but still show confirm page to not leak fact user exists end @@ -352,7 +359,7 @@ class UserController < ApplicationController # if new email already in use, send email there saying what happened user_alreadyexists = User.find_user_by_email(@signchangeemail.new_email) if user_alreadyexists - UserMailer.deliver_changeemail_already_used(@user.email, @signchangeemail.new_email) + UserMailer.changeemail_already_used(@user.email, @signchangeemail.new_email).deliver # it is important this screen looks the same as the one below, so # you can't change to someone's email in order to tell if they are # registered with that email on the site @@ -373,7 +380,7 @@ class UserController < ApplicationController post_redirect.save! url = confirm_url(:email_token => post_redirect.email_token) - UserMailer.deliver_changeemail_confirm(@user, @signchangeemail.new_email, url) + UserMailer.changeemail_confirm(@user, @signchangeemail.new_email, url).deliver # it is important this screen looks the same as the one above, so # you can't change to someone's email in order to tell if they are # registered with that email on the site @@ -419,13 +426,13 @@ class UserController < ApplicationController params[:contact][:email] = @user.email @contact = ContactValidator.new(params[:contact]) if @contact.valid? - ContactMailer.deliver_user_message( + ContactMailer.user_message( @user, @recipient_user, - main_url(user_url(@user)), + user_url(@user), params[:contact][:subject], params[:contact][:message] - ) + ).deliver flash[:notice] = _("Your message to {{recipient_user_name}} has been sent!",:recipient_user_name=>CGI.escapeHTML(@recipient_user.name)) redirect_to user_url(@recipient_user) return @@ -465,7 +472,7 @@ class UserController < ApplicationController @draft_profile_photo = ProfilePhoto.new(:data => file_content, :draft => true) if !@draft_profile_photo.valid? # error page (uses @profile_photo's error fields in view to show errors) - render :template => 'user/set_draft_profile_photo.rhtml' + render :template => 'user/set_draft_profile_photo' return end @draft_profile_photo.save @@ -480,7 +487,7 @@ class UserController < ApplicationController return end - render :template => 'user/set_crop_profile_photo.rhtml' + render :template => 'user/set_crop_profile_photo' return elsif !params[:submitted_crop_profile_photo].nil? # crop the draft photo according to jquery parameters and set it as the users photo @@ -499,7 +506,7 @@ class UserController < ApplicationController redirect_to set_profile_about_me_url() end else - render :template => 'user/set_draft_profile_photo.rhtml' + render :template => 'user/set_draft_profile_photo' end end @@ -527,11 +534,12 @@ class UserController < ApplicationController def get_draft_profile_photo profile_photo = ProfilePhoto.find(params[:id]) response.content_type = "image/png" - render_for_text(profile_photo.data) + render :text => profile_photo.data end # actual profile photo of a user def get_profile_photo + long_cache @display_user = User.find(:first, :conditions => [ "url_name = ? and email_confirmed = ?", params[:url_name], true ]) if !@display_user raise ActiveRecord::RecordNotFound.new("user not found, url_name=" + params[:url_name]) @@ -542,7 +550,7 @@ class UserController < ApplicationController end response.content_type = "image/png" - render_for_text(@display_user.profile_photo.data) + render :text => @display_user.profile_photo.data end # Change about me text on your profile page @@ -631,7 +639,7 @@ class UserController < ApplicationController post_redirect.save! url = confirm_url(:email_token => post_redirect.email_token) - UserMailer.deliver_confirm_login(user, post_redirect.reason_params, url) + UserMailer.confirm_login(user, post_redirect.reason_params, url).deliver render :action => 'confirm' end @@ -642,7 +650,7 @@ class UserController < ApplicationController post_redirect.save! url = confirm_url(:email_token => post_redirect.email_token) - UserMailer.deliver_already_registered(user, post_redirect.reason_params, url) + UserMailer.already_registered(user, post_redirect.reason_params, url).deliver render :action => 'confirm' # must be same as for send_confirmation_mail above to avoid leak of presence of email in db end diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb new file mode 100644 index 000000000..059cebdfa --- /dev/null +++ b/app/helpers/admin_helper.rb @@ -0,0 +1,37 @@ +module AdminHelper + def icon(name) + content_tag(:i, "", :class => "icon-#{name}") + end + + def eye + icon("eye-open") + end + + def chevron_right + icon("chevron-right") + end + + def chevron_down + icon("chevron-down") + end + + def arrow_right + icon("arrow-right") + end + + def request_both_links(info_request) + link_to(eye, request_path(info_request), :title => "view request on public website") + " " + + link_to(info_request.title, admin_request_show_path(info_request), :title => "view full details") + end + + def public_body_both_links(public_body) + link_to(eye, public_body_path(public_body), :title => "view authority on public website") + " " + + link_to(h(public_body.name), admin_body_show_path(public_body), :title => "view full details") + end + + def user_both_links(user) + link_to(eye, user_path(user), :title => "view user's page on public website") + " " + + link_to(h(user.name), admin_user_show_path(user), :title => "view full details") + end +end + diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 42f9d30f1..154697377 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -3,7 +3,7 @@ # in the application. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'languages' @@ -54,16 +54,16 @@ module ApplicationHelper # Highlight words, also escapes HTML (other than spans that we add) def highlight_words(t, words, html = true) if html - highlight(h(t), words, '<span class="highlight">\1</span>').html_safe + highlight(h(t), words, :highlighter => '<span class="highlight">\1</span>').html_safe else - highlight(t, words, '*\1*') + highlight(t, words, :highlighter => '*\1*') end end def highlight_and_excerpt(t, words, excount, html = true) - newt = excerpt(t, words[0], excount) + newt = excerpt(t, words[0], :radius => excount) if not newt - newt = excerpt(t, '', excount) + newt = excerpt(t, '', :radius => excount) end t = newt t = highlight_words(t, words, html) @@ -116,5 +116,25 @@ module ApplicationHelper return !session[:using_admin].nil? || (!@user.nil? && @user.super?) end + def cache_if_caching_fragments(*args, &block) + if AlaveteliConfiguration::cache_fragments + cache(*args) { yield } + else + yield + end + end + + # We only want to cache request lists that have a reasonable chance of not expiring + # before they're requested again. Don't cache lists returned from specific searches + # or anything except the first page of results, just the first page of the default + # views + def request_list_cache_key + cacheable_param_list = ['controller', 'action', 'locale', 'view'] + if params.keys.all?{ |key| cacheable_param_list.include?(key) } + "request-list-#{@view}-#{@locale}" + else + nil + end + end end diff --git a/app/helpers/config_helper.rb b/app/helpers/config_helper.rb index 73e12172f..026600ff1 100644 --- a/app/helpers/config_helper.rb +++ b/app/helpers/config_helper.rb @@ -1,5 +1,5 @@ module ConfigHelper def site_name - Configuration::site_name + AlaveteliConfiguration::site_name end -end
\ No newline at end of file +end diff --git a/app/helpers/link_to_helper.rb b/app/helpers/link_to_helper.rb index 030fab20b..405886a85 100755 --- a/app/helpers/link_to_helper.rb +++ b/app/helpers/link_to_helper.rb @@ -3,102 +3,100 @@ # - # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ module LinkToHelper # Links to various models # Requests - def request_url(info_request, extra_params={}) - params = {:url_title => info_request.url_title, :only_path => true} - return show_request_url(params.merge(extra_params)) + def request_url(info_request, options = {}) + show_request_url({:url_title => info_request.url_title}.merge(options)) end - def request_link(info_request, cls=nil ) - link_to h(info_request.title), request_url(info_request), :class => cls + def request_path(info_request, options = {}) + request_url(info_request, {:only_path => true}.merge(options)) end - def request_admin_url(info_request) - return admin_url('request/show/' + info_request.id.to_s) + def request_link(info_request, cls=nil ) + link_to h(info_request.title), request_path(info_request), :class => cls end - def request_admin_link(info_request, name="admin", cls=nil) - link_to name, request_admin_url(info_request), :class => cls + def request_details_path(info_request) + details_request_path(:url_title => info_request.url_title) end - def request_both_links(info_request) - link_to(h(info_request.title), main_url(request_url(info_request))) + " (" + link_to("admin", request_admin_url(info_request)) + ")" + # Incoming / outgoing messages + def incoming_message_url(incoming_message, options = {}) + return request_url(incoming_message.info_request, options.merge(:anchor => "incoming-#{incoming_message.id}")) end - def request_similar_url(info_request) - return similar_request_url(:url_title => info_request.url_title, :only_path => true) + def incoming_message_path(incoming_message) + incoming_message_url(incoming_message, :only_path => true) end - def request_details_url(info_request) - return details_request_url(:url_title => info_request.url_title, :only_path => true) + def outgoing_message_url(outgoing_message, options = {}) + request_url(outgoing_message.info_request, options.merge(:anchor => "outgoing-#{outgoing_message.id}")) end - # Incoming / outgoing messages - def incoming_message_url(incoming_message) - return request_url(incoming_message.info_request)+"#incoming-"+incoming_message.id.to_s + def outgoing_message_path(outgoing_message) + outgoing_message_url(outgoing_message, :only_path => true) end - def outgoing_message_url(outgoing_message) - return request_url(outgoing_message.info_request)+"#outgoing-"+outgoing_message.id.to_s + def comment_url(comment, options = {}) + request_url(comment.info_request, options.merge(:anchor => "comment-#{comment.id}")) end - def comment_url(comment) - return request_url(comment.info_request)+"#comment-"+comment.id.to_s + def comment_path(comment) + comment_url(comment, :only_path => true) end # Respond to request - def respond_to_last_url(info_request) - last_response = info_request.get_last_response + def respond_to_last_url(info_request, options = {}) + last_response = info_request.get_last_public_response if last_response.nil? - respond_url = show_response_no_followup_url(:id => info_request.id) + show_response_no_followup_url(options.merge(:id => info_request.id)) else - respond_url = show_response_url(:id => info_request.id, :incoming_message_id => last_response.id) + show_response_url(options.merge(:id => info_request.id, :incoming_message_id => last_response.id)) end - return respond_url end - # Public bodies - def public_body_url(public_body) - public_body.url_name.nil? ? '' : show_public_body_url(:url_name => public_body.url_name, :only_path => true) + def respond_to_last_path(info_request) + respond_to_last_url(info_request, :only_path => true) end - def public_body_link_short(public_body) - link_to h(public_body.short_or_long_name), public_body_url(public_body) + # Public bodies + def public_body_url(public_body, options = {}) + public_body.url_name.nil? ? '' : show_public_body_url(options.merge(:url_name => public_body.url_name)) end - def public_body_link(public_body, cls=nil) - link_to h(public_body.name), public_body_url(public_body), :class => cls + def public_body_path(public_body) + public_body_url(public_body, :only_path => true) end - def public_body_link_absolute(public_body) # e.g. for in RSS - link_to h(public_body.name), main_url(public_body_url(public_body)) + def public_body_link_short(public_body) + link_to h(public_body.short_or_long_name), public_body_path(public_body) end - def public_body_admin_url(public_body) - return admin_url('body/show/' + public_body.id.to_s) + def public_body_link(public_body, cls=nil) + link_to h(public_body.name), public_body_path(public_body), :class => cls end - def public_body_both_links(public_body) - link_to(h(public_body.name), main_url(public_body_url(public_body))) + " (" + link_to("admin", public_body_admin_url(public_body)) + ")" + def public_body_link_absolute(public_body) # e.g. for in RSS + link_to h(public_body.name), public_body_url(public_body) end - def list_public_bodies_default - list_public_bodies_url(:tag => 'all') + # Users + def user_url(user, options = {}) + show_user_url(options.merge(:url_name => user.url_name)) end - # Users - def user_url(user) - return show_user_url(:url_name => user.url_name, :only_path => true) + def user_path(user) + user_url(user, :only_path => true) end def user_link(user, cls=nil) - link_to h(user.name), user_url(user), :class => cls + link_to h(user.name), user_path(user), :class => cls end def user_link_for_request(request, cls=nil) @@ -110,36 +108,60 @@ module LinkToHelper user_name end else - link_to h(request.user.name), user_url(request.user), :class => cls + link_to h(request.user.name), user_path(request.user), :class => cls end end def user_admin_link_for_request(request, external_text=nil, internal_text=nil) if request.is_external? - text = external_text ? external_text : (request.external_user_name || _("Anonymous user")) + " (external)" + external_text || (request.external_user_name || _("Anonymous user")) + " (external)" else - text = internal_text ? internal_text : request.user.name - link_to(h(text), user_admin_url(request.user)) + link_to(h(internal_text || request.user.name), admin_user_show_url(request.user)) end end def user_link_absolute(user) - link_to h(user.name), main_url(user_url(user)) + link_to user.name, user_url(user) + end + + def user_link(user) + link_to user.name, user_path(user) end - def request_user_link_absolute(request) + def external_user_link(request, absolute, text) + if request.external_user_name + request.external_user_name + else + if absolute + url = help_privacy_url(:anchor => 'anonymous') + else + url = help_privacy_path(:anchor => 'anonymous') + end + link_to(text, url) + end + end + + def request_user_link_absolute(request, anonymous_text=_("Anonymous user")) if request.is_external? - request.external_user_name || _("Anonymous user") + external_user_link(request, absolute=true, anonymous_text) else user_link_absolute(request.user) end end + def request_user_link(request, anonymous_text=_("Anonymous user")) + if request.is_external? + external_user_link(request, absolute=false, anonymous_text) + else + user_link(request.user) + end + end + def user_or_you_link(user) if @user && user == @user - link_to h("you"), user_url(user) + link_to h("you"), user_path(user) else - link_to h(user.name), user_url(user) + link_to h(user.name), user_path(user) end end @@ -152,40 +174,36 @@ module LinkToHelper end def user_or_you_capital_link(user) - link_to user_or_you_capital(user), user_url(user) - end - - def user_admin_url(user) - return admin_url('user/show/' + user.id.to_s) + link_to user_or_you_capital(user), user_path(user) end def user_admin_link(user, name="admin", cls=nil) - link_to name, user_admin_url(user), :class => cls - end - - def user_both_links(user) - link_to(h(user.name), main_url(user_url(user))) + " (" + link_to("admin", user_admin_url(user)) + ")" + link_to name, admin_user_show_url(user), :class => cls end # Tracks. feed can be 'track' or 'feed' - def do_track_url(track_thing, feed = 'track') + def do_track_url(track_thing, feed = 'track', options = {}) if track_thing.track_type == 'request_updates' - track_request_url(:url_title => track_thing.info_request.url_title, :feed => feed) + track_request_url(options.merge(:url_title => track_thing.info_request.url_title, :feed => feed)) elsif track_thing.track_type == 'all_new_requests' - track_list_url(:view => 'recent', :feed => feed) + track_list_url(options.merge(:view => 'recent', :feed => feed)) elsif track_thing.track_type == 'all_successful_requests' - track_list_url(:view => 'successful', :feed => feed) + track_list_url(options.merge(:view => 'successful', :feed => feed)) elsif track_thing.track_type == 'public_body_updates' - track_public_body_url(:url_name => track_thing.public_body.url_name, :feed => feed) + track_public_body_url(options.merge(:url_name => track_thing.public_body.url_name, :feed => feed)) elsif track_thing.track_type == 'user_updates' - track_user_url(:url_name => track_thing.tracked_user.url_name, :feed => feed) + track_user_url(options.merge(:url_name => track_thing.tracked_user.url_name, :feed => feed)) elsif track_thing.track_type == 'search_query' - track_search_url(:query_array => track_thing.track_query, :feed => feed) + track_search_url(options.merge(:query_array => track_thing.track_query, :feed => feed)) else raise "unknown tracking type " + track_thing.track_type end end + def do_track_path(track_thing, feed = 'track') + do_track_url(track_thing, feed, :only_path => true) + end + # General pages. def search_url(query, params = nil) if query.kind_of?(Array) @@ -213,32 +231,27 @@ module LinkToHelper return url end - def search_link(query, variety_postfix = nil, sort_postfix = nil, advanced = nil) - link_to h(query), search_url(query) - end - # Admin pages - def admin_url(relative_path) - admin_url_prefix = Configuration::admin_base_url - (admin_url_prefix.empty? ? admin_general_index_url + '/' : admin_url_prefix) + relative_path + def search_path(query, options = {}) + search_url(query, options.merge(:only_path => true)) end - # About page URLs - def about_url - return help_general_url(:action => 'about') + def search_link(query) + link_to h(query), search_url(query) end - def unhappy_url(info_request = nil) - if info_request.nil? - return help_general_url(:action => 'unhappy') - else - return help_unhappy_url(:url_title => info_request.url_title) - end + # Deprecated helper + # TODO: Remove in next release + def admin_url(relative_path) + warn "[DEPRECATION] admin_url is deprecated. Please remove it from your theme." + relative_path end - + # Deprecated helper + # TODO: Remove in next release def main_url(relative_path, append = nil) - url_prefix = "http://" + Configuration::domain + warn "[DEPRECATION] main_url is deprecated. Please remove it from your theme." + url_prefix = "http://" + AlaveteliConfiguration::domain url = url_prefix + relative_path if !append.nil? begin @@ -253,6 +266,19 @@ module LinkToHelper return url end + # About page URLs + def about_url + return help_general_url(:action => 'about') + end + + def unhappy_url(info_request = nil) + if info_request.nil? + return help_general_url(:action => 'unhappy') + else + return help_unhappy_url(:url_title => info_request.url_title) + end + end + # Basic date format def simple_date(date) date = date.in_time_zone.to_date unless date.is_a? Date diff --git a/app/helpers/mailer_helper.rb b/app/helpers/mailer_helper.rb index be2ce47aa..3d4bbac71 100644 --- a/app/helpers/mailer_helper.rb +++ b/app/helpers/mailer_helper.rb @@ -1,5 +1,5 @@ module MailerHelper def contact_from_name_and_email - "#{Configuration::contact_name} <#{Configuration::contact_email}>" + "#{AlaveteliConfiguration::contact_name} <#{AlaveteliConfiguration::contact_email}>" end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..d2230bb82 --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,30 @@ +# models/application_mailer.rb: +# Shared code between different mailers. +# +# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +require 'action_mailer/version' +class ApplicationMailer < ActionMailer::Base + # Include all the functions views get, as emails call similar things. + helper :application + include MailerHelper + + # This really should be the default - otherwise you lose any information + # about the errors, and have to do error checking on return codes. + self.raise_delivery_errors = true + + def blackhole_email + AlaveteliConfiguration::blackhole_prefix+"@"+AlaveteliConfiguration::incoming_email_domain + end + + # URL generating functions are needed by all controllers (for redirects), + # views (for links) and mailers (for use in emails), so include them into + # all of all. + include LinkToHelper + + # Site-wide access to configuration settings + include ConfigHelper + +end + diff --git a/app/mailers/contact_mailer.rb b/app/mailers/contact_mailer.rb new file mode 100644 index 000000000..27e04ca4b --- /dev/null +++ b/app/mailers/contact_mailer.rb @@ -0,0 +1,64 @@ +# models/contact_mailer.rb: +# Sends contact form mails. +# +# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +class ContactMailer < ApplicationMailer + # Send message to administrator + def to_admin_message(name, email, subject, message, logged_in_user, last_request, last_body) + @message, @logged_in_user, @last_request, @last_body = message, logged_in_user, last_request, last_body + + mail(:from => "#{name} <#{email}>", + :to => contact_from_name_and_email, + :subject => subject) + end + + # We always set Reply-To when we set Return-Path to be different from From, + # since some email clients seem to erroneously use the envelope from when + # they shouldn't, and this might help. (Have had mysterious cases of a + # reply coming in duplicate from a public body to both From and envelope + # from) + + # Send message to another user + def user_message(from_user, recipient_user, from_user_url, subject, message) + @message, @from_user, @recipient_user, @from_user_url = message, from_user, recipient_user, from_user_url + + # Do not set envelope from address to the from_user, so they can't get + # someone's email addresses from transitory bounce messages. + headers('Return-Path' => blackhole_email, 'Reply-To' => from_user.name_and_email) + + mail(:from => from_user.name_and_email, + :to => recipient_user.name_and_email, + :subject => subject) + end + + # Send message to a user from the administrator + def from_admin_message(recipient_name, recipient_email, subject, message) + @message, @from_user = message, contact_from_name_and_email + @recipient_name, @recipient_email = recipient_name, recipient_email + mail(:from => contact_from_name_and_email, + :to => MailHandler.address_from_name_and_email(@recipient_name, @recipient_email), + :bcc => AlaveteliConfiguration::contact_email, + :subject => subject) + end + + # Send a request to the administrator to add an authority + def add_public_body(change_request) + @change_request = change_request + mail(:from => MailHandler.address_from_name_and_email(@change_request.get_user_name, @change_request.get_user_email), + :to => contact_from_name_and_email, + :subject => _('Add authority - {{public_body_name}}', + :public_body_name => @change_request.get_public_body_name)) + end + + # Send a request to the administrator to update an authority email address + def update_public_body_email(change_request) + @change_request = change_request + mail(:from => MailHandler.address_from_name_and_email(@change_request.get_user_name, @change_request.get_user_email), + :to => contact_from_name_and_email, + :subject => _('Update email address - {{public_body_name}}', + :public_body_name => @change_request.get_public_body_name)) + end + +end diff --git a/app/mailers/info_request_batch_mailer.rb b/app/mailers/info_request_batch_mailer.rb new file mode 100644 index 000000000..a2becfb24 --- /dev/null +++ b/app/mailers/info_request_batch_mailer.rb @@ -0,0 +1,25 @@ +# models/info_request_batch_mailer.rb: +# Emails relating to user accounts. e.g. Confirming a new account +# +# Copyright (c) 2013 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +class InfoRequestBatchMailer < ApplicationMailer + + def batch_sent(info_request_batch, unrequestable, user) + @info_request_batch, @unrequestable = info_request_batch, unrequestable + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) + + # Make a link going to the info request batch page, which logs the user in. + post_redirect = PostRedirect.new( + :uri => info_request_batch_url(@info_request_batch), + :user_id => info_request_batch.user_id) + post_redirect.save! + @url = confirm_url(:email_token => post_redirect.email_token) + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => _("Your batch request \"{{title}}\" has been sent", + :title => info_request_batch.title)) + end +end diff --git a/app/models/outgoing_mailer.rb b/app/mailers/outgoing_mailer.rb index 503166b8a..083c05a7c 100644 --- a/app/models/outgoing_mailer.rb +++ b/app/mailers/outgoing_mailer.rb @@ -2,7 +2,7 @@ # Emails which go to public bodies on behalf of users. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # Note: The layout for this wraps messages by lines rather than (blank line # separated) paragraphs, as is the convention for all the other mailers. This @@ -13,31 +13,29 @@ # throughout the application. class OutgoingMailer < ApplicationMailer - # Email to public body requesting info def initial_request(info_request, outgoing_message) + @info_request, @outgoing_message, @contact_email = info_request, outgoing_message, AlaveteliConfiguration::contact_email @wrap_lines_as_paragraphs = true - @from = info_request.incoming_name_and_email - @recipients = info_request.recipient_name_and_email - @subject = info_request.email_subject_request - @headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) - @body = {:info_request => info_request, :outgoing_message => outgoing_message, - :contact_email => Configuration::contact_email } + headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) + + mail(:from => info_request.incoming_name_and_email, + :to => info_request.recipient_name_and_email, + :subject => info_request.email_subject_request) end # Later message to public body regarding existing request def followup(info_request, outgoing_message, incoming_message_followup) + @info_request, @outgoing_message, @incoming_message_followup, @contact_email = info_request, outgoing_message, incoming_message_followup, AlaveteliConfiguration::contact_email @wrap_lines_as_paragraphs = true - @from = info_request.incoming_name_and_email - @recipients = OutgoingMailer.name_and_email_for_followup(info_request, incoming_message_followup) - @subject = OutgoingMailer.subject_for_followup(info_request, outgoing_message) - @headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) - @body = {:info_request => info_request, :outgoing_message => outgoing_message, - :incoming_message_followup => incoming_message_followup, - :contact_email => Configuration::contact_email } + headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) + + mail(:from => info_request.incoming_name_and_email, + :to => OutgoingMailer.name_and_email_for_followup(info_request, incoming_message_followup), + :subject => OutgoingMailer.subject_for_followup(info_request, outgoing_message)) end - # XXX the condition checking valid_to_reply_to? also appears in views/request/_followup.rhtml, + # XXX the condition checking valid_to_reply_to? also appears in views/request/_followup.html.erb, # it shouldn't really, should call something here. # XXX also OutgoingMessage.get_salutation # XXX these look like they should be members of IncomingMessage, but logically they @@ -90,7 +88,7 @@ class OutgoingMailer < ApplicationMailer message_id = "ogm-" + outgoing_message.id.to_s t = Time.now message_id += "+" + '%08x%05x-%04x' % [t.to_i, t.tv_usec, rand(0xffff)] - message_id += "@" + Configuration::incoming_email_domain + message_id += "@" + AlaveteliConfiguration::incoming_email_domain return "<" + message_id + ">" end diff --git a/app/models/request_mailer.rb b/app/mailers/request_mailer.rb index 493d6961c..af1a75df9 100644 --- a/app/models/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -2,89 +2,88 @@ # Alerts relating to requests. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -require 'alaveteli_file_types' +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class RequestMailer < ApplicationMailer - - # Used when an FOI officer uploads a response from their web browser - this is # the "fake" email used to store in the same format in the database as if they # had emailed it. - def fake_response(info_request, from_user, body, attachment_name, attachment_content) - @from = from_user.name_and_email - @recipients = info_request.incoming_name_and_email - @body = { - :body => body - } + def fake_response(info_request, from_user, message_body, attachment_name, attachment_content) + @message_body = message_body + if !attachment_name.nil? && !attachment_content.nil? content_type = AlaveteliFileTypes.filename_to_mimetype(attachment_name) || 'application/octet-stream' - attachment :content_type => content_type, - :body => attachment_content, - :filename => attachment_name + attachments[attachment_name] = {:content => attachment_content, + :content_type => content_type} end + + mail(:from => from_user.name_and_email, + :to => info_request.incoming_name_and_email, + :subject => info_request.email_subject_followup) end # Used when a response is uploaded using the API - def external_response(info_request, body, sent_at, attachments) - @from = blackhole_email - @recipients = info_request.incoming_name_and_email - @body = { :body => body } - - # ActionMailer only works properly when the time is in the local timezone: - # see https://rails.lighthouseapp.com/projects/8994/tickets/3113-actionmailer-only-works-correctly-with-sent_on-times-that-are-in-the-local-time-zone - @sent_on = sent_at.dup.localtime + def external_response(info_request, message_body, sent_at, attachment_hashes) + @message_body = message_body - attachments.each do |attachment_hash| - attachment attachment_hash + attachment_hashes.each do |attachment_hash| + attachments[attachment_hash[:filename]] = {:content => attachment_hash[:body], + :content_type => attachment_hash[:content_type]} end + + mail(:from => blackhole_email, + :to => info_request.incoming_name_and_email, + :date => sent_at) end # Incoming message arrived for a request, but new responses have been stopped. def stopped_responses(info_request, email, raw_email_data) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # we don't care about bounces, likely from spammers - 'Auto-Submitted' => 'auto-replied' # http://tools.ietf.org/html/rfc3834 - @recipients = email.from_addrs[0].to_s - @subject = _("Your response to an FOI request was not delivered") - attachment :content_type => 'message/rfc822', :body => raw_email_data, - :filename => "original.eml", :transfer_encoding => '7bit', :content_disposition => 'inline' - @body = { - :info_request => info_request, - :contact_email => Configuration::contact_email - } + headers('Return-Path' => blackhole_email, # we don't care about bounces, likely from spammers + 'Auto-Submitted' => 'auto-replied') # http://tools.ietf.org/html/rfc3834 + + attachments.inline["original.eml"] = raw_email_data + + @info_request = info_request + @contact_email = AlaveteliConfiguration::contact_email + + mail(:to => email.from_addrs[0].to_s, + :from => contact_from_name_and_email, + :reply_to => contact_from_name_and_email, + :subject => _("Your response to an FOI request was not delivered")) end # An FOI response is outside the scope of the system, and needs admin attention - def requires_admin(info_request, set_by = nil) - if !set_by.nil? - user = set_by - else - user = info_request.user - end - @from = user.name_and_email - @recipients = contact_from_name_and_email - @subject = _("FOI response requires admin ({{reason}}) - {{title}}", :reason => info_request.described_state, :title => info_request.title) - url = main_url(request_url(info_request)) - admin_url = request_admin_url(info_request) - @body = {:reported_by => user, :info_request => info_request, :url => url, :admin_url => admin_url } + def requires_admin(info_request, set_by = nil, message = "") + user = set_by || info_request.user + @reported_by = user + @url = request_url(info_request) + @admin_url = admin_request_show_url(info_request) + @info_request = info_request + @message = message + + mail(:from => user.name_and_email, + :to => contact_from_name_and_email, + :subject => _("FOI response requires admin ({{reason}}) - {{title}}", :reason => info_request.described_state, :title => info_request.title).html_safe) end # Tell the requester that a new response has arrived def new_response(info_request, incoming_message) # Don't use login link here, just send actual URL. This is # because people tend to forward these emails amongst themselves. - url = main_url(incoming_message_url(incoming_message)) + @url = incoming_message_url(incoming_message) + @incoming_message, @info_request = incoming_message, info_request - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + headers('Return-Path' => blackhole_email, 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("New response to your FOI request - ") + info_request.title - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => (_("New response to your FOI request - ") + info_request.title).html_safe, + :charset => "UTF-8", + # not much we can do if the user's email is broken + :reply_to => contact_from_name_and_email) end # Tell the requester that the public body is late in replying @@ -92,18 +91,21 @@ class RequestMailer < ApplicationMailer respond_url = respond_to_last_url(info_request) + "#followup" post_redirect = PostRedirect.new( - :uri => respond_url, + :uri => respond_to_last_url(info_request) + "#followup", :user_id => user.id) post_redirect.save! url = confirm_url(:email_token => post_redirect.email_token) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + @url = confirm_url(:email_token => post_redirect.email_token) + @info_request = info_request + + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = user.name_and_email - @subject = _("Delayed response to your FOI request - ") + info_request.title - @body = { :info_request => info_request, :url => url } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => (_("Delayed response to your FOI request - ") + info_request.title).html_safe) end # Tell the requester that the public body is very late in replying @@ -111,18 +113,19 @@ class RequestMailer < ApplicationMailer respond_url = respond_to_last_url(info_request) + "#followup" post_redirect = PostRedirect.new( - :uri => respond_url, + :uri => respond_to_last_url(info_request) + "#followup", :user_id => user.id) post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) + @url = confirm_url(:email_token => post_redirect.email_token) + @info_request = info_request - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = user.name_and_email - @subject = _("You're long overdue a response to your FOI request - ") + info_request.title - @body = { :info_request => info_request, :url => url } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => (_("You're long overdue a response to your FOI request - ") + info_request.title).html_safe) end # Tell the requester that they need to say if the new response @@ -131,30 +134,34 @@ class RequestMailer < ApplicationMailer # Make a link going to the form to describe state, and which logs the # user in. post_redirect = PostRedirect.new( - :uri => main_url(request_url(info_request)) + "#describe_state_form_1", + :uri => request_url(info_request) + "#describe_state_form_1", :user_id => info_request.user.id) post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) + @url = confirm_url(:email_token => post_redirect.email_token) + @incoming_message = incoming_message + @info_request = info_request - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Was the response you got to your FOI request any good?") - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => _("Was the response you got to your FOI request any good?")) end # Tell the requester that someone updated their old unclassified request def old_unclassified_updated(info_request) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + @url = request_url(info_request) + @info_request = info_request + + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Someone has updated the status of your request") - url = main_url(request_url(info_request)) - @body = {:info_request => info_request, :url => url} + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => _("Someone has updated the status of your request")) end # Tell the requester that they need to clarify their request @@ -166,35 +173,43 @@ class RequestMailer < ApplicationMailer :uri => respond_url, :user_id => info_request.user.id) post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) + @url = confirm_url(:email_token => post_redirect.email_token) + @incoming_message = incoming_message + @info_request = info_request - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Clarify your FOI request - ") + info_request.title - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => (_("Clarify your FOI request - ") + info_request.title).html_safe) end # Tell requester that somebody add an annotation to their request def comment_on_alert(info_request, comment) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + @comment, @info_request = comment, info_request + @url = comment_url(comment) + + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Somebody added a note to your FOI request - ") + info_request.title - @body = { :comment => comment, :info_request => info_request, :url => main_url(comment_url(comment)) } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => (_("Somebody added a note to your FOI request - ") + info_request.title).html_safe) end def comment_on_alert_plural(info_request, count, earliest_unalerted_comment) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken + @count, @info_request = count, info_request + @url = comment_url(earliest_unalerted_comment) + + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email, # not much we can do if the user's email is broken 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Some notes have been added to your FOI request - ") + info_request.title - @body = { :count => count, :info_request => info_request, :url => main_url(comment_url(earliest_unalerted_comment)) } + 'X-Auto-Response-Suppress' => 'OOF') + + mail(:from => contact_from_name_and_email, + :to => info_request.user.name_and_email, + :subject => (_("Some notes have been added to your FOI request - ") + info_request.title).html_safe) end # Class function, called by script/mailin with all incoming responses. @@ -307,9 +322,9 @@ class RequestMailer < ApplicationMailer # (otherwise they are banned, and there is no point sending it) if info_request.user.can_make_followup? if calculated_status == 'waiting_response_overdue' - RequestMailer.deliver_overdue_alert(info_request, info_request.user) + RequestMailer.overdue_alert(info_request, info_request.user).deliver elsif calculated_status == 'waiting_response_very_overdue' - RequestMailer.deliver_very_overdue_alert(info_request, info_request.user) + RequestMailer.very_overdue_alert(info_request, info_request.user).deliver else raise "unknown request status" end @@ -323,7 +338,7 @@ class RequestMailer < ApplicationMailer # Send email alerts for new responses which haven't been classified. By default, # it goes out 3 days after last update of event, then after 10, then after 24. def self.alert_new_response_reminders - Configuration::new_response_reminder_after_days.each_with_index do |days, i| + AlaveteliConfiguration::new_response_reminder_after_days.each_with_index do |days, i| self.alert_new_response_reminders_internal(days, "new_response_reminder_#{i+1}") end end @@ -333,8 +348,8 @@ class RequestMailer < ApplicationMailer :age_in_days => days_since) for info_request in info_requests - alert_event_id = info_request.get_last_response_event_id - last_response_message = info_request.get_last_response + alert_event_id = info_request.get_last_public_response_event_id + last_response_message = info_request.get_last_public_response if alert_event_id.nil? raise "internal error, no last response while making alert new response reminder, request id " + info_request.id.to_s end @@ -348,7 +363,7 @@ class RequestMailer < ApplicationMailer store_sent.alert_type = type_code store_sent.info_request_event_id = alert_event_id # XXX uses same template for reminder 1 and reminder 2 right now. - RequestMailer.deliver_new_response_reminder_alert(info_request, last_response_message) + RequestMailer.new_response_reminder_alert(info_request, last_response_message).deliver store_sent.save! end end @@ -359,8 +374,8 @@ class RequestMailer < ApplicationMailer def self.alert_not_clarified_request() info_requests = InfoRequest.find(:all, :conditions => [ "awaiting_description = ? and described_state = 'waiting_clarification' and info_requests.updated_at < ?", false, Time.now() - 3.days ], :include => [ :user ], :order => "info_requests.id" ) for info_request in info_requests - alert_event_id = info_request.get_last_response_event_id - last_response_message = info_request.get_last_response + alert_event_id = info_request.get_last_public_response_event_id + last_response_message = info_request.get_last_public_response if alert_event_id.nil? raise "internal error, no last response while making alert not clarified reminder, request id " + info_request.id.to_s end @@ -376,7 +391,7 @@ class RequestMailer < ApplicationMailer # Only send the alert if the user can act on it by making a followup # (otherwise they are banned, and there is no point sending it) if info_request.user.can_make_followup? - RequestMailer.deliver_not_clarified_alert(info_request, last_response_message) + RequestMailer.not_clarified_alert(info_request, last_response_message).deliver end store_sent.save! end @@ -440,9 +455,9 @@ class RequestMailer < ApplicationMailer store_sent.alert_type = 'comment_1' store_sent.info_request_event_id = last_comment_event.id if count > 1 - RequestMailer.deliver_comment_on_alert_plural(info_request, count, earliest_unalerted_comment_event.comment) + RequestMailer.comment_on_alert_plural(info_request, count, earliest_unalerted_comment_event.comment).deliver elsif count == 1 - RequestMailer.deliver_comment_on_alert(info_request, last_comment_event.comment) + RequestMailer.comment_on_alert(info_request, last_comment_event.comment).deliver else raise "internal error" end diff --git a/app/models/track_mailer.rb b/app/mailers/track_mailer.rb index 7dfa87f52..8e9beded6 100644 --- a/app/models/track_mailer.rb +++ b/app/mailers/track_mailer.rb @@ -2,30 +2,31 @@ # Emails which go to users who are tracking things. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackMailer < ApplicationMailer def event_digest(user, email_about_things) + @user, @email_about_things = user, email_about_things + post_redirect = PostRedirect.new( - :uri => main_url(user_url(user)) + "#email_subscriptions", + :uri => user_url(user) + "#email_subscriptions", :user_id => user.id) post_redirect.save! - unsubscribe_url = confirm_url(:email_token => post_redirect.email_token) + @unsubscribe_url = confirm_url(:email_token => post_redirect.email_token) - @from = contact_from_name_and_email - headers 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'Precedence' => 'bulk' # http://www.vbulletin.com/forum/project.php?issueid=27687 (Exchange hack) + headers('Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 + 'Precedence' => 'bulk')# http://www.vbulletin.com/forum/project.php?issueid=27687 (Exchange hack) # 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces for tracks # (We let it return bounces for now, so we can manually kill the tracks that bounce so Yahoo # etc. don't decide we are spammers.) - @recipients = user.name_and_email - @subject = _("Your {{site_name}} email alert", :site_name => site_name) - @body = { :user => user, :email_about_things => email_about_things, :unsubscribe_url => unsubscribe_url } + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => _("Your {{site_name}} email alert", :site_name => site_name)) end def contact_from_name_and_email - "#{Configuration::track_sender_name} <#{Configuration::track_sender_email}>" + "#{AlaveteliConfiguration::track_sender_name} <#{AlaveteliConfiguration::track_sender_email}>" end # Send email alerts for tracked things. Never more than one email @@ -38,11 +39,9 @@ class TrackMailer < ApplicationMailer def self.alert_tracks done_something = false now = Time.now() - users = User.find(:all, :conditions => [ "last_daily_track_email < ?", now - 1.day ]) - if users.empty? - return done_something - end - for user in users + one_week_ago = now - 7.days + User.find_each(:conditions => [ "last_daily_track_email < ?", + now - 1.day ]) do |user| next if !user.should_be_emailed? || !user.receive_email_alerts email_about_things = [] @@ -66,7 +65,11 @@ class TrackMailer < ApplicationMailer # Query for things in this track. We use described_at for the # ordering, so we catch anything new (before described), or # anything whose new status has been described. - xapian_object = InfoRequest.full_search([InfoRequestEvent], track_thing.track_query, 'described_at', true, nil, 100, 1) + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], track_thing.track_query, + :sort_by_prefix => 'described_at', + :sort_by_ascending => true, + :collapse_by_prefix => nil, + :limit => 100) # Go through looking for unalerted things alert_results = [] for result in xapian_object.results @@ -75,7 +78,7 @@ class TrackMailer < ApplicationMailer end next if track_thing.created_at >= result[:model].described_at # made before the track was created - next if result[:model].described_at < now - 7.days # older than 1 week (see 14 days / 7 days in comment above) + next if result[:model].described_at < one_week_ago # older than 1 week (see 14 days / 7 days in comment above) next if done_info_request_events.include?(result[:model].id) # definitely already done # OK alert this one @@ -91,10 +94,9 @@ class TrackMailer < ApplicationMailer if email_about_things.size > 0 # Send the email - previous_locale = I18n.locale - I18n.locale = user.get_locale - TrackMailer.deliver_event_digest(user, email_about_things) - I18n.locale = previous_locale + I18n.with_locale(user.get_locale) do + TrackMailer.event_digest(user, email_about_things).deliver + end end # Record that we've now sent those alerts to that user diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 000000000..a351147f9 --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,44 @@ +# models/user_mailer.rb: +# Emails relating to user accounts. e.g. Confirming a new account +# +# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +class UserMailer < ApplicationMailer + def confirm_login(user, reasons, url) + @reasons, @name, @url = reasons, user.name, url + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) # we don't care about bounces when people are fiddling with their account + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => reasons[:email_subject]) + end + + def already_registered(user, reasons, url) + @reasons, @name, @url = reasons, user.name, url + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) # we don't care about bounces when people are fiddling with their account + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => reasons[:email_subject]) + end + + def changeemail_confirm(user, new_email, url) + @name, @url, @old_email, @new_email = user.name, url, user.email, new_email + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) # we don't care about bounces when people are fiddling with their account + + mail(:from => contact_from_name_and_email, + :to => new_email, + :subject => _("Confirm your new email address on {{site_name}}", :site_name => site_name)) + end + + def changeemail_already_used(old_email, new_email) + @old_email, @new_email = old_email, new_email + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) # we don't care about bounces when people are fiddling with their account + + mail(:from => contact_from_name_and_email, + :to => new_email, + :subject => _("Unable to change email address on {{site_name}}", :site_name=>site_name)) + end +end + diff --git a/app/models/about_me_validator.rb b/app/models/about_me_validator.rb index 8ee505ac8..7df70fb61 100644 --- a/app/models/about_me_validator.rb +++ b/app/models/about_me_validator.rb @@ -1,25 +1,23 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: about_me_validators -# -# about_me :text default("I..."), not null -# - # models/about_me_validator.rb: # Validates editing about me text on user profile pages. # # Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -class AboutMeValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +class AboutMeValidator + include ActiveModel::Validations - column :about_me, :text, "I...", false + attr_accessor :about_me # TODO: Switch to built in validations validate :length_of_about_me + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end + end + private def length_of_about_me diff --git a/app/models/application_mailer.rb b/app/models/application_mailer.rb deleted file mode 100644 index 1a97a4bf9..000000000 --- a/app/models/application_mailer.rb +++ /dev/null @@ -1,164 +0,0 @@ -# models/application_mailer.rb: -# Shared code between different mailers. -# -# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -require 'action_mailer/version' -class ApplicationMailer < ActionMailer::Base - # Include all the functions views get, as emails call similar things. - helper :application - include MailerHelper - - # This really should be the default - otherwise you lose any information - # about the errors, and have to do error checking on return codes. - self.raise_delivery_errors = true - - def blackhole_email - Configuration::blackhole_prefix+"@"+Configuration::incoming_email_domain - end - - # URL generating functions are needed by all controllers (for redirects), - # views (for links) and mailers (for use in emails), so include them into - # all of all. - include LinkToHelper - - # Site-wide access to configuration settings - include ConfigHelper - - # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer - # will be initialized according to the named method. If not, the mailer will - # remain uninitialized (useful when you only need to invoke the "receive" - # method, for instance). - def initialize(method_name=nil, *parameters) #:nodoc: - create!(method_name, *parameters) if method_name - end - - # For each multipart template (e.g. "the_template_file.text.html.erb") available, - # add the one from the view path with the highest priority as a part to the mail - def render_multipart_templates - added_content_types = {} - self.view_paths.each do |view_path| - Dir.glob("#{view_path}/#{mailer_name}/#{@template}.*").each do |path| - template = view_path["#{mailer_name}/#{File.basename(path)}"] - - # Skip unless template has a multipart format - next unless template && template.multipart? - next if added_content_types[template.content_type] == true - @parts << Part.new( - :content_type => template.content_type, - :disposition => "inline", - :charset => charset, - :body => render_message(template, @body) - ) - added_content_types[template.content_type] = true - end - end - end - - # Look for the current template in each element of view_paths in order, - # return the first - def find_template - self.view_paths.each do |view_path| - if template = view_path["#{mailer_name}/#{@template}"] - return template - end - end - return nil - end - - if ActionMailer::VERSION::MAJOR == 2 - - # This method is a customised version of ActionMailer::Base.create! - # modified to allow templates to be selected correctly for multipart - # mails when themes have added to the view_paths. The problem from our - # point of view with ActionMailer::Base is that it sets template_root to - # the first element of the view_paths array and then uses only that (directly - # and via template_path, which is created from it) in the create! method when - # looking for templates. Our modified version looks for templates in the view_paths - # in order. - - # It also has a line converting the mail subject to a string. This is because we - # use translated strings in the subject lines, sometimes in conjunction with - # user input, like request titles. The _() function used for translation - # returns an instance of SafeBuffer, which doesn't handle gsub calls in the block form - # with $ variables - https://github.com/rails/rails/issues/1555. - # Unfortunately ActionMailer uses that form in quoted_printable(), which will be - # called if any part of the subject requires quoting. So we convert the subject - # back to a string via to_str() before passing in to create_mail. There is a test - # for this in spec/models/request_mailer_spec.rb - - # Changed lines marked with *** - - # Initialize the mailer via the given +method_name+. The body will be - # rendered and a new TMail::Mail object created. - def create!(method_name, *parameters) #:nodoc: - initialize_defaults(method_name) - __send__(method_name, *parameters) - - # If an explicit, textual body has not been set, we check assumptions. - unless String === @body - # First, we look to see if there are any likely templates that match, - # which include the content-type in their file name (i.e., - # "the_template_file.text.html.erb", etc.). Only do this if parts - # have not already been specified manually. - if @parts.empty? - # *** render_multipart_templates replaces the following code - # Dir.glob("#{template_path}/#{@template}.*").each do |path| - # template = template_root["#{mailer_name}/#{File.basename(path)}"] - # - # # Skip unless template has a multipart format - # next unless template && template.multipart? - # - # @parts << Part.new( - # :content_type => template.content_type, - # :disposition => "inline", - # :charset => charset, - # :body => render_message(template, @body) - # ) - # end - render_multipart_templates - - unless @parts.empty? - @content_type = "multipart/alternative" if @content_type !~ /^multipart/ - @parts = sort_parts(@parts, @implicit_parts_order) - end - end - - # Then, if there were such templates, we check to see if we ought to - # also render a "normal" template (without the content type). If a - # normal template exists (or if there were no implicit parts) we render - # it. - template_exists = @parts.empty? - - # *** find_template replaces template_root call - # template_exists ||= template_root["#{mailer_name}/#{@template}"] - template_exists ||= find_template - - @body = render_message(@template, @body) if template_exists - - # Finally, if there are other message parts and a textual body exists, - # we shift it onto the front of the parts and set the body to nil (so - # that create_mail doesn't try to render it in addition to the parts). - if !@parts.empty? && String === @body - @parts.unshift ActionMailer::Part.new(:charset => charset, :body => @body) - @body = nil - end - end - - # If this is a multipart e-mail add the mime_version if it is not - # already set. - @mime_version ||= "1.0" if !@parts.empty? - - # *** Convert into a string - @subject = @subject.to_str if @subject - - # build the mail object itself - @mail = create_mail - end - else - raise "ApplicationMailer.create! is obsolete - find another way to ensure that themes can override mail templates for multipart mails" - end - -end - diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index f40ab6fbb..3c5c77563 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -1,18 +1,17 @@ # == Schema Information -# Schema version: 20120919140404 # # Table name: censor_rules # -# id :integer not null, primary key +# id :integer not null, primary key # info_request_id :integer # user_id :integer # public_body_id :integer -# text :text not null -# replacement :text not null -# last_edit_editor :string(255) not null -# last_edit_comment :text not null -# created_at :datetime not null -# updated_at :datetime not null +# text :text not null +# replacement :text not null +# last_edit_editor :string(255) not null +# last_edit_comment :text not null +# created_at :datetime not null +# updated_at :datetime not null # regexp :boolean # @@ -20,7 +19,7 @@ # Stores alterations to remove specific data from requests. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class CensorRule < ActiveRecord::Base belongs_to :info_request @@ -33,13 +32,15 @@ class CensorRule < ActiveRecord::Base validate :require_valid_regexp, :if => proc{ |rule| rule.regexp? == true } validates_presence_of :text - named_scope :global, {:conditions => {:info_request_id => nil, - :user_id => nil, - :public_body_id => nil}} + scope :global, {:conditions => {:info_request_id => nil, + :user_id => nil, + :public_body_id => nil}} def require_user_request_or_public_body if self.info_request.nil? && self.user.nil? && self.public_body.nil? - errors.add("Censor must apply to an info request a user or a body; ") + [:info_request, :user, :public_body].each do |a| + errors.add(a, "Rule must apply to an info request, a user or a body") + end end end diff --git a/app/models/change_email_validator.rb b/app/models/change_email_validator.rb index 2ddebb177..5cc13d4c2 100644 --- a/app/models/change_email_validator.rb +++ b/app/models/change_email_validator.rb @@ -1,36 +1,27 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: change_email_validators -# -# old_email :string -# new_email :string -# password :string -# user_circumstance :string -# - # models/changeemail_validator.rb: # Validates email change form submissions. # # Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class ChangeEmailValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ - column :old_email, :string - column :new_email, :string - column :password, :string - column :user_circumstance, :string +class ChangeEmailValidator + include ActiveModel::Validations - attr_accessor :logged_in_user + attr_accessor :old_email, :new_email, :password, :user_circumstance, :logged_in_user validates_presence_of :old_email, :message => N_("Please enter your old email address") validates_presence_of :new_email, :message => N_("Please enter your new email address") validates_presence_of :password, :message => N_("Please enter your password"), :unless => :changing_email validate :password_and_format_of_email - def changing_email() + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end + end + + + def changing_email self.user_circumstance == 'change_email' end @@ -41,11 +32,11 @@ class ChangeEmailValidator < ActiveRecord::BaseWithoutTable errors.add(:old_email, _("Old email doesn't look like a valid address")) end - if !errors[:old_email] + if errors[:old_email].blank? if self.old_email.downcase != self.logged_in_user.email.downcase errors.add(:old_email, _("Old email address isn't the same as the address of the account you are logged in with")) elsif (!self.changing_email) && (!self.logged_in_user.has_this_password?(self.password)) - if !errors[:password] + if errors[:password].blank? errors.add(:password, _("Password is not correct")) end end @@ -55,5 +46,4 @@ class ChangeEmailValidator < ActiveRecord::BaseWithoutTable errors.add(:new_email, _("New email doesn't look like a valid address")) end end - end diff --git a/app/models/comment.rb b/app/models/comment.rb index 70f3ba00d..b4c099123 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,24 +1,23 @@ # == Schema Information -# Schema version: 114 # # Table name: comments # -# id :integer not null, primary key -# user_id :integer not null -# comment_type :string(255) default("internal_error"), not null +# id :integer not null, primary key +# user_id :integer not null +# comment_type :string(255) default("internal_error"), not null # info_request_id :integer -# body :text not null -# visible :boolean default(TRUE), not null -# created_at :datetime not null -# updated_at :datetime not null -# locale :text default(""), not null +# body :text not null +# visible :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# locale :text default(""), not null # # models/comments.rb: # A comment by a user upon something. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class Comment < ActiveRecord::Base strip_attributes! @@ -63,7 +62,7 @@ class Comment < ActiveRecord::Base end # When posting a new comment, use this to check user hasn't double submitted. - def Comment.find_by_existing_comment(info_request_id, body) + def Comment.find_existing(info_request_id, body) # XXX can add other databases here which have regexp_replace if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" # Exclude spaces from the body comparison using regexp_replace diff --git a/app/models/contact_mailer.rb b/app/models/contact_mailer.rb deleted file mode 100644 index abde64928..000000000 --- a/app/models/contact_mailer.rb +++ /dev/null @@ -1,56 +0,0 @@ -# models/contact_mailer.rb: -# Sends contact form mails. -# -# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class ContactMailer < ApplicationMailer - - # Send message to administrator - def to_admin_message(name, email, subject, message, logged_in_user, last_request, last_body) - @from = name + " <" + email + ">" - @recipients = contact_from_name_and_email - @subject = subject - @body = { :message => message, - :logged_in_user => logged_in_user , - :last_request => last_request, - :last_body => last_body - } - end - - # We always set Reply-To when we set Return-Path to be different from From, - # since some email clients seem to erroneously use the envelope from when - # they shouldn't, and this might help. (Have had mysterious cases of a - # reply coming in duplicate from a public body to both From and envelope - # from) - - # Send message to another user - def user_message(from_user, recipient_user, from_user_url, subject, message) - @from = from_user.name_and_email - # Do not set envelope from address to the from_user, so they can't get - # someone's email addresses from transitory bounce messages. - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from - @recipients = recipient_user.name_and_email - @subject = subject - @body = { - :message => message, - :from_user => from_user, - :recipient_user => recipient_user, - :from_user_url => from_user_url - } - end - - # Send message to a user from the administrator - def from_admin_message(recipient_user, subject, message) - @from = contact_from_name_and_email - @recipients = recipient_user.name_and_email - @subject = subject - @body = { - :message => message, - :from_user => @from, - :recipient_user => recipient_user, - } - bcc Configuration::contact_email - end - -end diff --git a/app/models/contact_validator.rb b/app/models/contact_validator.rb index d277161f9..65e539669 100644 --- a/app/models/contact_validator.rb +++ b/app/models/contact_validator.rb @@ -1,35 +1,29 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: contact_validators -# -# name :string -# email :string -# subject :text -# message :text -# - # models/contact_validator.rb: # Validates contact form submissions. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -class ContactValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +class ContactValidator + include ActiveModel::Validations - column :name, :string - column :email, :string - column :subject, :text - column :message, :text + attr_accessor :name, :email, :subject, :message validates_presence_of :name, :message => N_("Please enter your name") validates_presence_of :email, :message => N_("Please enter your email address") validates_presence_of :subject, :message => N_("Please enter a subject") validates_presence_of :message, :message => N_("Please enter the message you want to send") + validate :email_format - def validate - errors.add(:email, _("Email doesn't look like a valid address")) unless MySociety::Validate.is_valid_email(self.email) + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end end + private + + def email_format + errors.add(:email, _("Email doesn't look like a valid address")) unless MySociety::Validate.is_valid_email(self.email) + end end diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb index 723bc4abb..ecd4a1872 100644 --- a/app/models/foi_attachment.rb +++ b/app/models/foi_attachment.rb @@ -1,11 +1,9 @@ # encoding: UTF-8 - # == Schema Information -# Schema version: 114 # # Table name: foi_attachments # -# id :integer not null, primary key +# id :integer not null, primary key # content_type :text # filename :text # charset :text @@ -20,7 +18,7 @@ # An attachment to an email (IncomingMessage) # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # This is the type which is used to send data about attachments to the view require 'digest' @@ -38,11 +36,7 @@ class FoiAttachment < ActiveRecord::Base BODY_MAX_DELAY = 5 def directory - rails_env = Rails.env - if rails_env.nil? || rails_env.empty? - raise "$RAILS_ENV is not set" - end - base_dir = File.expand_path(File.join(File.dirname(__FILE__), "../../cache", "attachments_#{rails_env}")) + base_dir = File.expand_path(File.join(File.dirname(__FILE__), "../../cache", "attachments_#{Rails.env}")) return File.join(base_dir, self.hexdigest[0..2]) end @@ -67,28 +61,20 @@ class FoiAttachment < ActiveRecord::Base file.write d } update_display_size! - encode_cached_body! @cached_body = d end - # If the original mail part had a charset, it's some kind of string, so assume that - # it should be handled as a string in the stated charset, not a bytearray, and then - # convert it our default encoding. For ruby 1.8 this is a noop. - def encode_cached_body! - if RUBY_VERSION.to_f >= 1.9 - if charset - @cached_body.force_encoding(charset) - @cached_body = @cached_body.encode(Encoding.default_internal, charset) - end - end - end - def body if @cached_body.nil? tries = 0 delay = 1 begin - @cached_body = File.open(self.filepath, "rb" ).read + binary_data = File.open(self.filepath, "rb" ){ |file| file.read } + if self.content_type =~ /^text/ + @cached_body = convert_string_to_utf8_or_binary(binary_data, 'UTF-8') + else + @cached_body = binary_data + end rescue Errno::ENOENT # we've lost our cached attachments for some reason. Reparse them. if tries > BODY_MAX_TRIES @@ -103,7 +89,6 @@ class FoiAttachment < ActiveRecord::Base self.incoming_message.parse_raw_email!(force) retry end - encode_cached_body! end return @cached_body end @@ -317,8 +302,7 @@ class FoiAttachment < ActiveRecord::Base text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text) html = text.gsub(/\n/, '<br>') - return '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"><html><head><title></title></head><body>' + html + "</body></html>", wrapper_id + return '<!DOCTYPE html><html><head><title></title></head><body>' + html + "</body></html>", wrapper_id end # the extractions will also produce image files, which go in the @@ -375,7 +359,8 @@ class FoiAttachment < ActiveRecord::Base ret = "<html><head></head><body>"; if self.has_google_docs_viewer? wrapper_id = "wrapper_google_embed" - ret = ret + "<iframe src='http://docs.google.com/viewer?url=<attachment-url-here>&embedded=true' width='100%' height='100%' style='border: none;'></iframe>"; + protocol = AlaveteliConfiguration::force_ssl ? 'https' : 'http' + ret = ret + "<iframe src='#{protocol}://docs.google.com/viewer?url=<attachment-url-here>&embedded=true' width='100%' height='100%' style='border: none;'></iframe>"; else ret = ret + "<p>Sorry, we were unable to convert this file to HTML. Please use the download link at the top right.</p>" end diff --git a/app/models/holiday.rb b/app/models/holiday.rb index 13258396a..3076cc0fd 100644 --- a/app/models/holiday.rb +++ b/app/models/holiday.rb @@ -1,9 +1,8 @@ # == Schema Information -# Schema version: 114 # # Table name: holidays # -# id :integer not null, primary key +# id :integer not null, primary key # day :date # description :text # @@ -19,7 +18,7 @@ # -- Freedom of Information Act 2000 section 10 # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class Holiday < ActiveRecord::Base diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index 5c845ead3..6db145348 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -1,15 +1,13 @@ # coding: utf-8 - # == Schema Information -# Schema version: 114 # # Table name: incoming_messages # -# id :integer not null, primary key -# info_request_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null -# raw_email_id :integer not null +# id :integer not null, primary key +# info_request_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# raw_email_id :integer not null # cached_attachment_text_clipped :text # cached_main_body_text_folded :text # cached_main_body_text_unfolded :text @@ -19,27 +17,28 @@ # last_parsed :datetime # mail_from :text # sent_at :datetime +# prominence :string(255) default("normal"), not null +# prominence_reason :text +# # models/incoming_message.rb: # An (email) message from really anybody to be logged with a request. e.g. A # response from the public body. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # TODO # Move some of the (e.g. quoting) functions here into rblib, as they feel # general not specific to IncomingMessage. -require 'alaveteli_file_types' require 'htmlentities' require 'rexml/document' require 'zip/zip' -require 'mapi/msg' -require 'mapi/convert' - +require 'iconv' unless String.method_defined?(:encode) class IncomingMessage < ActiveRecord::Base + extend MessageProminence belongs_to :info_request validates_presence_of :info_request @@ -51,6 +50,8 @@ class IncomingMessage < ActiveRecord::Base belongs_to :raw_email + has_prominence + # See binary_mask_stuff function below. It just test for inclusion # in this hash, not the value of the right hand side. DoNotBinaryMask = { @@ -62,6 +63,12 @@ class IncomingMessage < ActiveRecord::Base 'application/zip' => 1, } + # Given that there are in theory many info request events, a convenience method for + # getting the response event + def response_event + self.info_request_events.detect{ |e| e.event_type == 'response' } + end + # Return a cached structured mail object def mail(force = nil) if (!force.nil? || @mail.nil?) && !self.raw_email.nil? @@ -122,16 +129,17 @@ class IncomingMessage < ActiveRecord::Base if (!force.nil? || self.last_parsed.nil?) ActiveRecord::Base.transaction do self.extract_attachments! - self.sent_at = self.mail.date || self.created_at - self.subject = self.mail.subject - self.mail_from = MailHandler.get_from_name(self.mail) + write_attribute(:sent_at, self.mail.date || self.created_at) + write_attribute(:subject, self.mail.subject) + write_attribute(:mail_from, MailHandler.get_from_name(self.mail)) if self.from_email self.mail_from_domain = PublicBody.extract_domain_from_email(self.from_email) else self.mail_from_domain = "" end - self.valid_to_reply_to = self._calculate_valid_to_reply_to + write_attribute(:valid_to_reply_to, self._calculate_valid_to_reply_to) self.last_parsed = Time.now + self.foi_attachments reload=true self.save! end end @@ -153,14 +161,17 @@ class IncomingMessage < ActiveRecord::Base parse_raw_email! super end + def subject parse_raw_email! super end + def mail_from parse_raw_email! super end + def safe_mail_from if !self.mail_from.nil? mail_from = self.mail_from.dup @@ -168,20 +179,43 @@ class IncomingMessage < ActiveRecord::Base return mail_from end end + + def specific_from_name? + !safe_mail_from.nil? && safe_mail_from.strip != info_request.public_body.name.strip + end + + def from_public_body? + safe_mail_from.nil? || (mail_from_domain == info_request.public_body.request_email_domain) + end + def mail_from_domain parse_raw_email! super end - # And look up by URL part number to get an attachment + # And look up by URL part number and display filename to get an attachment # XXX relies on extract_attachments calling MailHandler.ensure_parts_counted - def self.get_attachment_by_url_part_number(attachments, found_url_part_number) - attachments.each do |a| - if a.url_part_number == found_url_part_number - return a + # The filename here is passed from the URL parameter, so it's the + # display_filename rather than the real filename. + def self.get_attachment_by_url_part_number_and_filename(attachments, found_url_part_number, display_filename) + attachment_by_part_number = attachments.detect { |a| a.url_part_number == found_url_part_number } + if attachment_by_part_number && attachment_by_part_number.display_filename == display_filename + # Then the filename matches, which is fine: + attachment_by_part_number + else + # Otherwise if the URL part number and filename don't + # match - this is probably due to a reparsing of the + # email. In that case, try to find a unique matching + # filename from any attachment. + attachments_by_filename = attachments.select { |a| + a.display_filename == display_filename + } + if attachments_by_filename.length == 1 + attachments_by_filename[0] + else + nil end end - return nil end # Converts email addresses we know about into textual descriptions of them @@ -193,7 +227,7 @@ class IncomingMessage < ActiveRecord::Base text.gsub!(self.info_request.public_body.request_email, _("[{{public_body}} request email]", :public_body => self.info_request.public_body.short_or_long_name)) end text.gsub!(self.info_request.incoming_email, _('[FOI #{{request}} email]', :request => self.info_request.id.to_s) ) - text.gsub!(Configuration::contact_email, _("[{{site_name}} contact email]", :site_name => Configuration::site_name) ) + text.gsub!(AlaveteliConfiguration::contact_email, _("[{{site_name}} contact email]", :site_name => AlaveteliConfiguration::site_name) ) end # Replaces all email addresses in (possibly binary data) with equal length alternative ones. @@ -219,7 +253,7 @@ class IncomingMessage < ActiveRecord::Base if censored_uncompressed_text != uncompressed_text # then use the altered file (recompressed) recompressed_text = nil - if Configuration::use_ghostscript_compression == true + if AlaveteliConfiguration::use_ghostscript_compression == true command = ["gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-sOutputFile=-", "-"] else command = ["pdftk", "-", "output", "-", "compress"] @@ -246,7 +280,7 @@ class IncomingMessage < ActiveRecord::Base # Used by binary_mask_stuff - replace text in place def _binary_mask_stuff_internal!(text) # Keep original size, so can check haven't resized it - orig_size = text.size + orig_size = text.mb_chars.size # Replace ASCII email addresses... text.gsub!(MySociety::Validate.email_find_regexp) do |email| @@ -258,11 +292,21 @@ class IncomingMessage < ActiveRecord::Base # equivalents to the UCS-2 ascii_chars = text.gsub(/\0/, "") emails = ascii_chars.scan(MySociety::Validate.email_find_regexp) + # Convert back to UCS-2, making a mask at the same time - emails.map! {|email| [ - Iconv.conv('ucs-2le', 'ascii', email[0]), - Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x')) - ] } + if String.method_defined?(:encode) + emails.map! do |email| + # We want the ASCII representation of UCS-2 + [email[0].encode('UTF-16LE').force_encoding('US-ASCII'), + email[0].gsub(/[^@.]/, 'x').encode('UTF-16LE').force_encoding('US-ASCII')] + end + else + emails.map! {|email| [ + Iconv.conv('ucs-2le', 'ascii', email[0]), + Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x')) + ] } + end + # Now search and replace the UCS-2 email with the UCS-2 mask for email, mask in emails text.gsub!(email, mask) @@ -271,7 +315,7 @@ class IncomingMessage < ActiveRecord::Base # Replace censor items self.info_request.apply_censor_rules_to_binary!(text) - raise "internal error in binary_mask_stuff" if text.size != orig_size + raise "internal error in binary_mask_stuff" if text.mb_chars.size != orig_size return text end @@ -316,7 +360,7 @@ class IncomingMessage < ActiveRecord::Base text.gsub!(/(Mobile|Mob)([\s\/]*(Fax|Tel))*\s*:?[\s\d]*\d/, "[mobile number]") # Remove WhatDoTheyKnow signup links - text.gsub!(/http:\/\/#{Configuration::domain}\/c\/[^\s]+/, "[WDTK login link]") + text.gsub!(/http:\/\/#{AlaveteliConfiguration::domain}\/c\/[^\s]+/, "[WDTK login link]") # Remove things from censor rules self.info_request.apply_censor_rules_to_text!(text) @@ -341,6 +385,10 @@ class IncomingMessage < ActiveRecord::Base multiline_original_message = '(' + '''>>>.* \d\d/\d\d/\d\d\d\d\s+\d\d:\d\d(?::\d\d)?\s*>>>''' + ')' text.gsub!(/^(#{multiline_original_message}\n.*)$/m, replacement) + # On Thu, Nov 28, 2013 at 9:08 AM, A User + # <[1]request-7-skm40s2ls@xxx.xxxx> wrote: + text.gsub!(/^( On [^\n]+\n\s*\<[^>\n]+\> (wrote|said):\s*\n.*)$/m, replacement) + # Single line sections text.gsub!(/^(>.*\n)/, replacement) text.gsub!(/^(On .+ (wrote|said):\n)/, replacement) @@ -472,7 +520,7 @@ class IncomingMessage < ActiveRecord::Base # should instead tell elinks to respect the source # charset use_charset = "utf-8" - if RUBY_VERSION.to_f >= 1.9 + if String.method_defined?(:encode) begin text.encode('utf-8') rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError @@ -503,7 +551,7 @@ class IncomingMessage < ActiveRecord::Base end def _sanitize_text(text) - if RUBY_VERSION.to_f >= 1.9 + if String.method_defined?(:encode) begin # Test if it's good UTF-8 text.encode('utf-8') @@ -534,7 +582,7 @@ class IncomingMessage < ActiveRecord::Base source_charset = 'utf-8' if source_charset.nil? text = Iconv.conv('utf-8//IGNORE', source_charset, text) + _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]", - :site_name => Configuration::site_name) + :site_name => AlaveteliConfiguration::site_name) rescue Iconv::InvalidEncoding, Iconv::IllegalSequence, Iconv::InvalidCharacter if source_charset != "utf-8" source_charset = "utf-8" @@ -546,9 +594,11 @@ class IncomingMessage < ActiveRecord::Base text end - # Returns part which contains main body text, or nil if there isn't one - def get_main_body_text_part - leaves = self.foi_attachments + # Returns part which contains main body text, or nil if there isn't one, + # from a set of foi_attachments. If the leaves parameter is empty or not + # supplied, uses its own foi_attachments. + def get_main_body_text_part(leaves=[]) + leaves = self.foi_attachments if leaves.empty? # Find first part which is text/plain or text/html # (We have to include HTML, as increasingly there are mail clients that @@ -582,6 +632,7 @@ class IncomingMessage < ActiveRecord::Base # nil in this case) return p end + # Returns attachments that are uuencoded in main body part def _uudecode_and_save_attachments(text) # Find any uudecoded things buried in it, yeuchly @@ -605,7 +656,7 @@ class IncomingMessage < ActiveRecord::Base content_type = 'application/octet-stream' end hexdigest = Digest::MD5.hexdigest(content) - attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest) + attachment = self.foi_attachments.find_or_create_by_hexdigest(hexdigest) attachment.update_attributes(:filename => filename, :content_type => content_type, :body => content, @@ -632,15 +683,19 @@ class IncomingMessage < ActiveRecord::Base attachment_attributes = MailHandler.get_attachment_attributes(self.mail(force)) attachments = [] attachment_attributes.each do |attrs| - attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => attrs[:hexdigest]) - body = attrs.delete(:body) + attachment = self.foi_attachments.find_or_create_by_hexdigest(attrs[:hexdigest]) attachment.update_attributes(attrs) - # Set the body separately as its handling can depend on the value of charset - attachment.body = body attachment.save! - attachments << attachment.id + attachments << attachment end - main_part = get_main_body_text_part + + # Reload to refresh newly created foi_attachments + self.reload + + # get the main body part from the set of attachments we just created, + # not from the self.foi_attachments association - some of the total set of + # self.foi_attachments may now be obsolete + main_part = get_main_body_text_part(attachments) # we don't use get_main_body_text_internal, as we want to avoid charset # conversions, since /usr/bin/uudecode needs to deal with those. # e.g. for https://secure.mysociety.org/admin/foi/request/show_raw_email/24550 @@ -651,12 +706,14 @@ class IncomingMessage < ActiveRecord::Base c += 1 uudecode_attachment.url_part_number = c uudecode_attachment.save! - attachments << uudecode_attachment.id + attachments << uudecode_attachment end end + attachment_ids = attachments.map{ |attachment| attachment.id } # now get rid of any attachments we no longer have - FoiAttachment.destroy_all("id NOT IN (#{attachments.join(',')}) AND incoming_message_id = #{self.id}") + FoiAttachment.destroy_all(["id NOT IN (?) AND incoming_message_id = ?", + attachment_ids, self.id]) end # Returns body text as HTML with quotes flattened, and emails removed. @@ -682,7 +739,7 @@ class IncomingMessage < ActiveRecord::Base text.strip! # if there is nothing but quoted stuff, then show the subject if text == "FOLDED_QUOTED_SECTION" - text = "[Subject only] " + CGI.escapeHTML(self.subject) + text + text = "[Subject only] " + CGI.escapeHTML(self.subject || '') + text end # and display link for quoted stuff text = text.gsub(/FOLDED_QUOTED_SECTION/, "\n\n" + '<span class="unfold_link"><a href="?unfold=1#incoming-'+self.id.to_s+'">'+_("show quoted sections")+'</a></span>' + "\n\n") @@ -739,20 +796,27 @@ class IncomingMessage < ActiveRecord::Base return self.cached_attachment_text_clipped end - def _get_attachment_text_internal + def _extract_text # Extract text from each attachment - text = '' - attachments = self.get_attachments_for_display - for attachment in attachments - text += MailHandler.get_attachment_text_one_file(attachment.content_type, + self.get_attachments_for_display.reduce(''){ |memo, attachment| + memo += MailHandler.get_attachment_text_one_file(attachment.content_type, attachment.body, attachment.charset) - end - # Remove any bad characters - text = Iconv.conv('utf-8//IGNORE', 'utf-8', text) - return text + } end + def _get_attachment_text_internal + text = self._extract_text + + # Remove any bad characters + if String.method_defined?(:encode) + # handle "problematic" encoding + text.encode!('UTF-16', 'UTF-8', :invalid => :replace, :undef => :replace, :replace => '') + text.encode('UTF-8', 'UTF-16') + else + Iconv.conv('utf-8//IGNORE', 'utf-8', text) + end + end # Returns text for indexing def get_text_for_indexing_full diff --git a/app/models/info_request.rb b/app/models/info_request.rb index cee9eb959..47ad435cb 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -1,44 +1,49 @@ +# encoding: utf-8 # == Schema Information -# Schema version: 20120919140404 +# Schema version: 20131024114346 # # Table name: info_requests # -# id :integer not null, primary key -# title :text not null +# id :integer not null, primary key +# title :text not null # user_id :integer -# public_body_id :integer not null -# created_at :datetime not null -# updated_at :datetime not null -# described_state :string(255) not null -# awaiting_description :boolean default(FALSE), not null -# prominence :string(255) default("normal"), not null -# url_title :text not null -# law_used :string(255) default("foi"), not null -# allow_new_responses_from :string(255) default("anybody"), not null -# handle_rejected_responses :string(255) default("bounce"), not null -# idhash :string(255) not null +# public_body_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# described_state :string(255) not null +# awaiting_description :boolean default(FALSE), not null +# prominence :string(255) default("normal"), not null +# url_title :text not null +# law_used :string(255) default("foi"), not null +# allow_new_responses_from :string(255) default("anybody"), not null +# handle_rejected_responses :string(255) default("bounce"), not null +# idhash :string(255) not null # external_user_name :string(255) # external_url :string(255) -# attention_requested :boolean default(FALSE) -# comments_allowed :boolean default(TRUE), not null +# attention_requested :boolean default(FALSE) +# comments_allowed :boolean default(TRUE), not null +# info_request_batch_id :integer # require 'digest/sha1' class InfoRequest < ActiveRecord::Base - include ActionView::Helpers::UrlHelper - include ActionController::UrlWriter + include Rails.application.routes.url_helpers strip_attributes! validates_presence_of :title, :message => N_("Please enter a summary of your request") - validates_format_of :title, :with => /[a-zA-Z]/, :message => N_("Please write a summary with some text in it"), :if => Proc.new { |info_request| !info_request.title.nil? && !info_request.title.empty? } + # TODO: When we no longer support Ruby 1.8, this can be done with /[[:alpha:]]/ + validates_format_of :title, :with => /[ёЁа-яА-Яa-zA-Zà-üÀ-Ü]/iu, + :message => N_("Please write a summary with some text in it"), + :if => Proc.new { |info_request| !info_request.title.nil? && !info_request.title.empty? } belongs_to :user validate :must_be_internal_or_external belongs_to :public_body, :counter_cache => true - validates_presence_of :public_body_id + belongs_to :info_request_batch + validates_presence_of :public_body_id, :unless => Proc.new { |info_request| info_request.is_batch_request_template? } has_many :outgoing_messages, :order => 'created_at' has_many :incoming_messages, :order => 'created_at' @@ -48,10 +53,11 @@ class InfoRequest < ActiveRecord::Base has_many :comments, :order => 'created_at' has_many :censor_rules, :order => 'created_at desc' has_many :mail_server_logs, :order => 'mail_server_log_done_id' + attr_accessor :is_batch_request_template has_tag_string - named_scope :visible, :conditions => {:prominence => "normal"} + scope :visible, :conditions => {:prominence => "normal"} # user described state (also update in info_request_event, admin_request/edit.rhtml) validate :must_be_valid_state @@ -81,6 +87,11 @@ class InfoRequest < ActiveRecord::Base 'blackhole' # just dump them ] + # only check on create, so existing models with mixed case are allowed + validate :title_formatting, :on => :create + + after_initialize :set_defaults + def self.enumerate_states states = [ 'waiting_response', @@ -104,11 +115,25 @@ class InfoRequest < ActiveRecord::Base states end + # Possible reasons that a request could be reported for administrator attention + def report_reasons + [_("Contains defamatory material"), + _("Not a valid request"), + _("Request for personal information"), + _("Contains personal information"), + _("Vexatious"), + _("Other")] + end + def must_be_valid_state errors.add(:described_state, "is not a valid state") if !InfoRequest.enumerate_states.include? described_state end + def is_batch_request_template? + is_batch_request_template == true + end + # The request must either be internal, in which case it has # a foreign key reference to a User object and no external_url or external_user_name, # or else be external in which case it has no user_id but does have an external_url, @@ -146,9 +171,13 @@ class InfoRequest < ActiveRecord::Base end end + def user_json_for_api + is_external? ? { :name => user_name || _("Anonymous user") } : user.json_for_api + end + @@custom_states_loaded = false begin - if ENV["RAILS_ENV"] != "test" + if !Rails.env.test? require 'customstates' include InfoRequestCustomStates @@custom_states_loaded = true @@ -185,21 +214,6 @@ class InfoRequest < ActiveRecord::Base self.comments.find(:all, :conditions => 'visible') end - # Central function to do all searches - # (Not really the right place to put it, but everything can get it here, and it - # does *mainly* find info requests, via their events, so hey) - def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page) - offset = (page - 1) * per_page - - return ::ActsAsXapian::Search.new( - models, query, - :offset => offset, :limit => per_page, - :sort_by_prefix => order, - :sort_by_ascending => ascending, - :collapse_by_prefix => collapse - ) - end - # If the URL name has changed, then all request: queries will break unless # we update index for every event. Also reindex if prominence changes. after_update :reindex_some_request_events @@ -228,17 +242,6 @@ class InfoRequest < ActiveRecord::Base end end - # For debugging - def InfoRequest.profile_search(query) - t = Time.now.usec - for i in (1..10) - t = Time.now.usec - t - secs = t / 1000000.0 - STDOUT.write secs.to_s + " query " + i.to_s + "\n" - results = InfoRequest.full_search([InfoRequestEvent], query, "created_at", true, nil, 25, 1).results - end - end - public # When name is changed, also change the url name def title=(title) @@ -280,17 +283,11 @@ public # Subject lines for emails about the request def email_subject_request - # XXX pull out this general_register_office specialisation - # into some sort of separate jurisdiction dependent file - if self.public_body.url_name == 'general_register_office' - # without GQ in the subject, you just get an auto response - _('{{law_used_full}} request GQ - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title.html_safe) - else - _('{{law_used_full}} request - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title.html_safe) - end + _('{{law_used_full}} request - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title.html_safe) end + def email_subject_followup(incoming_message = nil) - if incoming_message.nil? || !incoming_message.valid_to_reply_to? + if incoming_message.nil? || !incoming_message.valid_to_reply_to? || !incoming_message.subject 'Re: ' + self.email_subject_request else if incoming_message.subject.match(/^Re:/i) @@ -347,7 +344,10 @@ public # copying an email, and that doesn't matter) def InfoRequest.find_by_incoming_email(incoming_email) id, hash = InfoRequest._extract_id_hash_from_email(incoming_email) - return self.find_by_magic_email(id, hash) + if hash_from_id(id) == hash + # Not using find(id) because we don't exception raised if nothing found + find_by_id(id) + end end # Return list of info requests which *might* be right given email address @@ -391,7 +391,7 @@ public # repeated requests, say once a quarter for time information, then might need to do that. # XXX this *should* also check outgoing message joined to is an initial # request (rather than follow up) - def InfoRequest.find_by_existing_request(title, public_body_id, body) + def InfoRequest.find_existing(title, public_body_id, body) return InfoRequest.find(:first, :conditions => [ "title = ? and public_body_id = ? and outgoing_messages.body = ?", title, public_body_id, body ], :include => [ :outgoing_messages ] ) end @@ -456,7 +456,7 @@ public if !allow if self.handle_rejected_responses == 'bounce' - RequestMailer.deliver_stopped_responses(self, email, raw_email_data) if !is_external? + RequestMailer.stopped_responses(self, email, raw_email_data).deliver if !is_external? elsif self.handle_rejected_responses == 'holding_pen' InfoRequest.holding_pen_request.receive(email, raw_email_data, false, reason) elsif self.handle_rejected_responses == 'blackhole' @@ -474,6 +474,17 @@ public incoming_message = IncomingMessage.new ActiveRecord::Base.transaction do + + # To avoid a deadlock when simultaneously dealing with two + # incoming emails that refer to the same InfoRequest, we + # lock the row for update. In Rails 3.2.0 and later this + # can be done with info_request.with_lock or + # info_request.lock!, but upgrading to that version of + # Rails creates many other problems at the moment. In the + # interim, just use raw SQL to do the SELECT ... FOR UPDATE + raw_sql = "SELECT * FROM info_requests WHERE id = #{self.id} LIMIT 1 FOR UPDATE" + ActiveRecord::Base.connection.execute(raw_sql) + raw_email = RawEmail.new incoming_message.raw_email = raw_email incoming_message.info_request = self @@ -484,13 +495,13 @@ public self.awaiting_description = true params = { :incoming_message_id => incoming_message.id } if !rejected_reason.empty? - params[:rejected_reason] = rejected_reason + params[:rejected_reason] = rejected_reason.to_str end self.log_event("response", params) self.save! end self.info_request_events.each { |event| event.xapian_mark_needs_index } # for the "waiting_classification" index - RequestMailer.deliver_new_response(self, incoming_message) if !is_external? + RequestMailer.new_response(self, incoming_message).deliver if !is_external? end @@ -548,16 +559,28 @@ public end def requires_admin? - return true if InfoRequest.requires_admin_states.include?(described_state) - return false + ['requires_admin', 'error_message', 'attention_requested'].include?(described_state) + end + + # Report this request for administrator attention + def report!(reason, message, user) + ActiveRecord::Base.transaction do + set_described_state('attention_requested', user, "Reason: #{reason}\n\n#{message}") + self.attention_requested = true # tells us if attention has ever been requested + save! + end end # change status, including for last event for later historical purposes - def set_described_state(new_state, set_by = nil) + # described_state should always indicate the current state of the request, as described + # by the request owner (or, in some other cases an admin or other user) + def set_described_state(new_state, set_by = nil, message = "") + old_described_state = described_state ActiveRecord::Base.transaction do self.awaiting_description = false - last_event = self.get_last_event + last_event = self.info_request_events.last last_event.described_state = new_state + self.described_state = new_state last_event.save! self.save! @@ -568,16 +591,23 @@ public if self.requires_admin? # Check there is someone to send the message "from" if !set_by.nil? || !self.user.nil? - RequestMailer.deliver_requires_admin(self, set_by) + RequestMailer.requires_admin(self, set_by, message).deliver end end + + unless set_by.nil? || is_actual_owning_user?(set_by) || described_state == 'attention_requested' + RequestMailer.old_unclassified_updated(self).deliver if !is_external? + end end - # Work out what the situation of the request is. In addition to values of - # self.described_state, can take these two values: + # Work out what state to display for the request on the site. In addition to values of + # self.described_state, can take these values: # waiting_classification # waiting_response_overdue # waiting_response_very_overdue + # (this method adds an assessment of overdueness with respect to the current time to 'waiting_response' + # states, and will return 'waiting_classification' instead of the described_state if the + # awaiting_description flag is set on the request). def calculate_status(cached_value_ok=false) if cached_value_ok && @cached_calculated_status return @cached_calculated_status @@ -596,10 +626,22 @@ public return 'waiting_response' end + + # 'described_state' can be populated on any info_request_event but is only + # ever used in the process populating calculated_state on the + # info_request_event (if it represents a response, outgoing message, edit + # or status update), or previous response or outgoing message events for + # the same request. + # Fill in any missing event states for first response before a description # was made. i.e. We take the last described state in between two responses # (inclusive of earlier), and set it as calculated value for the earlier - # response. + # response. Also set the calculated state for any initial outgoing message, + # follow up, edit or status_update to the described state of that event. + + # Note that the calculated state of the latest info_request_event will + # be used in latest_status based searches and should match the described_state + # of the info_request. def calculate_event_states curr_state = nil for event in self.info_request_events.reverse @@ -633,10 +675,22 @@ public event.save! end - # And we don't want to propogate it to the response itself, + # And we don't want to propagate it to the response itself, # as that might already be set to waiting_clarification / a # success status, which we want to know about. curr_state = nil + elsif !curr_state.nil? && (['edit', 'status_update'].include? event.event_type) + # A status update or edit event should get the same calculated state as described state + # so that the described state is always indexed (and will be the latest_status + # for the request immediately after it has been described, regardless of what + # other request events precede it). This means that request should be correctly included + # in status searches for that status. These events allow the described state to propagate in + # case there is a preceding response that the described state should be applied to. + if event.calculated_state != event.described_state + event.calculated_state = event.described_state + event.last_described_at = Time.now() + event.save! + end end end end @@ -684,7 +738,7 @@ public # last_event_forming_initial_request. There may be more obscure # things, e.g. fees, not properly covered. def date_response_required_by - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::reply_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::reply_late_after_days, AlaveteliConfiguration::working_or_calendar_days) end # This is a long stop - even with UK public interest test extensions, 40 # days is a very long time. @@ -692,10 +746,10 @@ public last_sent = last_event_forming_initial_request if self.public_body.is_school? # schools have 60 working days maximum (even over a long holiday) - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::special_reply_very_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::special_reply_very_late_after_days, AlaveteliConfiguration::working_or_calendar_days) else # public interest test ICO guidance gives 40 working maximum - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::reply_very_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::reply_very_late_after_days, AlaveteliConfiguration::working_or_calendar_days) end end @@ -719,41 +773,30 @@ public self.info_request_events.create!(:event_type => type, :params => params) end - # The last response is the default one people might want to reply to - def get_last_response_event_id - for e in self.info_request_events.reverse - if e.event_type == 'response' - return e.id - end - end - return nil + def public_response_events + self.info_request_events.select{|e| e.response? && e.incoming_message.all_can_view? } + end + # The last public response is the default one people might want to reply to + def get_last_public_response_event_id + get_last_public_response_event.id if get_last_public_response_event end - def get_last_response_event - for e in self.info_request_events.reverse - if e.event_type == 'response' - return e - end - end - return nil + + def get_last_public_response_event + public_response_events.last end - def get_last_response - last_response_event = self.get_last_response_event - if last_response_event.nil? - return nil - else - return last_response_event.incoming_message - end + + def get_last_public_response + get_last_public_response_event.incoming_message if get_last_public_response_event end - # The last outgoing message - def get_last_outgoing_event - for e in self.info_request_events.reverse - if [ 'sent', 'followup_sent' ].include?(e.event_type) - return e - end - end - return nil + def public_outgoing_events + info_request_events.select{|e| e.outgoing? && e.outgoing_message.all_can_view? } + end + + # The last public outgoing message + def get_last_public_outgoing_event + public_outgoing_events.last end # Text from the the initial request, for use in summary display @@ -804,6 +847,10 @@ public end end + def last_update_hash + Digest::SHA1.hexdigest(info_request_events.last.created_at.to_i.to_s + updated_at.to_i.to_s) + end + # Get previous email sent to def get_previous_email_sent_to(info_request_event) last_email = nil @@ -821,46 +868,31 @@ public # Display version of status def InfoRequest.get_status_description(status) - if status == 'waiting_classification' - _("Awaiting classification.") - elsif status == 'waiting_response' - _("Awaiting response.") - elsif status == 'waiting_response_overdue' - _("Delayed.") - elsif status == 'waiting_response_very_overdue' - _("Long overdue.") - elsif status == 'not_held' - _("Information not held.") - elsif status == 'rejected' - _("Refused.") - elsif status == 'partially_successful' - _("Partially successful.") - elsif status == 'successful' - _("Successful.") - elsif status == 'waiting_clarification' - _("Waiting clarification.") - elsif status == 'gone_postal' - _("Handled by post.") - elsif status == 'internal_review' - _("Awaiting internal review.") - elsif status == 'error_message' - _("Delivery error") - elsif status == 'requires_admin' - _("Unusual response.") - elsif status == 'attention_requested' - _("Reported for administrator attention.") - elsif status == 'user_withdrawn' - _("Withdrawn by the requester.") - elsif status == 'vexatious' - _("Considered by administrators as vexatious and hidden from site.") - elsif status == 'not_foi' - _("Considered by administrators as not an FOI request and hidden from site.") + descriptions = { + 'waiting_classification' => _("Awaiting classification."), + 'waiting_response' => _("Awaiting response."), + 'waiting_response_overdue' => _("Delayed."), + 'waiting_response_very_overdue' => _("Long overdue."), + 'not_held' => _("Information not held."), + 'rejected' => _("Refused."), + 'partially_successful' => _("Partially successful."), + 'successful' => _("Successful."), + 'waiting_clarification' => _("Waiting clarification."), + 'gone_postal' => _("Handled by post."), + 'internal_review' => _("Awaiting internal review."), + 'error_message' => _("Delivery error"), + 'requires_admin' => _("Unusual response."), + 'attention_requested' => _("Reported for administrator attention."), + 'user_withdrawn' => _("Withdrawn by the requester."), + 'vexatious' => _("Considered by administrators as vexatious and hidden from site."), + 'not_foi' => _("Considered by administrators as not an FOI request and hidden from site."), + } + if descriptions[status] + descriptions[status] + elsif respond_to?(:theme_display_status) + theme_display_status(status) else - begin - return self.theme_display_status(status) - rescue NoMethodError - raise _("unknown status ") + status - end + raise _("unknown status ") + status end end @@ -895,10 +927,10 @@ public end def InfoRequest.magic_email_for_id(prefix_part, id) - magic_email = Configuration::incoming_email_prefix + magic_email = AlaveteliConfiguration::incoming_email_prefix magic_email += prefix_part + id.to_s magic_email += "-" + InfoRequest.hash_from_id(id) - magic_email += "@" + Configuration::incoming_email_domain + magic_email += "@" + AlaveteliConfiguration::incoming_email_domain return magic_email end @@ -908,42 +940,48 @@ public self.idhash = InfoRequest.hash_from_id(self.id) end - def InfoRequest.hash_from_id(id) - return Digest::SHA1.hexdigest(id.to_s + Configuration::incoming_email_secret)[0,8] + def InfoRequest.create_from_attributes(info_request_atts, outgoing_message_atts, user=nil) + info_request = new(info_request_atts) + default_message_params = { + :status => 'ready', + :message_type => 'initial_request', + :what_doing => 'normal_sort' + } + outgoing_message = OutgoingMessage.new(outgoing_message_atts.merge(default_message_params)) + info_request.outgoing_messages << outgoing_message + outgoing_message.info_request = info_request + info_request.user = user + info_request end - # Called by find_by_incoming_email - and used to be called by separate - # function for envelope from address, until we abandoned it. - def InfoRequest.find_by_magic_email(id, hash) - expected_hash = InfoRequest.hash_from_id(id) - #print "expected: " + expected_hash + "\nhash: " + hash + "\n" - if hash != expected_hash - return nil - else - begin - return self.find(id) - rescue ActiveRecord::RecordNotFound - # so error email is sent to admin, rather than the exception sending weird - # error to the public body. - return nil - end - end + def InfoRequest.hash_from_id(id) + return Digest::SHA1.hexdigest(id.to_s + AlaveteliConfiguration::incoming_email_secret)[0,8] end # Used to find when event last changed - def InfoRequest.last_event_time_clause(event_type=nil) + def InfoRequest.last_event_time_clause(event_type=nil, join_table=nil, join_clause=nil) event_type_clause = '' event_type_clause = " AND info_request_events.event_type = '#{event_type}'" if event_type - "(SELECT created_at - FROM info_request_events + tables = ['info_request_events'] + tables << join_table if join_table + join_clause = "AND #{join_clause}" if join_clause + "(SELECT info_request_events.created_at + FROM #{tables.join(', ')} WHERE info_request_events.info_request_id = info_requests.id #{event_type_clause} + #{join_clause} ORDER BY created_at desc LIMIT 1)" end + def InfoRequest.last_public_response_clause() + join_clause = "incoming_messages.id = info_request_events.incoming_message_id + AND incoming_messages.prominence = 'normal'" + last_event_time_clause('response', 'incoming_messages', join_clause) + end + def InfoRequest.old_unclassified_params(extra_params, include_last_response_time=false) - last_response_created_at = last_event_time_clause('response') + last_response_created_at = last_public_response_clause() age = extra_params[:age_in_days] ? extra_params[:age_in_days].days : OLD_AGE_IN_DAYS params = { :conditions => ["awaiting_description = ? AND #{last_response_created_at} < ? @@ -959,11 +997,21 @@ public def InfoRequest.count_old_unclassified(extra_params={}) params = old_unclassified_params(extra_params) + if extra_params[:conditions] + condition_string = extra_params[:conditions].shift + params[:conditions][0] += " AND #{condition_string}" + params[:conditions] += extra_params[:conditions] + end count(:all, params) end - def InfoRequest.get_random_old_unclassified(limit) + def InfoRequest.get_random_old_unclassified(limit, extra_params) params = old_unclassified_params({}) + if extra_params[:conditions] + condition_string = extra_params[:conditions].shift + params[:conditions][0] += " AND #{condition_string}" + params[:conditions] += extra_params[:conditions] + end params[:limit] = limit params[:order] = "random()" find(:all, params) @@ -986,14 +1034,39 @@ public find(:all, params) end + def InfoRequest.download_zip_dir() + File.join(Rails.root, "cache", "zips", "#{Rails.env}") + end + + def request_dirs + first_three_digits = id.to_s()[0..2] + File.join(first_three_digits.to_s, id.to_s) + end + + def download_zip_dir + File.join(InfoRequest.download_zip_dir, "download", request_dirs) + end + + def make_zip_cache_path(user) + cache_file_dir = File.join(InfoRequest.download_zip_dir(), + "download", + request_dirs, + last_update_hash) + cache_file_suffix = if all_can_view_all_correspondence? + "" + elsif Ability.can_view_with_prominence?('hidden', self, user) + "_hidden" + elsif Ability.can_view_with_prominence?('requester_only', self, user) + "_requester_only" + else + "" + end + File.join(cache_file_dir, "#{url_title}#{cache_file_suffix}.zip") + end + def is_old_unclassified? - return false if is_external? - return false if !awaiting_description - return false if url_title == 'holding_pen' - last_response_event = get_last_response_event - return false unless last_response_event - return false if last_response_event.created_at >= Time.now - OLD_AGE_IN_DAYS - return true + !is_external? && awaiting_description && url_title != 'holding_pen' && get_last_public_response_event && + Time.now > get_last_public_response_event.created_at + OLD_AGE_IN_DAYS end # List of incoming messages to followup, by unique email @@ -1006,6 +1079,8 @@ public end incoming_message.safe_mail_from + next if ! incoming_message.all_can_view? + email = OutgoingMailer.email_for_followup(self, incoming_message) name = OutgoingMailer.name_for_followup(self, incoming_message) @@ -1025,7 +1100,10 @@ public # Get the list of censor rules that apply to this request def applicable_censor_rules - applicable_rules = [self.censor_rules, self.public_body.censor_rules, CensorRule.global.all] + applicable_rules = [self.censor_rules, CensorRule.global.all] + unless is_batch_request_template? + applicable_rules << self.public_body.censor_rules + end if self.user && !self.user.censor_rules.empty? applicable_rules << self.user.censor_rules end @@ -1055,13 +1133,7 @@ public end def user_can_view?(user) - if self.prominence == 'hidden' - return User.view_hidden_requests?(user) - end - if self.prominence == 'requester_only' - return self.is_owning_user?(user) - end - return true + Ability.can_view_with_prominence?(self.prominence, self, user) end # Is this request visible to everyone? @@ -1070,6 +1142,12 @@ public return false end + def all_can_view_all_correspondence? + all_can_view? && + incoming_messages.all?{ |message| message.all_can_view? } && + outgoing_messages.all?{ |message| message.all_can_view? } + end + def indexed_by_search? if self.prominence == 'backpage' || self.prominence == 'hidden' || self.prominence == 'requester_only' return false @@ -1085,25 +1163,6 @@ public InfoRequest.update_all "allow_new_responses_from = 'nobody' where updated_at < (now() - interval '1 year') and allow_new_responses_from in ('anybody', 'authority_only') and url_title <> 'holding_pen'" end - # Returns a random FOI request - def InfoRequest.random - max_id = InfoRequest.connection.select_value('select max(id) as a from info_requests').to_i - info_request = nil - count = 0 - while info_request.nil? - if count > 100 - return nil - end - id = rand(max_id) + 1 - begin - count += 1 - info_request = find(id, :conditions => ["prominence = 'normal'"]) - rescue ActiveRecord::RecordNotFound - end - end - return info_request - end - def json_for_api(deep) ret = { :id => self.id, @@ -1137,7 +1196,7 @@ public before_save :purge_in_cache def purge_in_cache - if !Configuration::varnish_host.blank? && !self.id.nil? + if !AlaveteliConfiguration::varnish_host.blank? && !self.id.nil? # we only do this for existing info_requests (new ones have a nil id) path = url_for(:controller => 'request', :action => 'show', :url_title => self.url_title, :only_path => true, :locale => :none) req = PurgeRequest.find_by_url(path) @@ -1150,10 +1209,151 @@ public end end + after_save :update_counter_cache + after_destroy :update_counter_cache + # This method updates the count columns of the PublicBody that + # store the number of "not held", "to some extent successful" and + # "both visible and classified" requests when saving or destroying + # an InfoRequest associated with the body: + def update_counter_cache + PublicBody.skip_callback(:save, :after, :purge_in_cache) + basic_params = { + :public_body_id => self.public_body_id, + :awaiting_description => false, + :prominence => 'normal' + } + [['info_requests_not_held_count', {:described_state => 'not_held'}], + ['info_requests_successful_count', {:described_state => ['successful', 'partially_successful']}], + ['info_requests_visible_classified_count', {}]].each do |column, extra_params| + params = basic_params.clone.update extra_params + self.public_body.send "#{column}=", InfoRequest.where(params).count + end + self.public_body.without_revision do + public_body.no_xapian_reindex = true + public_body.save + end + PublicBody.set_callback(:save, :after, :purge_in_cache) + end + def for_admin_column self.class.content_columns.map{|c| c unless %w(title url_title).include?(c.name) }.compact.each do |column| yield(column.human_name, self.send(column.name), column.type.to_s, column.name) end end + + + # Get requests that have similar important terms + def similar_requests(limit=10) + xapian_similar = nil + xapian_similar_more = false + begin + xapian_similar = ActsAsXapian::Similar.new([InfoRequestEvent], + info_request_events, + :limit => limit, + :collapse_by_prefix => 'request_collapse') + xapian_similar_more = (xapian_similar.matches_estimated > limit) + rescue + end + return [xapian_similar, xapian_similar_more] + end + + def InfoRequest.request_list(filters, page, per_page, max_results) + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], + InfoRequestEvent.make_query_from_params(filters), + :offset => (page - 1) * per_page, + :limit => 25, + :sort_by_prefix => 'created_at', + :sort_by_ascending => true, + :collapse_by_prefix => 'request_collapse' + ) + list_results = xapian_object.results.map { |r| r[:model] } + matches_estimated = xapian_object.matches_estimated + show_no_more_than = [matches_estimated, max_results].min + return { :results => list_results, + :matches_estimated => matches_estimated, + :show_no_more_than => show_no_more_than } + end + + def InfoRequest.recent_requests + request_events = [] + request_events_all_successful = false + # Get some successful requests + begin + query = 'variety:response (status:successful OR status:partially_successful)' + sortby = "newest" + max_count = 5 + + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], + query, + :offset => 0, + :limit => 5, + :sort_by_prefix => 'created_at', + :sort_by_ascending => true, + :collapse_by_prefix => 'request_title_collapse' + ) + xapian_object.results + request_events = xapian_object.results.map { |r| r[:model] } + + # If there are not yet enough successful requests, fill out the list with + # other requests + if request_events.count < max_count + query = 'variety:sent' + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], + query, + :offset => 0, + :limit => max_count-request_events.count, + :sort_by_prefix => 'created_at', + :sort_by_ascending => true, + :collapse_by_prefix => 'request_title_collapse' + ) + xapian_object.results + more_events = xapian_object.results.map { |r| r[:model] } + request_events += more_events + # Overall we still want the list sorted with the newest first + request_events.sort!{|e1,e2| e2.created_at <=> e1.created_at} + else + request_events_all_successful = true + end + rescue + request_events = [] + end + + return [request_events, request_events_all_successful] + end + + def InfoRequest.find_in_state(state) + find(:all, :select => '*, ' + last_event_time_clause + ' as last_event_time', + :conditions => ["described_state = ?", state], + :order => "last_event_time") + end + + private + + def set_defaults + begin + if self.described_state.nil? + self.described_state = 'waiting_response' + end + rescue ActiveModel::MissingAttributeError + # this should only happen on Model.exists?() call. It can be safely ignored. + # See http://www.tatvartha.com/2011/03/activerecordmissingattributeerror-missing-attribute-a-bug-or-a-features/ + end + # FOI or EIR? + if !self.public_body.nil? && self.public_body.eir_only? + self.law_used = 'eir' + end + end + + def title_formatting + if !self.title.nil? && !MySociety::Validate.uses_mixed_capitals(self.title, 10) + errors.add(:title, _('Please write the summary using a mixture of capital and lower case letters. This makes it easier for others to read.')) + end + if !self.title.nil? && title.size > 200 + errors.add(:title, _('Please keep the summary short, like in the subject of an email. You can use a phrase, rather than a full sentence.')) + end + if !self.title.nil? && self.title =~ /^(FOI|Freedom of Information)\s*requests?$/i + errors.add(:title, _('Please describe more what the request is about in the subject. There is no need to say it is an FOI request, we add that on anyway.')) + end + end end diff --git a/app/models/info_request_batch.rb b/app/models/info_request_batch.rb new file mode 100644 index 000000000..498ab4951 --- /dev/null +++ b/app/models/info_request_batch.rb @@ -0,0 +1,73 @@ +# == Schema Information +# Schema version: 20131024114346 +# +# Table name: info_request_batches +# +# id :integer not null, primary key +# title :text not null +# user_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +class InfoRequestBatch < ActiveRecord::Base + has_many :info_requests + belongs_to :user + has_and_belongs_to_many :public_bodies + + validates_presence_of :user + validates_presence_of :title + validates_presence_of :body + + # When constructing a new batch, use this to check user hasn't double submitted. + def InfoRequestBatch.find_existing(user, title, body, public_body_ids) + find(:first, :conditions => ['user_id = ? + AND title = ? + AND body = ? + AND info_request_batches_public_bodies.public_body_id in (?)', + user, title, body, public_body_ids], + :include => :public_bodies) + end + + # Create a batch of information requests, returning a list of public bodies + # that are unrequestable from the initial list of public body ids passed. + def create_batch! + unrequestable = [] + created = [] + ActiveRecord::Base.transaction do + public_bodies.each do |public_body| + if public_body.is_requestable? + created << create_request!(public_body) + else + unrequestable << public_body + end + end + self.sent_at = Time.now + self.save! + end + created.each{ |info_request| info_request.outgoing_messages.first.send_message } + + return unrequestable + end + + # Create and send an FOI request to a public body + def create_request!(public_body) + body = OutgoingMessage.fill_in_salutation(self.body, public_body) + info_request = InfoRequest.create_from_attributes({:title => self.title}, + {:body => body}, + self.user) + info_request.public_body_id = public_body.id + info_request.info_request_batch = self + info_request.save! + info_request + end + + def InfoRequestBatch.send_batches() + find_each(:conditions => "sent_at IS NULL") do |info_request_batch| + unrequestable = info_request_batch.create_batch! + mail_message = InfoRequestBatchMailer.batch_sent(info_request_batch, + unrequestable, + info_request_batch.user).deliver + end + end +end diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb index 09eba31ab..5eed5ba83 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -1,28 +1,29 @@ # == Schema Information -# Schema version: 114 # # Table name: info_request_events # -# id :integer not null, primary key -# info_request_id :integer not null -# event_type :text not null -# params_yaml :text not null -# created_at :datetime not null +# id :integer not null, primary key +# info_request_id :integer not null +# event_type :text not null +# params_yaml :text not null +# created_at :datetime not null # described_state :string(255) # calculated_state :string(255) # last_described_at :datetime # incoming_message_id :integer # outgoing_message_id :integer # comment_id :integer -# prominence :string(255) default("normal"), not null # # models/info_request_event.rb: # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class InfoRequestEvent < ActiveRecord::Base + + extend XapianQueries + belongs_to :info_request validates_presence_of :info_request @@ -48,10 +49,10 @@ class InfoRequestEvent < ActiveRecord::Base 'destroy_incoming', # deleted an incoming message (in admin interface) 'destroy_outgoing', # deleted an outgoing message (in admin interface) 'redeliver_incoming', # redelivered an incoming message elsewhere (in admin interface) + 'edit_incoming', # incoming message edited (in admin interface) 'move_request', # changed user or public body (in admin interface) 'hide', # hid a request (in admin interface) 'manual', # you did something in the db by hand - 'response', 'comment', 'status_update' @@ -63,34 +64,12 @@ class InfoRequestEvent < ActiveRecord::Base # user described state (also update in info_request) validate :must_be_valid_state - # whether event is publicly visible - validates_inclusion_of :prominence, :in => [ - 'normal', - 'hidden', - 'requester_only' - ] - def must_be_valid_state if !described_state.nil? and !InfoRequest.enumerate_states.include?(described_state) errors.add(described_state, "is not a valid state") end end - def user_can_view?(user) - if !self.info_request.user_can_view?(user) - raise "internal error, called user_can_view? on event when there is not permission to view entire request" - end - - if self.prominence == 'hidden' - return User.view_hidden_requests?(user) - end - if self.prominence == 'requester_only' - return self.info_request.is_owning_user?(user) - end - return true - end - - # Full text search indexing acts_as_xapian :texts => [ :search_text_main, :title ], :values => [ @@ -259,6 +238,12 @@ class InfoRequestEvent < ActiveRecord::Base if !self.info_request.indexed_by_search? return false end + if self.event_type == 'response' && !self.incoming_message.indexed_by_search? + return false + end + if ['sent', 'followup_sent'].include?(self.event_type) && !self.outgoing_message.indexed_by_search? + return false + end if self.event_type == 'comment' && !self.comment.visible return false end @@ -267,6 +252,7 @@ class InfoRequestEvent < ActiveRecord::Base return false end end + def variety self.event_type end @@ -356,6 +342,9 @@ class InfoRequestEvent < ActiveRecord::Base end raise _("unknown status ") + status end + # TRANSLATORS: "Follow up" in this context means a further + # message sent by the requester to the authority after + # the initial request return _("Follow up") end @@ -363,16 +352,19 @@ class InfoRequestEvent < ActiveRecord::Base end def is_sent_sort? - if [ 'sent', 'resent'].include?(self.event_type) - return true - end - return false + ['sent', 'resent'].include?(event_type) end + def is_followup_sort? - if [ 'followup_sent', 'followup_resent'].include?(self.event_type) - return true - end - return false + ['followup_sent', 'followup_resent'].include?(event_type) + end + + def outgoing? + ['sent', 'followup_sent'].include?(event_type) + end + + def response? + event_type == 'response' end def same_email_as_previous_send? @@ -401,7 +393,7 @@ class InfoRequestEvent < ActiveRecord::Base :comment_id => self.comment_id, # XXX would be nice to add links here, but alas the - # code to make them is in views only. See views/request/details.rhtml + # code to make them is in views only. See views/request/details.html.erb # perhaps can call with @template somehow } @@ -416,7 +408,7 @@ class InfoRequestEvent < ActiveRecord::Base if deep ret[:info_request] = self.info_request.json_for_api(false) ret[:public_body] = self.info_request.public_body.json_for_api - ret[:user] = self.info_request.user.json_for_api + ret[:user] = self.info_request.user_json_for_api end return ret @@ -427,4 +419,5 @@ class InfoRequestEvent < ActiveRecord::Base yield(column.human_name, self.send(column.name), column.type.to_s, column.name) end end + end diff --git a/app/models/mail_server_log.rb b/app/models/mail_server_log.rb index 755584b90..0e5b60ff1 100644 --- a/app/models/mail_server_log.rb +++ b/app/models/mail_server_log.rb @@ -1,23 +1,20 @@ # == Schema Information -# Schema version: 20121010214348 # # Table name: mail_server_logs # -# id :integer not null, primary key +# id :integer not null, primary key # mail_server_log_done_id :integer # info_request_id :integer -# order :integer not null -# line :text not null -# created_at :datetime not null -# updated_at :datetime not null +# order :integer not null +# line :text not null +# created_at :datetime not null +# updated_at :datetime not null # # We load log file lines for requests in here, for display in the admin interface. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ -# -# $Id: exim_log.rb,v 1.14 2009-09-17 21:10:05 francis Exp $ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class MailServerLog < ActiveRecord::Base belongs_to :info_request @@ -53,7 +50,7 @@ class MailServerLog < ActiveRecord::Base done.save! f = is_gz ? Zlib::GzipReader.open(file_name) : File.open(file_name, 'r') - case(Configuration::mta_log_type.to_sym) + case(AlaveteliConfiguration::mta_log_type.to_sym) when :exim load_exim_log_data(f, done) when :postfix @@ -123,13 +120,13 @@ class MailServerLog < ActiveRecord::Base # We also check the email prefix so that we could, for instance, separately handle a staging and production # instance running on the same server with different email prefixes. def MailServerLog.email_addresses_on_line(line) - prefix = Regexp::quote(Configuration::incoming_email_prefix) - domain = Regexp::quote(Configuration::incoming_email_domain) + prefix = Regexp::quote(AlaveteliConfiguration::incoming_email_prefix) + domain = Regexp::quote(AlaveteliConfiguration::incoming_email_domain) line.scan(/#{prefix}request-[^\s]+@#{domain}/).sort.uniq end def MailServerLog.request_sent?(ir) - case(Configuration::mta_log_type.to_sym) + case(AlaveteliConfiguration::mta_log_type.to_sym) when :exim request_exim_sent?(ir) when :postfix diff --git a/app/models/mail_server_log_done.rb b/app/models/mail_server_log_done.rb index 3fb20f0b3..222b072c5 100644 --- a/app/models/mail_server_log_done.rb +++ b/app/models/mail_server_log_done.rb @@ -1,19 +1,18 @@ # == Schema Information -# Schema version: 20121010214348 # # Table name: mail_server_log_dones # -# id :integer not null, primary key -# filename :text not null -# last_stat :datetime not null -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# filename :text not null +# last_stat :datetime not null +# created_at :datetime not null +# updated_at :datetime not null # # Stores that a particular mail server log file has been loaded in, see mail_server_log.rb # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class MailServerLogDone < ActiveRecord::Base has_many :mail_server_logs diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index c75894e6a..a435511d3 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -1,18 +1,20 @@ # == Schema Information -# Schema version: 114 +# Schema version: 20131024114346 # # Table name: outgoing_messages # -# id :integer not null, primary key -# info_request_id :integer not null -# body :text not null -# status :string(255) not null -# message_type :string(255) not null -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# info_request_id :integer not null +# body :text not null +# status :string(255) not null +# message_type :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null # last_sent_at :datetime # incoming_message_followup_id :integer -# what_doing :string(255) not null +# what_doing :string(255) not null +# prominence :string(255) default("normal"), not null +# prominence_reason :text # # models/outgoing_message.rb: @@ -20,11 +22,22 @@ # else. e.g. An initial request for information, or a complaint. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class OutgoingMessage < ActiveRecord::Base + extend MessageProminence + include Rails.application.routes.url_helpers + include LinkToHelper + self.default_url_options[:host] = AlaveteliConfiguration::domain + # https links in emails if forcing SSL + if AlaveteliConfiguration::force_ssl + self.default_url_options[:protocol] = "https" + end + strip_attributes! + has_prominence + belongs_to :info_request validates_presence_of :info_request @@ -51,17 +64,34 @@ class OutgoingMessage < ActiveRecord::Base end end + after_initialize :set_default_letter + # How the default letter starts and ends def get_salutation + if self.info_request.is_batch_request_template? + return OutgoingMessage.placeholder_salutation + end ret = "" if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? ret = ret + OutgoingMailer.name_for_followup(self.info_request, self.incoming_message_followup) else - ret = ret + self.info_request.public_body.name + return OutgoingMessage.default_salutation(self.info_request.public_body) end salutation = _("Dear {{public_body_name}},", :public_body_name => ret) end + def OutgoingMessage.default_salutation(public_body) + _("Dear {{public_body_name}},", :public_body_name => public_body.name) + end + + def OutgoingMessage.placeholder_salutation + _("Dear [Authority name],") + end + + def OutgoingMessage.fill_in_salutation(body, public_body) + body.gsub(placeholder_salutation, default_salutation(public_body)) + end + def get_signoff if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? return _("Yours sincerely,") @@ -78,15 +108,15 @@ class OutgoingMessage < ActiveRecord::Base end if self.what_doing == 'internal_review' - "Please pass this on to the person who conducts Freedom of Information reviews." + + _("Please pass this on to the person who conducts Freedom of Information reviews.") + "\n\n" + - "I am writing to request an internal review of " + - self.info_request.public_body.name + - "'s handling of my FOI request " + - "'" + self.info_request.title + "'." + + _("I am writing to request an internal review of {{public_body_name}}'s handling of my FOI request '{{info_request_title}}'.", + :public_body_name => self.info_request.public_body.name, + :info_request_title => self.info_request.title) + "\n\n\n\n [ " + self.get_internal_review_insert_here_note + " ] \n\n\n\n" + - "A full history of my FOI request and all correspondence is available on the Internet at this address:\n" + - "http://" + Configuration::domain + "/request/" + self.info_request.url_title + _("A full history of my FOI request and all correspondence is available on the Internet at this address: {{url}}", + :url => request_url(self.info_request)) + + "\n" else "" end @@ -130,13 +160,6 @@ class OutgoingMessage < ActiveRecord::Base MySociety::Validate.contains_postcode?(self.body) end - # Set default letter - def after_initialize - if self.body.nil? - self.body = get_default_message - end - end - # Deliver outgoing message # Note: You can test this from script/console with, say: # InfoRequest.find(1).outgoing_messages[0].send_message @@ -147,7 +170,7 @@ class OutgoingMessage < ActiveRecord::Base self.status = 'sent' self.save! - mail_message = OutgoingMailer.deliver_initial_request(self.info_request, self) + mail_message = OutgoingMailer.initial_request(self.info_request, self).deliver self.info_request.log_event(log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, @@ -159,7 +182,7 @@ class OutgoingMessage < ActiveRecord::Base self.status = 'sent' self.save! - mail_message = OutgoingMailer.deliver_followup(self.info_request, self, self.incoming_message_followup) + mail_message = OutgoingMailer.followup(self.info_request, self, self.incoming_message_followup).deliver self.info_request.log_event('followup_' + log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, @@ -206,11 +229,11 @@ class OutgoingMessage < ActiveRecord::Base end # Returns text for indexing / text display - def get_text_for_indexing + def get_text_for_indexing(strip_salutation=true) text = self.body.strip # Remove salutation - text.sub!(/Dear .+,/, "") + text.sub!(/Dear .+,/, "") if strip_salutation # Remove email addresses from display/index etc. self.remove_privacy_sensitive_things!(text) @@ -230,6 +253,12 @@ class OutgoingMessage < ActiveRecord::Base return text.html_safe end + # Return body for display as text + def get_body_for_text_display + get_text_for_indexing(strip_salutation=false) + end + + def fully_destroy ActiveRecord::Base.transaction do info_request_event = InfoRequestEvent.find_by_outgoing_message_id(self.id) @@ -253,8 +282,14 @@ class OutgoingMessage < ActiveRecord::Base private + def set_default_letter + if self.body.nil? + self.body = get_default_message + end + end + def format_of_body - if self.body.empty? || self.body =~ /\A#{get_salutation}\s+#{get_signoff}/ || self.body =~ /#{get_internal_review_insert_here_note}/ + if self.body.empty? || self.body =~ /\A#{Regexp.escape(get_salutation)}\s+#{Regexp.escape(get_signoff)}/ || self.body =~ /#{Regexp.escape(get_internal_review_insert_here_note)}/ if self.message_type == 'followup' if self.what_doing == 'internal_review' errors.add(:body, _("Please give details explaining why you want a review")) @@ -268,7 +303,7 @@ class OutgoingMessage < ActiveRecord::Base end end if self.body =~ /#{get_signoff}\s*\Z/m - errors.add(:body, _("Please sign at the bottom with your name, or alter the \"%{signoff}\" signature" % { :signoff => get_signoff })) + errors.add(:body, _("Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature", :signoff => get_signoff)) end if !MySociety::Validate.uses_mixed_capitals(self.body) errors.add(:body, _('Please write your message using a mixture of capital and lower case letters. This makes it easier for others to read.')) diff --git a/app/models/post_redirect.rb b/app/models/post_redirect.rb index 31f08c21a..5da3d2742 100644 --- a/app/models/post_redirect.rb +++ b/app/models/post_redirect.rb @@ -1,18 +1,17 @@ # == Schema Information -# Schema version: 114 # # Table name: post_redirects # -# id :integer not null, primary key -# token :text not null -# uri :text not null +# id :integer not null, primary key +# token :text not null +# uri :text not null # post_params_yaml :text -# created_at :datetime not null -# updated_at :datetime not null -# email_token :text not null +# created_at :datetime not null +# updated_at :datetime not null +# email_token :text not null # reason_params_yaml :text # user_id :integer -# circumstance :text default("normal"), not null +# circumstance :text default("normal"), not null # # models/post_redirect.rb: @@ -24,7 +23,7 @@ # fakes the redirect to include POST parameters in request later. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'openssl' # for random bytes function @@ -32,6 +31,8 @@ class PostRedirect < ActiveRecord::Base # Optional, does a login confirm before redirect for use in email links. belongs_to :user + after_initialize :generate_token + # We store YAML version of POST parameters in the database def post_params=(params) self.post_params_yaml = params.to_yaml @@ -62,18 +63,6 @@ class PostRedirect < ActiveRecord::Base MySociety::Util.generate_token end - # Make the token - def after_initialize - # The token is used to return you to what you are doing after the login form. - if not self.token - self.token = PostRedirect.generate_random_token - end - # There is a separate token to use in the URL if we send a confirmation email. - if not self.email_token - self.email_token = PostRedirect.generate_random_token - end - end - # Used by (rspec) test code only def self.get_last_post_redirect # XXX yeuch - no other easy way of getting the token so we can check @@ -89,6 +78,18 @@ class PostRedirect < ActiveRecord::Base PostRedirect.delete_all "updated_at < (now() - interval '2 months')" end + private + + def generate_token + # The token is used to return you to what you are doing after the login form. + if not self.token + self.token = PostRedirect.generate_random_token + end + # There is a separate token to use in the URL if we send a confirmation email. + if not self.email_token + self.email_token = PostRedirect.generate_random_token + end + end end diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb index 73d7ca12b..6c3b2cfa0 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -1,19 +1,18 @@ # == Schema Information -# Schema version: 114 # # Table name: profile_photos # -# id :integer not null, primary key -# data :binary not null +# id :integer not null, primary key +# data :binary not null # user_id :integer -# draft :boolean default(FALSE), not null +# draft :boolean default(FALSE), not null # # models/profile_photo.rb: # Image of user that goes on their profile. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class ProfilePhoto < ActiveRecord::Base WIDTH = 96 @@ -29,25 +28,9 @@ class ProfilePhoto < ActiveRecord::Base attr_accessor :x, :y, :w, :h - # convert binary data blob into ImageMagick image when assigned attr_accessor :image - def after_initialize - if data.nil? - self.image = nil - return - end - - image_list = Magick::ImageList.new - begin - image_list.from_blob(data) - rescue Magick::ImageMagickError - self.image = nil - return - end - self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file? - self.convert_image - end + after_initialize :convert_data_to_image # make image valid format and size def convert_image @@ -87,21 +70,25 @@ class ProfilePhoto < ActiveRecord::Base def data_and_draft_checks if self.data.nil? - errors.add(:data, N_("Please choose a file containing your photo.")) + errors.add(:data, _("Please choose a file containing your photo.")) return end if self.image.nil? - errors.add(:data, N_("Couldn't understand the image file that you uploaded. PNG, JPEG, GIF and many other common image file formats are supported.")) + errors.add(:data, _("Couldn't understand the image file that you uploaded. PNG, JPEG, GIF and many other common image file formats are supported.")) return end if self.image.format != 'PNG' - errors.add(:data, N_("Failed to convert image to a PNG")) + errors.add(:data, _("Failed to convert image to a PNG")) end if !self.draft && (self.image.columns != WIDTH || self.image.rows != HEIGHT) - errors.add(:data, N_("Failed to convert image to the correct size: at %{cols}x%{rows}, need %{width}x%{height}" % { :cols => self.image.columns, :rows => self.image.rows, :width => WIDTH, :height => HEIGHT })) + errors.add(:data, _("Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}", + :cols => self.image.columns, + :rows => self.image.rows, + :width => WIDTH, + :height => HEIGHT)) end if self.draft && self.user_id @@ -112,6 +99,25 @@ class ProfilePhoto < ActiveRecord::Base raise "Internal error, real pictures must have a user" end end + + # Convert binary data blob into ImageMagick image when assigned + def convert_data_to_image + if data.nil? + self.image = nil + return + end + + image_list = Magick::ImageList.new + begin + image_list.from_blob(data) + rescue Magick::ImageMagickError + self.image = nil + return + end + + self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file? + self.convert_image + end end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index 168b9f4c7..7b1ded820 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -1,27 +1,32 @@ +# -*- coding: utf-8 -*- # == Schema Information -# Schema version: 20120919140404 +# Schema version: 20131024114346 # # Table name: public_bodies # -# id :integer not null, primary key -# name :text not null -# short_name :text not null -# request_email :text not null -# version :integer not null -# last_edit_editor :string(255) not null -# last_edit_comment :text not null -# created_at :datetime not null -# updated_at :datetime not null -# url_name :text not null -# home_page :text default(""), not null -# notes :text default(""), not null -# first_letter :string(255) not null -# publication_scheme :text default(""), not null -# api_key :string(255) not null -# info_requests_count :integer default(0), not null +# id :integer not null, primary key +# name :text not null +# short_name :text not null +# request_email :text not null +# version :integer not null +# last_edit_editor :string(255) not null +# last_edit_comment :text not null +# created_at :datetime not null +# updated_at :datetime not null +# url_name :text not null +# home_page :text default(""), not null +# notes :text default(""), not null +# first_letter :string(255) not null +# publication_scheme :text default(""), not null +# api_key :string(255) not null +# info_requests_count :integer default(0), not null +# disclosure_log :text default(""), not null +# info_requests_successful_count :integer +# info_requests_not_held_count :integer +# info_requests_overdue_count :integer +# info_requests_visible_classified_count :integer # -# -*- coding: utf-8 -*- require 'csv' require 'securerandom' require 'set' @@ -40,12 +45,13 @@ class PublicBody < ActiveRecord::Base has_many :info_requests, :order => 'created_at desc' has_many :track_things, :order => 'created_at desc' has_many :censor_rules, :order => 'created_at desc' + attr_accessor :no_xapian_reindex has_tag_string before_save :set_api_key, :set_default_publication_scheme # Every public body except for the internal admin one is visible - named_scope :visible, lambda { + scope :visible, lambda { { :conditions => "public_bodies.id <> #{PublicBody.internal_admin_body.id}" } @@ -54,18 +60,29 @@ class PublicBody < ActiveRecord::Base translates :name, :short_name, :request_email, :url_name, :notes, :first_letter, :publication_scheme # Convenience methods for creating/editing translations via forms - def translation(locale) + def find_translation_by_locale(locale) self.translations.find_by_locale(locale) end # XXX - Don't like repeating this! def calculate_cached_fields(t) - t.first_letter = t.name.scan(/^./mu)[0].upcase unless t.name.nil? or t.name.empty? + PublicBody.set_first_letter(t) short_long_name = t.name short_long_name = t.short_name if t.short_name and !t.short_name.empty? t.url_name = MySociety::Format.simplify_url_part(short_long_name, 'body') end + # Set the first letter on a public body or translation + def PublicBody.set_first_letter(instance) + unless instance.name.nil? or instance.name.empty? + # we use a regex to ensure it works with utf-8/multi-byte + first_letter = Unicode.upcase instance.name.scan(/^./mu)[0] + if first_letter != instance.first_letter + instance.first_letter = first_letter + end + end + end + def translated_versions translations end @@ -79,7 +96,7 @@ class PublicBody < ActiveRecord::Base if translation_attrs.respond_to? :each_value # Hash => updating translation_attrs.each_value do |attrs| next if skip?(attrs) - t = translation(attrs[:locale]) || PublicBody::Translation.new + t = translation_for(attrs[:locale]) || PublicBody::Translation.new t.attributes = attrs calculate_cached_fields(t) t.save! @@ -106,35 +123,31 @@ class PublicBody < ActiveRecord::Base # like find_by_url_name but also search historic url_name if none found def self.find_by_url_name_with_historic(name) - locale = self.locale || I18n.locale - PublicBody.with_locale(locale) do - found = PublicBody.find(:all, - :conditions => ["public_body_translations.url_name=?", name], - :joins => :translations, - :readonly => false) - # If many bodies are found (usually because the url_name is the same across - # locales) return any of them - return found.first if found.size >= 1 - - # If none found, then search the history of short names - old = PublicBody::Version.find_all_by_url_name(name) - # Find unique public bodies in it - old = old.map { |x| x.public_body_id } - old = old.uniq - # Maybe return the first one, so we show something relevant, - # rather than throwing an error? - raise "Two bodies with the same historical URL name: #{name}" if old.size > 1 - return unless old.size == 1 - # does acts_as_versioned provide a method that returns the current version? - return PublicBody.find(old.first) - end + found = PublicBody.find(:all, + :conditions => ["public_body_translations.url_name=?", name], + :joins => :translations, + :readonly => false) + # If many bodies are found (usually because the url_name is the same across + # locales) return any of them + return found.first if found.size >= 1 + + # If none found, then search the history of short names + old = PublicBody::Version.find_all_by_url_name(name) + # Find unique public bodies in it + old = old.map { |x| x.public_body_id } + old = old.uniq + # Maybe return the first one, so we show something relevant, + # rather than throwing an error? + raise "Two bodies with the same historical URL name: #{name}" if old.size > 1 + return unless old.size == 1 + # does acts_as_versioned provide a method that returns the current version? + return PublicBody.find(old.first) end # Set the first letter, which is used for faster queries before_save(:set_first_letter) def set_first_letter - # we use a regex to ensure it works with utf-8/multi-byte - self.first_letter = self.name.scan(/./mu)[0].upcase + PublicBody.set_first_letter(self) end # If tagged "not_apply", then FOI/EIR no longer applies to authority at all @@ -180,9 +193,13 @@ class PublicBody < ActiveRecord::Base end acts_as_versioned - self.non_versioned_columns << 'created_at' << 'updated_at' << 'first_letter' << 'api_key' << 'info_requests_count' + self.non_versioned_columns << 'created_at' << 'updated_at' << 'first_letter' << 'api_key' + self.non_versioned_columns << 'info_requests_count' << 'info_requests_successful_count' + self.non_versioned_columns << 'info_requests_count' << 'info_requests_visible_classified_count' + self.non_versioned_columns << 'info_requests_not_held_count' << 'info_requests_overdue' + self.non_versioned_columns << 'info_requests_overdue_count' + class Version - attr_accessor :created_at def last_edit_comment_for_html_display text = self.last_edit_comment.strip @@ -234,6 +251,7 @@ class PublicBody < ActiveRecord::Base def reindex_requested_from if self.changes.include?('url_name') for info_request in self.info_requests + for info_request_event in info_request.info_request_events info_request_event.xapian_mark_needs_index end @@ -243,13 +261,13 @@ class PublicBody < ActiveRecord::Base # When name or short name is changed, also change the url name def short_name=(short_name) - globalize.write(self.class.locale || I18n.locale, :short_name, short_name) + globalize.write(Globalize.locale, :short_name, short_name) self[:short_name] = short_name self.update_url_name end def name=(name) - globalize.write(self.class.locale || I18n.locale, :name, name) + globalize.write(Globalize.locale, :name, name) self[:name] = name self.update_url_name end @@ -329,22 +347,26 @@ class PublicBody < ActiveRecord::Base # The "internal admin" is a special body for internal use. def PublicBody.internal_admin_body - PublicBody.with_locale(I18n.default_locale) do - pb = PublicBody.find_by_url_name("internal_admin_authority") - if pb.nil? - pb = PublicBody.new( - :name => 'Internal admin authority', - :short_name => "", - :request_email => Configuration::contact_email, - :home_page => "", - :notes => "", - :publication_scheme => "", - :last_edit_editor => "internal_admin", - :last_edit_comment => "Made by PublicBody.internal_admin_body" - ) - pb.save! + # Use find_by_sql to avoid the search being specific to a + # locale, since url_name is a translated field: + sql = "SELECT * FROM public_bodies WHERE url_name = 'internal_admin_authority'" + matching_pbs = PublicBody.find_by_sql sql + case + when matching_pbs.empty? then + I18n.with_locale(I18n.default_locale) do + PublicBody.create!(:name => 'Internal admin authority', + :short_name => "", + :request_email => AlaveteliConfiguration::contact_email, + :home_page => "", + :notes => "", + :publication_scheme => "", + :last_edit_editor => "internal_admin", + :last_edit_comment => "Made by PublicBody.internal_admin_body") end - return pb + when matching_pbs.length == 1 then + matching_pbs[0] + else + raise "Multiple public bodies (#{matching_pbs.length}) found with url_name 'internal_admin_authority'" end end @@ -352,10 +374,24 @@ class PublicBody < ActiveRecord::Base class ImportCSVDryRun < StandardError end - # Import from CSV. Just tests things and returns messages if dry_run is true. - # Returns an array of [array of errors, array of notes]. If there are errors, - # always rolls back (as with dry_run). + # Import from a string in CSV format. + # Just tests things and returns messages if dry_run is true. + # Returns an array of [array of errors, array of notes]. If there + # are errors, always rolls back (as with dry_run). def self.import_csv(csv, tag, tag_behaviour, dry_run, editor, available_locales = []) + tmp_csv = nil + Tempfile.open('alaveteli') do |f| + f.write csv + tmp_csv = f + end + PublicBody.import_csv_from_file(tmp_csv.path, tag, tag_behaviour, dry_run, editor, available_locales) + end + + # Import from a CSV file. + # Just tests things and returns messages if dry_run is true. + # Returns an array of [array of errors, array of notes]. If there + # are errors, always rolls back (as with dry_run). + def self.import_csv_from_file(csv_filename, tag, tag_behaviour, dry_run, editor, available_locales = []) errors = [] notes = [] available_locales = [I18n.default_locale] if available_locales.empty? @@ -367,7 +403,7 @@ class PublicBody < ActiveRecord::Base # of updating them bodies_by_name = {} set_of_existing = Set.new() - PublicBody.with_locale(I18n.default_locale) do + I18n.with_locale(I18n.default_locale) do bodies = (tag.nil? || tag.empty?) ? PublicBody.find(:all) : PublicBody.find_by_tag(tag) for existing_body in bodies # Hide InternalAdminBody from import notes @@ -381,7 +417,8 @@ class PublicBody < ActiveRecord::Base set_of_importing = Set.new() field_names = { 'name'=>1, 'request_email'=>2 } # Default values in case no field list is given line = 0 - CSV.parse(csv) do |row| + + CSV.foreach(csv_filename) do |row| line = line + 1 # Parse the first line as a field list if it starts with '#' @@ -394,6 +431,8 @@ class PublicBody < ActiveRecord::Base fields = {} field_names.each{|name, i| fields[name] = row[i]} + yield line, fields if block_given? + name = row[field_names['name']] email = row[field_names['request_email']] next if name.nil? @@ -410,7 +449,7 @@ class PublicBody < ActiveRecord::Base if public_body = bodies_by_name[name] # Existing public body available_locales.each do |locale| - PublicBody.with_locale(locale) do + I18n.with_locale(locale) do changed = ActiveSupport::OrderedHash.new field_list.each do |field_name| localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}" @@ -445,7 +484,7 @@ class PublicBody < ActiveRecord::Base else # New public body public_body = PublicBody.new(:name=>"", :short_name=>"", :request_email=>"") available_locales.each do |locale| - PublicBody.with_locale(locale) do + I18n.with_locale(locale) do changed = ActiveSupport::OrderedHash.new field_list.each do |field_name| localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}" @@ -494,10 +533,8 @@ class PublicBody < ActiveRecord::Base end # Returns all public bodies (except for the internal admin authority) as csv - def self.export_csv - public_bodies = PublicBody.visible.find(:all, :order => 'url_name', - :include => [:translations, :tags]) - FasterCSV.generate() do |csv| + def self.export_csv(output_filename) + CSV.open(output_filename, "w") do |csv| csv << [ 'Name', 'Short name', @@ -512,7 +549,7 @@ class PublicBody < ActiveRecord::Base 'Updated at', 'Version', ] - public_bodies.each do |public_body| + PublicBody.visible.find_each(:include => [:translations, :tags]) do |public_body| # Skip bodies we use only for site admin next if public_body.has_tag?('site_administration') csv << [ @@ -551,7 +588,7 @@ class PublicBody < ActiveRecord::Base # Returns nil if configuration variable not set def override_request_email - e = Configuration::override_all_public_body_request_emails + e = AlaveteliConfiguration::override_all_public_body_request_emails e if e != "" end @@ -635,6 +672,104 @@ class PublicBody < ActiveRecord::Base end end + def self.where_clause_for_stats(minimum_requests, total_column) + # When producing statistics for public bodies, we want to + # exclude any that are tagged with 'test' - we use a + # sub-select to find the IDs of those public bodies. + test_tagged_query = "SELECT model_id FROM has_tag_string_tags" \ + " WHERE model = 'PublicBody' AND name = 'test'" + "#{total_column} >= #{minimum_requests} AND id NOT IN (#{test_tagged_query})" + end + + # Return data for the 'n' public bodies with the highest (or + # lowest) number of requests, but only returning data for those + # with at least 'minimum_requests' requests. + def self.get_request_totals(n, highest, minimum_requests) + ordering = "info_requests_count" + ordering += " DESC" if highest + where_clause = where_clause_for_stats minimum_requests, 'info_requests_count' + public_bodies = PublicBody.order(ordering).where(where_clause).limit(n) + public_bodies.reverse! if highest + y_values = public_bodies.map { |pb| pb.info_requests_count } + return { + 'public_bodies' => public_bodies, + 'y_values' => y_values, + 'y_max' => y_values.max, + 'totals' => y_values} + end + + # Return data for the 'n' public bodies with the highest (or + # lowest) score according to the metric of the value in 'column' + # divided by the total number of requests, expressed as a + # percentage. This only returns data for those public bodies with + # at least 'minimum_requests' requests. + def self.get_request_percentages(column, n, highest, minimum_requests) + total_column = "info_requests_visible_classified_count" + ordering = "y_value" + ordering += " DESC" if highest + y_value_column = "(cast(#{column} as float) / #{total_column})" + where_clause = where_clause_for_stats minimum_requests, total_column + where_clause += " AND #{column} IS NOT NULL" + public_bodies = PublicBody.select("*, #{y_value_column} AS y_value").order(ordering).where(where_clause).limit(n) + public_bodies.reverse! if highest + y_values = public_bodies.map { |pb| pb.y_value.to_f } + + original_values = public_bodies.map { |pb| pb.send(column) } + # If these are all nil, then probably the values have never + # been set; some have to be set by a rake task. In that case, + # just return nil: + return nil unless original_values.any? { |ov| !ov.nil? } + + original_totals = public_bodies.map { |pb| pb.send(total_column) } + # Calculate confidence intervals, as offsets from the proportion: + cis_below = [] + cis_above = [] + original_totals.each_with_index.map { |total, i| + lower_ci, higher_ci = ci_bounds original_values[i], total, 0.05 + cis_below.push(y_values[i] - lower_ci) + cis_above.push(higher_ci - y_values[i]) + } + # Turn the y values and confidence interval offsets into + # percentages: + [y_values, cis_below, cis_above].each { |l| + l.map! { |v| 100 * v } + } + return { + 'public_bodies' => public_bodies, + 'y_values' => y_values, + 'cis_below' => cis_below, + 'cis_above' => cis_above, + 'y_max' => 100, + 'totals' => original_totals} + end + def self.popular_bodies(locale) + # get some example searches and public bodies to display + # either from config, or based on a (slow!) query if not set + body_short_names = AlaveteliConfiguration::frontpage_publicbody_examples.split(/\s*;\s*/) + locale_condition = 'public_body_translations.locale = ?' + underscore_locale = locale.gsub '-', '_' + conditions = [locale_condition, underscore_locale] + bodies = [] + I18n.with_locale(locale) do + if body_short_names.empty? + # This is too slow + bodies = visible.find(:all, + :order => "info_requests_count desc", + :limit => 32, + :conditions => conditions, + :joins => :translations + ) + else + conditions[0] += " and public_bodies.url_name in (?)" + conditions << body_short_names + bodies = find(:all, :conditions => conditions, :joins => :translations) + end + end + return bodies + end + + private + def request_email_if_requestable # Request_email can be blank, meaning we don't have details if self.is_requestable? diff --git a/app/models/public_body_change_request.rb b/app/models/public_body_change_request.rb new file mode 100644 index 000000000..0e59cbecc --- /dev/null +++ b/app/models/public_body_change_request.rb @@ -0,0 +1,131 @@ +# == Schema Information +# +# Table name: public_body_change_requests +# +# id :integer not null, primary key +# user_email :string(255) +# user_name :string(255) +# user_id :integer +# public_body_name :text +# public_body_id :integer +# public_body_email :string(255) +# source_url :text +# notes :text +# is_open :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class PublicBodyChangeRequest < ActiveRecord::Base + + belongs_to :user + belongs_to :public_body + validates_presence_of :public_body_name, :message => N_("Please enter the name of the authority"), + :unless => proc{ |change_request| change_request.public_body } + validates_presence_of :user_name, :message => N_("Please enter your name"), + :unless => proc{ |change_request| change_request.user } + validates_presence_of :user_email, :message => N_("Please enter your email address"), + :unless => proc{ |change_request| change_request.user } + validate :user_email_format, :unless => proc{ |change_request| change_request.user_email.blank? } + validate :body_email_format, :unless => proc{ |change_request| change_request.public_body_email.blank? } + + scope :new_body_requests, :conditions => ['public_body_id IS NULL'], :order => 'created_at' + scope :body_update_requests, :conditions => ['public_body_id IS NOT NULL'], :order => 'created_at' + scope :open, :conditions => ['is_open = ?', true] + + def self.from_params(params, user) + change_request = new + change_request.update_from_params(params, user) + end + + def update_from_params(params, user) + if user + self.user_id = user.id + else + self.user_name = params[:user_name] + self.user_email = params[:user_email] + end + self.public_body_name = params[:public_body_name] + self.public_body_id = params[:public_body_id] + self.public_body_email = params[:public_body_email] + self.source_url = params[:source_url] + self.notes = params[:notes] + self + end + + def get_user_name + user ? user.name : user_name + end + + def get_user_email + user ? user.email : user_email + end + + def get_public_body_name + public_body ? public_body.name : public_body_name + end + + def send_message + if public_body + ContactMailer.update_public_body_email(self).deliver + else + ContactMailer.add_public_body(self).deliver + end + end + + def thanks_notice + if self.public_body + _("Your request to update the address for {{public_body_name}} has been sent. Thank you for getting in touch! We'll get back to you soon.", + :public_body_name => get_public_body_name) + else + _("Your request to add an authority has been sent. Thank you for getting in touch! We'll get back to you soon.") + end + end + + def send_response(subject, response) + ContactMailer.from_admin_message(get_user_name, + get_user_email, + subject, + response.strip.html_safe).deliver + end + + def comment_for_public_body + comments = ["Requested by: #{get_user_name} (#{get_user_email})"] + if !source_url.blank? + comments << "Source URL: #{source_url}" + end + if !notes.blank? + comments << "Notes: #{notes}" + end + comments.join("\n") + end + + def default_response_subject + if self.public_body + _("Your request to update {{public_body_name}} on {{site_name}}", :site_name => AlaveteliConfiguration::site_name, + :public_body_name => public_body.name) + else + _("Your request to add {{public_body_name}} to {{site_name}}", :site_name => AlaveteliConfiguration::site_name, + :public_body_name => public_body_name) + end + end + + def close! + self.is_open = false + self.save! + end + + private + + def body_email_format + unless MySociety::Validate.is_valid_email(self.public_body_email) + errors.add(:public_body_email, _("The authority email doesn't look like a valid address")) + end + end + + def user_email_format + unless MySociety::Validate.is_valid_email(self.user_email) + errors.add(:user_email, _("Your email doesn't look like a valid address")) + end + end +end diff --git a/app/models/purge_request.rb b/app/models/purge_request.rb index 48a16f9e6..4e6267bd2 100644 --- a/app/models/purge_request.rb +++ b/app/models/purge_request.rb @@ -1,20 +1,19 @@ # == Schema Information -# Schema version: 114 # # Table name: purge_requests # -# id :integer not null, primary key +# id :integer not null, primary key # url :string(255) -# created_at :datetime not null -# model :string(255) not null -# model_id :integer not null +# created_at :datetime not null +# model :string(255) not null +# model_id :integer not null # # models/purge_request.rb: # A queue of URLs to purge # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # class PurgeRequest < ActiveRecord::Base diff --git a/app/models/raw_email.rb b/app/models/raw_email.rb index de7978b82..21a53f493 100644 --- a/app/models/raw_email.rb +++ b/app/models/raw_email.rb @@ -1,16 +1,15 @@ # == Schema Information -# Schema version: 114 # # Table name: raw_emails # -# id :integer not null, primary key +# id :integer not null, primary key # # models/raw_email.rb: # The fat part of models/incoming_message.rb # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class RawEmail < ActiveRecord::Base # deliberately don't strip_attributes, so keeps raw email properly @@ -23,10 +22,10 @@ class RawEmail < ActiveRecord::Base raise "Failed to find the id number of the associated request: has it been saved?" end - if ENV["RAILS_ENV"] == "test" + if Rails.env.test? return File.join(Rails.root, 'files/raw_email_test') else - return File.join(Configuration::raw_emails_location, + return File.join(AlaveteliConfiguration::raw_emails_location, request_id[0..2], request_id) end end diff --git a/app/models/request_classification.rb b/app/models/request_classification.rb index f5a1b4bee..6873d468b 100644 --- a/app/models/request_classification.rb +++ b/app/models/request_classification.rb @@ -1,9 +1,8 @@ # == Schema Information -# Schema version: 20120919140404 # # Table name: request_classifications # -# id :integer not null, primary key +# id :integer not null, primary key # user_id :integer # info_request_event_id :integer # created_at :datetime diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb index dfe92b7fe..d5dda7bb5 100644 --- a/app/models/track_thing.rb +++ b/app/models/track_thing.rb @@ -1,16 +1,15 @@ # == Schema Information -# Schema version: 114 # # Table name: track_things # -# id :integer not null, primary key -# tracking_user_id :integer not null -# track_query :string(255) not null +# id :integer not null, primary key +# tracking_user_id :integer not null +# track_query :string(255) not null # info_request_id :integer # tracked_user_id :integer # public_body_id :integer -# track_medium :string(255) not null -# track_type :string(255) default("internal_error"), not null +# track_medium :string(255) not null +# track_type :string(255) default("internal_error"), not null # created_at :datetime # updated_at :datetime # @@ -19,10 +18,12 @@ # When somebody is getting alerts for something. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'set' +# TODO: TrackThing looks like a good candidate for single table inheritance + class TrackThing < ActiveRecord::Base belongs_to :tracking_user, :class_name => 'User' validates_presence_of :track_query @@ -258,7 +259,7 @@ class TrackThing < ActiveRecord::Base :title_in_email => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'", :title_in_rss => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'", # Authentication - :web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'", :site_name=>Configuration::site_name, :public_body_name=>CGI.escapeHTML(self.public_body.name)), + :web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'", :site_name=>AlaveteliConfiguration::site_name, :public_body_name=>CGI.escapeHTML(self.public_body.name)), :email => _("Then you will be notified whenever someone requests something or gets a response from '{{public_body_name}}'.", :public_body_name=>CGI.escapeHTML(self.public_body.name)), :email_subject => _("Confirm you want to follow requests to '{{public_body_name}}'", :public_body_name=>self.public_body.name), # RSS sorting @@ -310,7 +311,7 @@ class TrackThing < ActiveRecord::Base end # When constructing a new track, use this to avoid duplicates / double posting - def TrackThing.find_by_existing_track(tracking_user, track) + def TrackThing.find_existing(tracking_user, track) if tracking_user.nil? return nil end diff --git a/app/models/track_things_sent_email.rb b/app/models/track_things_sent_email.rb index a0a4c0f0c..072d3bdea 100644 --- a/app/models/track_things_sent_email.rb +++ b/app/models/track_things_sent_email.rb @@ -1,10 +1,9 @@ # == Schema Information -# Schema version: 114 # # Table name: track_things_sent_emails # -# id :integer not null, primary key -# track_thing_id :integer not null +# id :integer not null, primary key +# track_thing_id :integer not null # info_request_event_id :integer # user_id :integer # public_body_id :integer @@ -16,7 +15,7 @@ # Record that alert has arrived. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackThingsSentEmail < ActiveRecord::Base belongs_to :info_request_event diff --git a/app/models/user.rb b/app/models/user.rb index e6c666e47..e63ce8129 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,26 +1,27 @@ # == Schema Information -# Schema version: 20120919140404 +# Schema version: 20131024114346 # # Table name: users # -# id :integer not null, primary key -# email :string(255) not null -# name :string(255) not null -# hashed_password :string(255) not null -# salt :string(255) not null -# created_at :datetime not null -# updated_at :datetime not null -# email_confirmed :boolean default(FALSE), not null -# url_name :text not null -# last_daily_track_email :datetime default(Sat Jan 01 00:00:00 UTC 2000) -# admin_level :string(255) default("none"), not null -# ban_text :text default(""), not null -# about_me :text default(""), not null +# id :integer not null, primary key +# email :string(255) not null +# name :string(255) not null +# hashed_password :string(255) not null +# salt :string(255) not null +# created_at :datetime not null +# updated_at :datetime not null +# email_confirmed :boolean default(FALSE), not null +# url_name :text not null +# last_daily_track_email :datetime default(2000-01-01 00:00:00 UTC) +# admin_level :string(255) default("none"), not null +# ban_text :text default(""), not null +# about_me :text default(""), not null # locale :string(255) # email_bounced_at :datetime -# email_bounce_message :text default(""), not null -# no_limit :boolean default(FALSE), not null -# receive_email_alerts :boolean default(TRUE), not null +# email_bounce_message :text default(""), not null +# no_limit :boolean default(FALSE), not null +# receive_email_alerts :boolean default(TRUE), not null +# can_make_batch_requests :boolean default(FALSE), not null # require 'digest/sha1' @@ -41,6 +42,7 @@ class User < ActiveRecord::Base has_many :comments, :order => 'created_at desc' has_one :profile_photo has_many :censor_rules, :order => 'created_at desc' + has_many :info_request_batches, :order => 'created_at desc' attr_accessor :password_confirmation, :no_xapian_reindex validates_confirmation_of :password, :message => _("Please enter the same password twice") @@ -58,6 +60,9 @@ class User < ActiveRecord::Base ], :terms => [ [ :variety, 'V', "variety" ] ], :if => :indexed_by_search? + + after_initialize :set_defaults + def created_at_numeric # format it here as no datetime support in Xapian's value ranges return self.created_at.strftime("%Y%m%d%H%M%S") @@ -67,17 +72,6 @@ class User < ActiveRecord::Base "user" end - def after_initialize - if self.admin_level.nil? - self.admin_level = 'none' - end - if self.new_record? - # make alert emails go out at a random time for each new user, so - # overall they are spread out throughout the day. - self.last_daily_track_email = User.random_time_in_last_day - end - end - # requested_by: and commented_by: search queries also need updating after save after_update :reindex_referencing_models def reindex_referencing_models @@ -98,12 +92,7 @@ class User < ActiveRecord::Base end def get_locale - if !self.locale.nil? - locale = self.locale - else - locale = I18n.locale - end - return locale.to_s + (self.locale || I18n.locale).to_s end def visible_comments @@ -119,7 +108,12 @@ class User < ActiveRecord::Base name.strip! end if self.public_banned? - name = _("{{user_name}} (Account suspended)", :user_name=>name) + # Use interpolation to return a string rather than a SafeBuffer so that + # gsub can be called on it until we upgrade to Rails 3.2. The name returned + # is not marked as HTML safe so will be escaped automatically in views. We + # do this in two steps so the string still gets picked up for translation + name = _("{{user_name}} (Account suspended)", :user_name=> name.html_safe) + name = "#{name}" end name end @@ -141,14 +135,14 @@ class User < ActiveRecord::Base if user # There is user with email, check password if !user.has_this_password?(params[:password]) - user.errors.add_to_base(auth_fail_message) + user.errors.add(:base, auth_fail_message) end else # No user of same email, make one (that we don't save in the database) # for the forms code to use. user = User.new(params) # deliberately same message as above so as not to leak whether registered - user.errors.add_to_base(auth_fail_message) + user.errors.add(:base, auth_fail_message) end user end @@ -201,12 +195,12 @@ class User < ActiveRecord::Base # The "internal admin" is a special user for internal use. def User.internal_admin_user - u = User.find_by_email(Configuration::contact_email) + u = User.find_by_email(AlaveteliConfiguration::contact_email) if u.nil? password = PostRedirect.generate_random_token u = User.new( :name => 'Internal admin user', - :email => Configuration::contact_email, + :email => AlaveteliConfiguration::contact_email, :password => password, :password_confirmation => password ) @@ -251,8 +245,8 @@ class User < ActiveRecord::Base !user.nil? && user.owns_every_request? end - # Can the user see every request, even hidden ones? - def User.view_hidden_requests?(user) + # Can the user see every request, response, and outgoing message, even hidden ones? + def User.view_hidden?(user) !user.nil? && user.super? end @@ -278,17 +272,20 @@ class User < ActiveRecord::Base # Some users have no limit return false if self.no_limit + # Batch request users don't have a limit + return false if self.can_make_batch_requests? + # Has the user issued as many as MAX_REQUESTS_PER_USER_PER_DAY requests in the past 24 hours? - return false if Configuration::max_requests_per_user_per_day.blank? + return false if AlaveteliConfiguration::max_requests_per_user_per_day.blank? recent_requests = InfoRequest.count(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id]) - return (recent_requests >= Configuration::max_requests_per_user_per_day) + return (recent_requests >= AlaveteliConfiguration::max_requests_per_user_per_day) end def next_request_permitted_at return nil if self.no_limit - n_most_recent_requests = InfoRequest.all(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id], :order => "created_at DESC", :limit => Configuration::max_requests_per_user_per_day) - return nil if n_most_recent_requests.size < Configuration::max_requests_per_user_per_day + n_most_recent_requests = InfoRequest.all(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id], :order => "created_at DESC", :limit => AlaveteliConfiguration::max_requests_per_user_per_day) + return nil if n_most_recent_requests.size < AlaveteliConfiguration::max_requests_per_user_per_day nth_most_recent_request = n_most_recent_requests[-1] return nth_most_recent_request.created_at + 1.day @@ -311,7 +308,7 @@ class User < ActiveRecord::Base text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text, :contract => 1) text = text.gsub(/\n/, '<br>') - return text + return text.html_safe end # Returns domain part of user's email address @@ -407,6 +404,17 @@ class User < ActiveRecord::Base self.salt = self.object_id.to_s + rand.to_s end + def set_defaults + if self.admin_level.nil? + self.admin_level = 'none' + end + if self.new_record? + # make alert emails go out at a random time for each new user, so + # overall they are spread out throughout the day. + self.last_daily_track_email = User.random_time_in_last_day + end + end + def email_and_name_are_valid if self.email != "" && !MySociety::Validate.is_valid_email(self.email) errors.add(:email, _("Please enter a valid email address")) diff --git a/app/models/user_info_request_sent_alert.rb b/app/models/user_info_request_sent_alert.rb index cf20bcbf5..098b773f8 100644 --- a/app/models/user_info_request_sent_alert.rb +++ b/app/models/user_info_request_sent_alert.rb @@ -1,12 +1,11 @@ # == Schema Information -# Schema version: 114 # # Table name: user_info_request_sent_alerts # -# id :integer not null, primary key -# user_id :integer not null -# info_request_id :integer not null -# alert_type :string(255) not null +# id :integer not null, primary key +# user_id :integer not null +# info_request_id :integer not null +# alert_type :string(255) not null # info_request_event_id :integer # @@ -15,7 +14,7 @@ # given type of alert. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class UserInfoRequestSentAlert < ActiveRecord::Base belongs_to :user diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb deleted file mode 100644 index 1be4f8aa3..000000000 --- a/app/models/user_mailer.rb +++ /dev/null @@ -1,48 +0,0 @@ -# models/user_mailer.rb: -# Emails relating to user accounts. e.g. Confirming a new account -# -# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class UserMailer < ApplicationMailer - def confirm_login(user, reasons, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = user.name_and_email - @subject = reasons[:email_subject] - @body[:reasons] = reasons - @body[:name] = user.name - @body[:url] = url - end - - def already_registered(user, reasons, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = user.name_and_email - @subject = reasons[:email_subject] - @body[:reasons] = reasons - @body[:name] = user.name - @body[:url] = url - end - - def changeemail_confirm(user, new_email, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = new_email - @subject = _("Confirm your new email address on {{site_name}}", :site_name=>site_name) - @body[:name] = user.name - @body[:url] = url - @body[:old_email] = user.email - @body[:new_email] = new_email - end - - def changeemail_already_used(old_email, new_email) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = new_email - @subject = _("Unable to change email address on {{site_name}}", :site_name=>site_name) - @body[:old_email] = old_email - @body[:new_email] = new_email - end -end - diff --git a/app/views/admin_censor_rule/_form.html.erb b/app/views/admin_censor_rule/_form.html.erb new file mode 100644 index 000000000..5035238d6 --- /dev/null +++ b/app/views/admin_censor_rule/_form.html.erb @@ -0,0 +1,75 @@ +<%= error_messages_for 'censor_rule' %> + +<div class="well"> + <%=_("Applies to")%> + <% unless info_request.nil? %> + <%= request_both_links(info_request) %> + <%= hidden_field 'censor_rule', 'info_request_id', { :value => info_request.id } %> + <% end %> + <% unless user.nil? %> + <%= user_both_links(user) %> + <%= hidden_field 'censor_rule', 'user_id', { :value => user.id } %> +<% end %> +</div> + +<div class="control-group"> + <label for="censor_rule_regexp" class="control-label">Is it regexp replacement?</label> + <div class="controls"> + <%= check_box 'censor_rule', 'regexp' %> + <div class="help-block"> + Leave unchecked if you are not sure about this + </div> + </div> +</div> + +<div class="control-group"> + <label for="censor_rule_text" class="control-label">Text</label> + <div class="controls"> + <%= text_field 'censor_rule', 'text', :class => "span3" %> + <div class="help-block"> + that you want to remove, case sensitive + </div> + </div> +</div> + +<div class="control-group"> + <label for="censor_rule_replacement" class="control-label">Replacement</label> + <div class="controls"> + <%= text_field 'censor_rule', 'replacement', :class => "span3" %> + <div class="help-block"> + put it in <strong>[square brackets]</strong>, e.g. [personal information removed]. applies to text in emails and HTML conversions of binaries; binaries themselves must stay the same length and the replacement is just a bunch of 'x's + </div> + </div> +</div> + +<div class="control-group"> + <label for="censor_rule_last_edit_comment" class="control-label">Comment for this edit</label> + <div class="controls"> + <%= text_area 'censor_rule', 'last_edit_comment', :rows => 2, :class => "span6" %> + <div class="help-block"> + put purpose of the rule, and why the change + </div> + </div> +</div> + +<div class="row"> + <div class="span10 offset2"> + <div class="alert alert-info"> + <h3>Warning and notes:</h3> + <p> This does replace text in binary files, but for + most formats only in a naive way. It works well on surprisingly many Word documents. Notably + it doesn't even do UCS-2 (unicode sometimes used in Word). There is also special code + which works on some PDFs. Please <strong>carefully check</strong> all attachments have + changed in the way you expect, and haven't become corrupted. + </p> + <p>You may need to manually rebuild the search index afterwards. You can redact + things by individual request or by user by adding the censor rule from the + appropriate page. If you need to redact across a whole + authority, it will be easy enough to make code changes to add it, so do ask. + </p> + <p> + <strong>Regexp rules that are hard to process will really slow down request display.</strong> Please only use regexps if you really need to. + </p> + </div> + </div> +</div> diff --git a/app/views/admin_censor_rule/_form.rhtml b/app/views/admin_censor_rule/_form.rhtml deleted file mode 100644 index ac43de704..000000000 --- a/app/views/admin_censor_rule/_form.rhtml +++ /dev/null @@ -1,40 +0,0 @@ -<%= error_messages_for 'censor_rule' %> - -<p>Applies to: - <% if !info_request.nil? %> - <%=request_both_links(info_request)%> request - <%= hidden_field 'censor_rule', 'info_request_id', { :value => info_request.id } %> - <% end %> - <% if !user.nil? %> - <%=user_both_links(user)%> user - <%= hidden_field 'censor_rule', 'user_id', { :value => user.id } %> - <% end %> -</p> - -<p><label for="censor_rule_regexp">Is it regexp replacement?</label> (Leave unchecked if you are not sure about this)<br/> -<%= check_box 'censor_rule', 'regexp' %></p> - -<p><label for="censor_rule_text">Text</label> (that you want to remove, case sensitive)<br/> -<%= text_field 'censor_rule', 'text', :size => 60 %></p> - -<p><label for="censor_rule_replacement">Replacement</label> (put it in <strong>[square brackets]</strong>, e.g. [personal information removed]. applies to text in emails and HTML conversions of binaries; binaries themselves must stay the same length and the replacement is just a bunch of 'x's)<br/> -<%= text_field 'censor_rule', 'replacement', :size => 60 %></p> - -<p><label for="censor_rule_last_edit_comment">Comment for this edit</label> (put purpose of the rule, and why the change)<br/> -<%= text_area 'censor_rule', 'last_edit_comment', :rows => 2, :cols => 60 %></p> - -<p><strong>Warning and notes:</strong> This does replace text in binary files, but for -most formats only in a naive way. It works well on surprisingly many Word documents. Notably -it doesn't even do UCS-2 (unicode sometimes used in Word). There is also special code -which works on some PDFs. Please <strong>carefully check</strong> all attachments have -changed in the way you expect, and haven't become corrupted. -</p> - -<p>You may need to manually rebuild the search index afterwards. You can redact -things by individual request or by user by adding the censor rule from the -appropriate page. If you need to redact across a whole -authority, it will be easy enough to make code changes to add it, so do ask. -</p> -<p><strong>Regexp rules that are hard to process will really slow down request display.</strong> Please only use regexps if you really need to. -</p> - diff --git a/app/views/admin_censor_rule/_show.rhtml b/app/views/admin_censor_rule/_show.html.erb index 363c3fb6f..0d4cece93 100644 --- a/app/views/admin_censor_rule/_show.rhtml +++ b/app/views/admin_censor_rule/_show.html.erb @@ -1,6 +1,6 @@ <% if censor_rules.size > 0 %> - <table> + <table class="table table-condensed"> <tr> <th>Id</th> <% for column in CensorRule.content_columns %> @@ -16,7 +16,7 @@ <td><%=h censor_rule.send(column) %></td> <% end %> <td> - <%= link_to "Edit", '../../censor/edit/' + censor_rule.id.to_s %> + <%= link_to "Edit", admin_rule_edit_path(censor_rule) %> </td> </tr> <% end %> @@ -26,14 +26,10 @@ <% end %> <% if defined? info_request %> - <p> - <%= link_to "New censor rule", '../../censor/new?info_request_id=' + info_request.id.to_s %> (for this request only) - </p> + <%= link_to "New censor rule (for this request only)", admin_rule_new_path(:info_request_id => info_request.id), :class => "btn btn-info" %> <% end %> <% if defined? user %> - <p> - <%= link_to "New censor rule", '../../censor/new?user_id=' + user.id.to_s %> (for all requests by this user) - </p> + <%= link_to "New censor rule", admin_rule_new_path(:user_id => user.id), :class => "btn btn-info" %> <span class="label label-info">for all requests by this user</span> <% end %> diff --git a/app/views/admin_censor_rule/edit.html.erb b/app/views/admin_censor_rule/edit.html.erb new file mode 100644 index 000000000..230446ed3 --- /dev/null +++ b/app/views/admin_censor_rule/edit.html.erb @@ -0,0 +1,18 @@ +<% @title = 'Edit censor rule' %> + +<h1><%=@title%></h1> + +<%= form_tag admin_rule_update_path(@censor_rule), :class => "form form-horizontal" do %> + <%= render :partial => 'form', :locals => { :info_request => @censor_rule.info_request, :user => @censor_rule.user } %> + <div class="form-actions"> + <%= submit_tag 'Save', :accesskey => 's', :class => "btn btn-primary" %> + </div> +<% end %> + +<%= form_tag admin_rule_destroy_path(@censor_rule), :class => "form form-horizontal" do %> + <%= hidden_field_tag(:censor_rule_id, @censor_rule.id) %> + <div class="form-actions"> + Permanent! --> <%= submit_tag "Destroy rule", :class => "btn btn-primary" %> + </div> +<% end %> + diff --git a/app/views/admin_censor_rule/edit.rhtml b/app/views/admin_censor_rule/edit.rhtml deleted file mode 100644 index e34e022a0..000000000 --- a/app/views/admin_censor_rule/edit.rhtml +++ /dev/null @@ -1,16 +0,0 @@ -<% @title = 'Edit censor rule' %> - -<h1><%=@title%></h1> - -<% form_tag '../update/' + @censor_rule.id.to_s do %> - <%= render :partial => 'form', :locals => { :info_request => @censor_rule.info_request, :user => @censor_rule.user } %> - <p><%= submit_tag 'Save', :accesskey => 's' %></p> -<% end %> - -<% form_tag('../destroy/' + @censor_rule.id.to_s) do %> - <p> - <%= hidden_field_tag(:censor_rule_id, @censor_rule.id) %> - Permanent! --> <%= submit_tag "Destroy rule" %> - </p> -<% end %> - diff --git a/app/views/admin_censor_rule/new.html.erb b/app/views/admin_censor_rule/new.html.erb new file mode 100644 index 000000000..77d22990c --- /dev/null +++ b/app/views/admin_censor_rule/new.html.erb @@ -0,0 +1,11 @@ +<% @title = _('New censor rule') %> + +<h1><%=@title%></h1> + +<%= form_tag admin_rule_create_path, :class => "form form-horizontal" do %> + <%= render :partial => 'form', :locals => { :info_request => @info_request, :user => @censor_user } %> + <div class="form-actions"> + <%= submit_tag "Create", :class => "btn btn-primary" %> + </div> +<% end %> + diff --git a/app/views/admin_censor_rule/new.rhtml b/app/views/admin_censor_rule/new.rhtml deleted file mode 100644 index c6b8cea6a..000000000 --- a/app/views/admin_censor_rule/new.rhtml +++ /dev/null @@ -1,9 +0,0 @@ -<% @title = 'New censor rule' %> - -<h1><%=@title%></h1> - -<% form_tag 'create' do %> - <%= render :partial => 'form', :locals => { :info_request => @info_request, :user => @user } %> - <p><%= submit_tag "Create" %></p> -<% end %> - diff --git a/app/views/admin_general/_admin_navbar.html.erb b/app/views/admin_general/_admin_navbar.html.erb new file mode 100644 index 000000000..5cc740f70 --- /dev/null +++ b/app/views/admin_general/_admin_navbar.html.erb @@ -0,0 +1,22 @@ +<div class="admin"> + <div class="navbar navbar-fixed-top navbar-inverse"> + <div class="navbar-inner"> + <div class="container"> + <%= link_to 'Alaveteli', frontpage_path, :class => "brand" %> + <div class="nav-collapse"> + <ul class="nav"> + <li><%= link_to 'Summary', admin_general_index_path %></li> + <li><%= link_to 'Timeline', admin_timeline_path %></li> + <li><%= link_to 'Stats', admin_stats_path %></li> + <li><%= link_to 'Debug', admin_debug_path %></li> + <li><%= link_to 'Authorities', admin_body_list_path %></li> + <li><%= link_to 'Requests', admin_request_list_path %></li> + <li><%= link_to 'Users', admin_user_list_path %></li> + <li><%= link_to 'Tracks', admin_track_list_path %></li> + <li><%= link_to 'Log out', signout_path %></li> + </ul> + </div> + </div> + </div> + </div> +</div> diff --git a/app/views/admin_general/_change_request_summary.html.erb b/app/views/admin_general/_change_request_summary.html.erb new file mode 100644 index 000000000..bec49c12c --- /dev/null +++ b/app/views/admin_general/_change_request_summary.html.erb @@ -0,0 +1,60 @@ +<table class="table table-striped table-condensed"> + <tbody> + <tr> + <td> + <b>Authority</b> + </td> + <td> + <%= @change_request.get_public_body_name %> + </td> + </tr> + + <% if @change_request.public_body %> + <tr> + <td> + <b>Current address</b> + </td> + <td> + <%= @change_request.public_body.request_email %> + </td> + </tr> + <% end %> + + <tr> + <td> + <b>Suggested address</b> + </td> + <td> + <%= @change_request.public_body_email %> + </td> + </tr> + + <tr> + <td> + <b>Source</b> + </td> + <td> + <%= @change_request.source_url %> + </td> + </tr> + + <tr> + <td> + <b>Requested by</b> + </td> + <td> + <%= @change_request.get_user_name %> (<%= @change_request.get_user_email %>) + </td> + </tr> + + <tr> + <td> + <b>Requested on</b> + </td> + <td> + <%= I18n.l(@change_request.created_at, :format => "%e %B %Y %H:%M:%S") %> + (<%= "#{time_ago_in_words(@change_request.created_at)} ago" %>) + </td> + </tr> + </tbody> +</table> diff --git a/app/views/admin_general/debug.rhtml b/app/views/admin_general/debug.html.erb index 99488ba0c..bf984c2e1 100644 --- a/app/views/admin_general/debug.rhtml +++ b/app/views/admin_general/debug.html.erb @@ -2,14 +2,18 @@ <h1><%=@title%></h1> -<p>You are <%= h @admin_current_user %></p> +<p>You are <%= h @http_auth_user %></p> <h2>Version numbers</h2> <p> Alaveteli version: <%= link_to @current_version, @github_origin + @current_version %> <br> -Alaveteli branch: <%= link_to @current_branch, @github_origin + @current_branch %> +<% if @current_branch == "(no branch)" %> + Alaveteli branch: (no branch) +<% else %> + Alaveteli branch: <%= link_to @current_branch, @github_origin + @current_branch %> +<% end %> <br> Alaveteli commit: <%= link_to @current_commit, @github_origin + @current_commit %> <br> @@ -17,18 +21,13 @@ RUBY_VERSION <%=h RUBY_VERSION %> <br> Rails::VERSION::STRING <%=h Rails::VERSION::STRING%> <br> -TMail::VERSION::STRING <%=h TMail::VERSION::STRING%> -<br> Xapian::version_string <%=h Xapian::version_string%> -<br> -Spec::VERSION::STRING <%=h Spec::VERSION::STRING%> </p> <h2>Configuration</h2> -<p>environment: <%=h Rails::configuration.environment %> -<br>environment_path: <%=h Rails::configuration.environment_path %> -<br>framework_paths: <%=h Rails::configuration.framework_paths.to_yaml %> +<p>Rails env: <%=h Rails.env %> +<br>Rails root: <%=h Rails.root %> </p> <h2>Environment variables</h2> diff --git a/app/views/admin_general/index.html.erb b/app/views/admin_general/index.html.erb new file mode 100644 index 000000000..2202663be --- /dev/null +++ b/app/views/admin_general/index.html.erb @@ -0,0 +1,215 @@ +<% @title = "Summary" %> + +<div class="row"> + <div class="span12"> + <h1><%=@title%></h1> + + <ul> + <li><%=@public_body_count%> public authorities</li> + <li> + <%=@info_request_count%> requests, <%=@outgoing_message_count%> outgoing messages, + <%=@incoming_message_count%> incoming messages + </li> + <li><%=@user_count%> users, <%=@track_thing_count%> tracked things</li> + <li><%=@comment_count%> annotations</li> + </ul> + </div> +</div> + +<hr> + +<div class="row"> + <div class="span12"> + <h1>Things to do</h1> + </div> +</div> + +<div class="accordion" id="things-to-do"> + <% if @holding_pen_messages.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#holding-pen" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@holding_pen_messages.size%></span><%= chevron_right %> Put misdelivered responses with the right request</a> + </div> + <div id="holding-pen" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for message in @holding_pen_messages %> + <tr> + <td> + <% if message.get_body_for_quoting.strip.size == 0 %> + <%= link_to "(no body)", admin_request_show_raw_email_path(message.raw_email_id) %> + <% else %> + <%= link_to excerpt(message.get_body_for_quoting, "", 60), admin_request_show_raw_email_path(message.raw_email_id) %> + <% end %> + </td> + <td class="span2"> + <%=simple_date(message.sent_at)%> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + + <% if @error_message_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#error-messages" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@error_message_requests.size%></span> <%= chevron_right %>Fix these delivery and other errors</a> + </div> + <div id="error-messages" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for @request in @error_message_requests %> + <tr> + <td class="link"> + <%= request_both_links(@request) %> + </td> + <td class="span2"> + <%=simple_date(@request.info_request_events.last.created_at)%> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + + <% if @attention_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#attention-messages" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@attention_requests.size%></span><%= chevron_right %> Review requests marked by users as requiring your attention +</a> + </div> + <div id="attention-messages" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for @request in @attention_requests %> + <tr> + <td class="link"> + <%= request_both_links(@request) %> + </td> + <td class="span2"> + <%=simple_date(@request.info_request_events.last.created_at)%> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + + <% if @requires_admin_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#requires-admin" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@requires_admin_requests.size%></span><%= chevron_right %> These require administrator attention</a> + </div> + <div id="requires-admin" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for @request in @requires_admin_requests %> + <tr> + <td class="link"> + <%= request_both_links(@request) %> + </td> + <td class="span2"> + <%=simple_date(@request.info_request_events.last.created_at)%> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + + <% if @blank_contacts.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#blank-contacts" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@blank_contacts.size%></span><%= chevron_right %> Find missing FOI email for these public authorities (try phoning!)</a> + </div> + <div id="blank-contacts" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for @blank_contact in @blank_contacts %> + <tr> + <td class="link"> + <%= public_body_both_links(@blank_contact) %> + </td> + <td class="span2"> + <%=simple_date(@blank_contact.updated_at)%> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + + <% if @old_unclassified.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#unclassified" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%=@old_unclassified.size%></span><%= chevron_right %> Classify responses that are still unclassified <%=InfoRequest::OLD_AGE_IN_DAYS.inspect %> after response</a> + </div> + <div id="unclassified" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% for @request in @old_unclassified %> + <tr> + <td class="link"> + <%= request_both_links(@request) %> + </td> + <td class="span2"> + <%=simple_date(@request.get_last_public_response_event.created_at)%> + </td> + </tr> + <% end %> + </tbody> + + </table> + </div> + </div> + <% end %> + + <% if @new_body_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#new-authorities" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%= @new_body_requests.size %></span><%= chevron_right %> Add new authorities</a> + </div> + <div id="new-authorities" class="accordion-body collapse"> + <% for @change_request in @new_body_requests %> + <%= render :partial => 'change_request_summary'%> + <%= link_to("Close and respond", admin_change_request_edit_path(@change_request), :class => 'btn') %> + <%= link_to("Add authority", admin_body_new_path(:change_request_id => @change_request.id), :class => 'btn btn-primary') %> + <% end %> + </div> + </div> + <% end %> + + <% if @body_update_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#update-authorities" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%= @body_update_requests.size %></span><%= chevron_right %> Update authorities</a> + </div> + <div id="update-authorities" class="accordion-body collapse"> + <% for @change_request in @body_update_requests %> + <%= render :partial => 'change_request_summary' %> + <%= link_to("Close and respond", admin_change_request_edit_path(@change_request), :class => 'btn') %> + <%= link_to("Make update", admin_body_edit_path(@change_request.public_body, :change_request_id => @change_request.id), :class => 'btn btn-primary') %> + <% end %> + </div> + </div> + <% end %> +</div> + +<% if @holding_pen_messages.size == 0 && @old_unclassified.size == 0 && @requires_admin_requests.size == 0 && @blank_contacts.size == 0 && @attention_requests.size == 0 && @new_body_requests.size == 0 && @body_update_requests.size == 0 %> + <div class="row"> + <div class="span12 alert alert-success"> + No pending administration required. + </div> + </div> +<% end %> diff --git a/app/views/admin_general/index.rhtml b/app/views/admin_general/index.rhtml deleted file mode 100644 index 48bd7f694..000000000 --- a/app/views/admin_general/index.rhtml +++ /dev/null @@ -1,109 +0,0 @@ -<% @title = "Summary" %> - -<h1><%=@title%></h1> - -<ul> -<li><%=@public_body_count%> public authorities</li> -<li><%=@info_request_count%> requests, <%=@outgoing_message_count%> outgoing messages, - <%=@incoming_message_count%> incoming messages -</li> -<li><%=@user_count%> users, <%=@track_thing_count%> tracked things</li> -<li><%=@comment_count%> annotations</li> -</ul> - -<hr> - -<h1>Things to do</h1> - -<% if @holding_pen_messages.size > 0 %> - <h3>Put misdelivered responses with the right request (<%=@holding_pen_messages.size%> total)</h3> - - <ul> - <% for message in @holding_pen_messages %> - <li> - <% if message.get_body_for_quoting.strip.size == 0 %> - <%= link_to "(no body)", "request/show_raw_email/" + message.raw_email_id.to_s %> - <% else %> - <%= link_to excerpt(message.get_body_for_quoting, "", 60), "request/show_raw_email/" + message.raw_email_id.to_s %> - <% end %> - (<%=simple_date(message.sent_at)%>) - </li> - <% end %> - </ul> - -<% end %> - -<% if @error_message_requests.size > 0 %> - <h3>Fix these delivery and other errors (<%=@error_message_requests.size%> total)</h3> - - <ul> - <% for @request in @error_message_requests %> - <li> - <%= request_both_links(@request)%> - – <%=simple_date(@request.get_last_event.created_at)%> - </li> - <% end %> - </ul> -<% end %> - -<% if @attention_requests.size > 0 %> - <h3>Review requests which have been marked as requiring your attention by users (<%=@error_message_requests.size%> total)</h3> - - <ul> - <% for @request in @attention_requests %> - <li> - <%= request_both_links(@request)%> - – <%=simple_date(@request.get_last_event.created_at)%> - </li> - <% end %> - </ul> -<% end %> - - -<% if @requires_admin_requests.size > 0 %> - <h3>These require administrator attention (<%=@requires_admin_requests.size%> total)</h3> - - <ul> - <% for @request in @requires_admin_requests %> - <li> - <%= request_both_links(@request)%> - – <%=simple_date(@request.get_last_event.created_at)%> - </li> - <% end %> - </ul> -<% end %> - -<% if @blank_contacts.size > 0 %> - <h3>Find missing FOI email for these public authorities (try phoning!) (<%=@blank_contacts.size%> total)</h3> - <ul> - <% for @blank_contact in @blank_contacts %> - <li> - <%= public_body_both_links(@blank_contact)%> - – <%=simple_date(@blank_contact.updated_at)%> - </li> - <% end %> - </ul> -<% end %> - -<% if @old_unclassified.size > 0 %> - <h3>Classify responses that are still unclassified <%=InfoRequest::OLD_AGE_IN_DAYS.inspect %> after response</h3> - - <ul> - <% for @request in @old_unclassified %> - <li> - <%= request_both_links(@request) %> - – <%=simple_date(@request.get_last_response_event.created_at)%> - </li> - <% end %> - </ul> - - <p>(<%= link_to "Full list", admin_url("unclassified") %>, or play public - <%= link_to "Categorisation game", main_url(play_url(:only_path => true)) %>) - </p> -<% end %> - -<% if @holding_pen_messages.size == 0 && @old_unclassified.size == 0 && @requires_admin_requests.size == 0 && @blank_contacts.size == 0 %> - <p>No pending administration required.</p> -<% end %> - - diff --git a/app/views/admin_general/stats.html.erb b/app/views/admin_general/stats.html.erb new file mode 100644 index 000000000..27dc25ee0 --- /dev/null +++ b/app/views/admin_general/stats.html.erb @@ -0,0 +1,60 @@ +<% @title = "Statistics" %> +<div class="hero-unit"> + <h2><%=@public_body_count%> public authorities</h2> + <h2><%=@info_request_count%> requests, <%=@outgoing_message_count%> outgoing messages, <%=@incoming_message_count%> incoming messages</h2> + <h2><%=@user_count%> users, <%=@track_thing_count%> tracked things</h2> + <h2><%=@comment_count%> annotations</h2> +</div> + +<div class="row"> + <div class="span12"> + <h1>Statistics</h1> + <h2>Chart of requests (excluding backpaged)</h2> + <img src="/foi-live-creation.png" alt="Chart of requests"> + </div> +</div> +<div class="row"> + <div class="span12"> + <h2>State of requests (includes backpaged)</h2> + <div class="container"> + <% for state, count in @request_by_state %> + <div class="row"> + <div class="span1"> + <span class="label label-info"><%=count%></span> + </div> + <div class="span4"> + <%=state%> + </div> + </div> + <% end %> + </div> + </div> +</div> +<div class="row"> + <div class="span12"> + <h2>Chart of users</h2> + <img src="/foi-user-use.png" alt="Chart of users"> + </div> +</div> +<div class="row"> + <div class="span12"> + <h2>Tracks by type</h2> + <div class="container"> + <% for state, count in @tracks_by_type %> + <div class="row"> + <div class="span1"> + <span class="label label-info"><%=count%></span> + </div> + <div class="span4"> + <%=state%> + </div> + </div> + <% end %> + </div> + </div> +</div> +<div class="row"> + <div class="span12"> + <h2>Web analytics</h2> + </div> +</div> diff --git a/app/views/admin_general/stats.rhtml b/app/views/admin_general/stats.rhtml deleted file mode 100644 index b22adb581..000000000 --- a/app/views/admin_general/stats.rhtml +++ /dev/null @@ -1,38 +0,0 @@ -<% @title = "Statistics" %> - -<h1>Statistics</h1> - -<h2>Chart of requests (excluding backpaged)</h2> - -<p> - <img src="<%= main_url("/foi-live-creation.png")%>"> -</p> - -<h2>State of requests (includes backpaged)</h2> - -<table> -<% for state, count in @request_by_state %> -<tr> <td><%=state %></td><td><%= count %></td> </tr> -<% end %> -</table> - -<h2>Chart of users</h2> - -<p> - <img src="<%= main_url("/foi-user-use.png")%>"> -</p> - -<h2>Tracks by type</h2> - -<table> -<% for state, count in @tracks_by_type %> -<tr> <td><%=state %></td><td><%= count %></td> </tr> -<% end %> -</table> - -<h2>Web analytics</h2> - - -</p> - - diff --git a/app/views/admin_general/timeline.rhtml b/app/views/admin_general/timeline.html.erb index e84539970..c4ea4849b 100644 --- a/app/views/admin_general/timeline.rhtml +++ b/app/views/admin_general/timeline.html.erb @@ -1,14 +1,19 @@ <% @title = "Timeline" %> - -<h1><%=h @events_title%></h1> - -<p> -<a href="?hour=1">Hour</a> -| <a href="?day=1">Day</a> -| <a href="?">2 days</a> -| <a href="?week=1">Week</a> -| <a href="?month=1">Month</a> -| <a href="?all=1">All time</a></p> +<div class="btn-toolbar"> + <div class="btn-group"> + <%= link_to "Hour", admin_timeline_path(:hour => 1), :class => "btn" %> + <%= link_to "Day", admin_timeline_path(:day => 1), :class => "btn" %> + <%= link_to "2 days", admin_timeline_path, :class => "btn" %> + <%= link_to "Week", admin_timeline_path(:week => 1), :class => "btn" %> + <%= link_to "Month", admin_timeline_path(:month => 1), :class => "btn" %> + <%= link_to "All time", admin_timeline_path(:all => 1), :class => "btn" %> + </div> +</div> +<div class="row"> + <div class="span12"> + <h1><%=h @events_title%></h1> + </div> +</div> <% last_date = nil %> <% for event, event_at in @events %> @@ -29,7 +34,7 @@ <%= request_both_links(event.info_request) %> <% if event.event_type == 'edit' %> was edited by administrator <strong><%=h event.params[:editor] %></strong>. - <% for p in ['title', 'prominence', 'described_state', 'awaiting_description'] + <% for p in ['title', 'described_state', 'awaiting_description'] if event.params[p.to_sym] != event.params[('old_'+p).to_sym] %> Changed <%=p%> from '<%=h event.params[('old_'+p).to_sym]%>' to '<%=h event.params[p.to_sym] %>'. <% end @@ -72,7 +77,7 @@ <% elsif event.event_type == 'response' %> <% incoming_message = event.incoming_message %> received - <%= link_to 'a response', main_url(incoming_message_url(incoming_message)) %> + <%= link_to 'a response', incoming_message_path(incoming_message) %> from <%=h event.info_request.public_body.name %>. <% elsif event.event_type == 'sent' %> was initially sent to <%=h event.params[:email]%> at <%=h event.info_request.public_body.name %>. @@ -83,7 +88,7 @@ <% elsif event.event_type == 'comment' %> had an annotation posted by <%=h event.comment.user.name %>. <% elsif event.event_type == 'status_update' %> - had its status updated by <%=h User.find(event.params[:user_id]).name %> from '<%= h event.params[:old_described_state] %>' to '<%= h event.params[:described_state] %>'. + had its status updated by <%= event.params[:user_id] ? User.find(event.params[:user_id]).name : event.params[:script] %> from '<%= h event.params[:old_described_state] %>' to '<%= h event.params[:described_state] %>'. <% else %> had '<%=event.event_type%>' done to it, parameters <%=h event.params_yaml%>. <% end %> @@ -92,8 +97,7 @@ was created/updated by administrator <strong><%=h event.last_edit_editor %></strong> <% end %> <% end %> +<%= will_paginate(@events, :class => 'paginator') %> <% if not @events.empty? %> </p> <% end %> -<%= will_paginate(@events) %> - diff --git a/app/views/admin_incoming_message/_intro.html.erb b/app/views/admin_incoming_message/_intro.html.erb new file mode 100644 index 000000000..1d5585f11 --- /dev/null +++ b/app/views/admin_incoming_message/_intro.html.erb @@ -0,0 +1,3 @@ +<% @title = "Incoming message #{incoming_message.id} of FOI request '#{incoming_message.info_request.title}'" %> +<h1>Incoming message <%= incoming_message.id %></h1> +<p>FOI request: <%= request_both_links(incoming_message.info_request) %></p> diff --git a/app/views/admin_incoming_message/edit.html.erb b/app/views/admin_incoming_message/edit.html.erb new file mode 100644 index 000000000..1088edcab --- /dev/null +++ b/app/views/admin_incoming_message/edit.html.erb @@ -0,0 +1,26 @@ +<%= render :partial => 'intro', :locals => {:incoming_message => @incoming_message } %> +<%= render :partial => 'admin_request/incoming_message_actions', :locals => { :incoming_message => @incoming_message } %> +<fieldset class="form-horizontal"> + <legend>Prominence</legend> + <%= form_tag admin_incoming_update_path(@incoming_message), :class => "form form-inline" do %> + + <div class="control-group"> + <label class="control-label" for="incoming_message_prominence"> Prominence</label> + <div class="controls"> + <%= select('incoming_message', "prominence", IncomingMessage.prominence_states) %> + </div> + </div> + + <div class="control-group"> + <label class="control-label" for="incoming_message_prominence_reason">Reason for prominence</label> + <div class="controls"> + <%= text_area "incoming_message", "prominence_reason", :rows => 5, :class => "span6" %> + </div> + </div> + + <div class="form-actions" > + <%= submit_tag 'Save', :class => "btn" %> + </div> + + <% end %> +</fieldset> diff --git a/app/views/admin_outgoing_message/edit.html.erb b/app/views/admin_outgoing_message/edit.html.erb new file mode 100644 index 000000000..d5f5f43bf --- /dev/null +++ b/app/views/admin_outgoing_message/edit.html.erb @@ -0,0 +1,50 @@ +<h1>Edit outgoing message</h1> + +<%= error_messages_for 'outgoing_message' %> + +<%= form_tag admin_outgoing_update_path(@outgoing_message) do %> + <div class="control-group"> + <label class="control-label" for="outgoing_message_prominence"> Prominence</label> + <div class="controls"> + <%= select('outgoing_message', "prominence", OutgoingMessage.prominence_states) %> + </div> + </div> + + <div class="control-group"> + <label class="control-label" for="outgoing_message_prominence_reason">Reason for prominence</label> + <div class="controls"> + <%= text_area "outgoing_message", "prominence_reason", :rows => 5, :class => "span6" %> + </div> + </div> + + <div class="control-group"> + <label class="control-label" for="outgoing_message_body">Body of message</label> + <div class="controls"> + <%= text_area 'outgoing_message', 'body', :rows => 10, :cols => 60 %> + </div> + + <p><strong>Note:</strong> This is mainly to be used to excise information + that users inadvertently put in their messages, not realising it would be + public. It will already have been sent to the public authority, and their + reply may also include that information and be automatically published on + this site. You could also use this to edit a message before resending it, but + only the edited version will be shown on the public page if you do that.</p> + +<div class="form-actions" > +<%= submit_tag 'Save', :accesskey => 's', :class => 'btn' %> +</div> +<% end %> + +<p> +<%= link_to 'Show', admin_request_show_path(@outgoing_message.info_request) %> | +<%= link_to 'List all', admin_request_list_path %> +</p> + +<%= form_tag admin_outgoing_destroy_path do %> + <div> + <%= hidden_field_tag 'outgoing_message_id', @outgoing_message.id %> + <%= submit_tag "Destroy outgoing message", :class => "btn btn-danger", :confirm => "This is permanent! Are you sure?" %> + </div> +<% end %> + + diff --git a/app/views/admin_public_body/_form.html.erb b/app/views/admin_public_body/_form.html.erb new file mode 100644 index 000000000..2da13ab01 --- /dev/null +++ b/app/views/admin_public_body/_form.html.erb @@ -0,0 +1,102 @@ +<%= error_messages_for 'public_body' %> + +<!--[form:public_body]--> + +<div id="div-locales"> + <ul class="locales nav nav-tabs"> + <% I18n.available_locales.each_with_index do |locale, i| %> + <li><a href="#div-locale-<%=locale.to_s%>" data-toggle="tab" ><%=locale_name(locale.to_s) || _("Default locale")%></a></li> + <% end %> + </ul> + <div class="tab-content"> +<% + for locale in I18n.available_locales do + if locale==I18n.default_locale # The default locale is submitted as part of the bigger object... + prefix = 'public_body' + object = @public_body + else # ...but additional locales go "on the side" + prefix = "public_body[translated_versions][]" + object = @public_body.new_record? ? + PublicBody::Translation.new : + @public_body.find_translation_by_locale(locale.to_s) || PublicBody::Translation.new + end +%> + <%= fields_for prefix, object do |t| %> + <div class="tab-pane" id="div-locale-<%=locale.to_s%>"> + <div class="control-group"> + <%= t.hidden_field :locale, :value => locale.to_s %> + <label for="<%= form_tag_id(t.object_name, :name, locale) %>" class="control-label">Name</label> + <div class="controls"> + <%= t.text_field :name, :id => form_tag_id(t.object_name, :name, locale), :class => "span4" %> + </div> + </div> + <div class="control-group"> + <label for="<%= form_tag_id(t.object_name, :short_name, locale) %>", class="control-label"><%=_("Short name")%></label> + <div class="controls"> + <%= t.text_field :short_name, :id => form_tag_id(t.object_name, :short_name, locale), :class => "span2" %> + <p class="help-block"><%=_("Only put in abbreviations which are really used, otherwise leave blank. Short or long name is used in the URL – don't worry about breaking URLs through renaming, as the history is used to redirect")%></p> + </div> + </div> + <div class="control-group"> + <label for="<%= form_tag_id(t.object_name, :request_email, locale) %>" class="control-label"><%=_("Request email")%></label> + <div class="controls"> + <%= t.text_field :request_email, :id => form_tag_id(t.object_name, :request_email, locale), :class => "span3" %> + <p class="help-block"><%=_("set to <strong>blank</strong> (empty string) if can't find an address; these emails are <strong>public</strong> as anyone can view with a CAPTCHA")%></p> + </div> + </div> + <div class="control-group"> + <label for="<%= form_tag_id(t.object_name, :publication_scheme, locale) %>" class="control-label"><%=_("Publication scheme URL")%></label> + <div class="controls"> + <%= t.text_field :publication_scheme, :size => 60, :id => form_tag_id(t.object_name, :publication_scheme, locale), :class => "span3" %> + </div> + </div> + <div class="control-group"> + <label for="<%= form_tag_id(t.object_name, :notes, locale) %>" class="control-label"><%=_("Public notes")%></label> + <div class="controls"> + <%= t.text_area :notes, :rows => 3, :id => form_tag_id(t.object_name, :notes, locale), :class => "span6" %> + <p class="help-block"> + HTML, for users to consider when making FOI requests to the authority + </p> + </div> + </div> + </div> +<% + end + end +%> + </div> +</div> + +<h3>Common Fields</h3> +<div class="control-group"> + <label for="public_body_tag_string" class="control-label"><%=_("Tags")%></label> + <div class="controls"> + <%= f.text_field :tag_string, :class => "span4" %> + <p class="help-block">space separated; see list of tags on the right; also <strong>not_apply</strong> if FOI and EIR no longer apply to authority, <strong>eir_only</strong> if EIR but not FOI applies to authority, <strong>defunct</strong> if the authority no longer exists; charity:NUMBER if a registered charity</p> + </div> +</div> +<div class="control-group"> + <label for="public_body_home_page" class="control-label"><%=_("Home page")%></label> + <div class="controls"> + <%= f.text_field :home_page, :class => "span4" %> + <p class="help-block">(of whole authority, not just their FOI page; set to <strong>blank</strong> (empty string) to guess it from the email)</p> + </div> +</div> +<div class="control-group"> + <label for="public_body_disclosure_log" class="control-label"><%=_("Disclosure log URL")%></label> + <div class="controls"> + <%= f.text_field :disclosure_log, :size => 60, :class => "span4" %> + </div> +</div> +<div class="control-group"> + <label for="public_body_last_edit_comment" class="control-label"><strong>Comment</strong> for this edit</label> + <div class="controls"> + <%= f.text_area :last_edit_comment, :rows => 3, :class => "span6" %></p> + <p class="help-block">put URL or other source of new info</p> + </div> +</div> +<% if @change_request %> + <%= render :partial => 'admin_public_body_change_requests/response' %> + +<% end %> +<!--[eoform:public_body]--> diff --git a/app/views/admin_public_body/_form.rhtml b/app/views/admin_public_body/_form.rhtml deleted file mode 100644 index 0d6ae51e2..000000000 --- a/app/views/admin_public_body/_form.rhtml +++ /dev/null @@ -1,65 +0,0 @@ -<%= error_messages_for 'public_body' %> - -<!--[form:public_body]--> - -<div id="div-locales"> - <ul> - <% for locale in I18n.available_locales do %> - <li><a href="#div-locale-<%=locale.to_s%>"><%=locale_name(locale.to_s)%></a></li> - <% end %> - </ul> - -<% - for locale in I18n.available_locales do - if locale==I18n.default_locale # The default locale is submitted as part of the bigger object... - prefix = 'public_body' - object = @public_body - else # ...but additional locales go "on the side" - prefix = "public_body[translated_versions][]" - object = @public_body.new_record? ? - PublicBody::Translation.new : - @public_body.translation(locale.to_s) || PublicBody::Translation.new - end - - fields_for prefix, object do |t| -%> - <div id="div-locale-<%=locale.to_s%>"> - <%= t.hidden_field :locale, :value => locale.to_s %> - - <p><label for="<%= form_tag_id(t.object_name, :name, locale) %>">Name</label><br/> - <%= t.text_field :name, :size => 60, :id => form_tag_id(t.object_name, :name, locale) %></p> - - <p><label for="<%= form_tag_id(t.object_name, :short_name, locale) %>">Short name <small>(only put in abbreviations which are really used, otherwise leave blank. Short or long name is used in the URL - don't worry about breaking URLs through renaming, as the history is used to redirect)</small></label><br/> - <%= t.text_field :short_name, :size => 60, :id => form_tag_id(t.object_name, :short_name, locale) %></p> - - <p><label for="<%= form_tag_id(t.object_name, :request_email, locale) %>">Request email <small>(set to <strong>blank</strong> (empty string) if can't find an address; these emails are <strong>public</strong> as anyone can view with a CAPTCHA)</small></label><br/> - <%= t.text_field :request_email, :size => 40, :id => form_tag_id(t.object_name, :request_email, locale) %></p> - - <p><label for="<%= form_tag_id(t.object_name, :publication_scheme, locale) %>">Publication scheme URL</label><br/> - <%= t.text_field :publication_scheme, :size => 60, :id => form_tag_id(t.object_name, :publication_scheme, locale) %></p> - - <p><label for="<%= form_tag_id(t.object_name, :disclosure_log, locale) %>">Disclosure log URL</label><br/> - <%= t.text_field :disclosure_log, :size => 60, :id => form_tag_id(t.object_name, :disclosure_log, locale) %></p> - - <p><label for="<%= form_tag_id(t.object_name, :notes, locale) %>">Public notes</label> <small>(HTML, for users to consider when making FOI requests to the authority)</small><br/> - <%= t.text_area :notes, :rows => 3, :cols => 60, :id => form_tag_id(t.object_name, :notes, locale) %></p> - </div> -<% - end - end -%> -</div> - -<h3>Common Fields</h3> - -<p><label for="public_body_tag_string">Tags <small>(space separated; see list of tags on the right; also <strong>not_apply</strong> if FOI and EIR no longer apply to authority, <strong>eir_only</strong> if EIR but not FOI applies to authority, <strong>defunct</strong> if the authority no longer exists; charity:NUMBER if a registered charity)</small></label><br/> - -<%= text_field :public_body, :tag_string, :size => 60, :id => 'public_body_tag_string' %></p> - -<p><label for="public_body_home_page">Home page <small>(of whole authority, not just their FOI page; set to <strong>blank</strong> (empty string) to guess it from the email)</small></label><br/> -<%= text_field :public_body, :home_page, :size => 60, :id => 'public_body_home_page' %></p> - -<p><label for="public_body_last_edit_comment"><strong>Comment</strong> for this edit</label> <small>(put URL or other source of new info)</small><br/> -<%= text_area :public_body, :last_edit_comment, :rows => 3, :cols => 60, :id => 'public_body_last_edit_comment' %></p> - -<!--[eoform:public_body]--> diff --git a/app/views/admin_public_body/_locale_selector.rhtml b/app/views/admin_public_body/_locale_selector.rhtml deleted file mode 100644 index 5ef79f2df..000000000 --- a/app/views/admin_public_body/_locale_selector.rhtml +++ /dev/null @@ -1,10 +0,0 @@ -<div id="locale_switcher"> -<%= _('Edit language version:') %> -<% for possible_locale in @locales %> - <% if possible_locale == @locale %> - <%= possible_locale %> - <% else %> - <a href="?show_locale=<%=possible_locale%>"><%= possible_locale %></a> - <% end %> -<% end %> -</div> diff --git a/app/views/admin_public_body/_one_list.html.erb b/app/views/admin_public_body/_one_list.html.erb new file mode 100644 index 000000000..8f1d719ec --- /dev/null +++ b/app/views/admin_public_body/_one_list.html.erb @@ -0,0 +1,44 @@ +<div class="accordion" id="bodies"> + <% for public_body in bodies %> + <div class="accordion-group"> + <div class="accordion-heading accordion-toggle row"> + <span class="item-title span6"> + <a href="#body_<%=public_body.id%>" data-toggle="collapse" data-parent="requests"><%= chevron_right %></a> + <%= link_to(public_body.name, admin_body_show_path(public_body), :title => "view full details")%> + </span> + <span class="item-metadata span6"> + <%= render :partial => 'tags', :locals => { :body => public_body} %> + </span> + </div> + <div id="body_<%=public_body.id%>" class="item-detail accordion-body collapse row"> + <% public_body.for_admin_column do |name, value, type| %> + <div> + <span class="span6"> + <b><%=name%></b> + </span> + <span class="span6"> + <% if type == 'datetime' %> + <%= I18n.l(value, :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(value)) %>) + <% else %> + <%= h value %> + <% end %> + </span> + </div> + <% end %> + </div> + </div> + <% end %> +</div> + +<%= form_tag(admin_body_mass_tag_add_url, :method => "post", :class => "form form-inline" ) do %> + <p> + <%= text_field_tag 'new_tag', params[:new_tag], { :size => 15, :id => "mass_add_tag_new_tag_" + table_name } %> + <%= hidden_field_tag(:query, params[:query], { :id => "mass_add_tag_query_" + table_name } ) %> + <%= hidden_field_tag(:page, params[:page], { :id => "mass_add_page_" + table_name } ) %> + <%= hidden_field_tag(:table_name, table_name, { :id => "mass_add_tag_table_name_" + table_name } ) %> + <%= submit_tag "Add tag to all", :class => "btn btn-primary" %> + (in table just above) + </p> +<% end %> + diff --git a/app/views/admin_public_body/_one_list.rhtml b/app/views/admin_public_body/_one_list.rhtml deleted file mode 100644 index e0d2399d0..000000000 --- a/app/views/admin_public_body/_one_list.rhtml +++ /dev/null @@ -1,29 +0,0 @@ -<table> - <tr> - <th>Name</th> - <th>Tags</th> - <% for column in PublicBody.content_columns.map { |c| c.human_name } - [ "Name", "Last edit comment" ] %> - <th><%= column %></th> - <% end %> - </tr> -<% for public_body in bodies %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%= public_body_both_links(public_body) %></td> - <td><%= render :partial => 'tags', :locals => { :body => public_body} %></td> - <% for column in PublicBody.content_columns.map { |c| c.name } - [ "name", "last_edit_comment" ] %> - <td><%=h public_body.send(column) %></td> - <% end %> - </tr> -<% end %> -</table> - -<% form_tag(admin_url("body/mass_tag_add"), :method => "post", :class => "forms_on_one_line" ) do %> - <p> - <%= text_field_tag 'new_tag', params[:new_tag], { :size => 15, :id => "mass_add_tag_new_tag_" + table_name } %> - <%= hidden_field_tag(:query, params[:query], { :id => "mass_add_tag_query_" + table_name } ) %> - <%= hidden_field_tag(:page, params[:page], { :id => "mass_add_page_" + table_name } ) %> - <%= hidden_field_tag(:table_name, table_name, { :id => "mass_add_tag_table_name_" + table_name } ) %> - <%= submit_tag "Add tag to all" %> (in table just above) - </p> -<% end %> - diff --git a/app/views/admin_public_body/_tag_help.html.erb b/app/views/admin_public_body/_tag_help.html.erb new file mode 100644 index 000000000..b64e65877 --- /dev/null +++ b/app/views/admin_public_body/_tag_help.html.erb @@ -0,0 +1,16 @@ +<h2>List of tags</h2> +<% first_row = true %> +<% for row in PublicBodyCategories::get().with_headings() %> + <% if row.instance_of?(Array) %> + <% if row[0] != 'other' %> + <strong><%= row[0] %></strong>=<%= row[1] %> + <br/> + <% end %> + <% elsif row != 'Miscellaneous' %> + <% if not first_row %> + <% else %> + <% first_row = false %> + <% end %> + <h3><%=h row%></h3> + <% end %> +<% end %> diff --git a/app/views/admin_public_body/_tag_help.rhtml b/app/views/admin_public_body/_tag_help.rhtml deleted file mode 100644 index 0d0f84dda..000000000 --- a/app/views/admin_public_body/_tag_help.rhtml +++ /dev/null @@ -1,18 +0,0 @@ -<div id="tag_help"> - <h2>List of tags</h2> - <% first_row = true %> - <% for row in PublicBodyCategories::get().with_headings() %> - <% if row.instance_of?(Array) %> - <% if row[0] != 'other' %> - <strong><%= row[0] %></strong>=<%= row[1] %> - <br/> - <% end %> - <% elsif row != 'Miscellaneous' %> - <% if not first_row %> - <% else %> - <% first_row = false %> - <% end %> - <h3><%=h row%></h3> - <% end %> - <% end %> -</div>
\ No newline at end of file diff --git a/app/views/admin_public_body/_tags.html.erb b/app/views/admin_public_body/_tags.html.erb new file mode 100644 index 000000000..26526f304 --- /dev/null +++ b/app/views/admin_public_body/_tags.html.erb @@ -0,0 +1,12 @@ +<% for t in body.tags %> + <span class="label label-info tag"> + <% if t.value %> + <%= link_to(h(t.name), list_public_bodies_path(:tag => t.name)) %>:<%= link_to(h(t.value), list_public_bodies_path(:tag => t.name_and_value)) %> + <% else %> + <%= link_to(h(t.name), list_public_bodies_path(:tag => t.name)) %> + <% end %> + </span> +<% end %> + + + diff --git a/app/views/admin_public_body/_tags.rhtml b/app/views/admin_public_body/_tags.rhtml deleted file mode 100644 index 85dc942fd..000000000 --- a/app/views/admin_public_body/_tags.rhtml +++ /dev/null @@ -1,11 +0,0 @@ -<% for t in body.tags %> - <% if t.value %> - <%= link_to(h(t.name), main_url(list_public_bodies_url(:tag => t.name, :only_path => true))) %>:<%= link_to(h(t.value), main_url(list_public_bodies_url(:tag => t.name_and_value, :only_path => true))) %> - <% else %> - <%= link_to(h(t.name), main_url(list_public_bodies_url(:tag => t.name, :only_path => true))) %> - <% end %> - (<a href="<%= admin_url('body/list') %>?query=<%= h(t.name)%>">admin</a>) -<% end %> - - - diff --git a/app/views/admin_public_body/edit.html.erb b/app/views/admin_public_body/edit.html.erb new file mode 100644 index 000000000..11b7eec22 --- /dev/null +++ b/app/views/admin_public_body/edit.html.erb @@ -0,0 +1,34 @@ +<h1><%=@title%></h1> + +<div class="row"> + <div class="span8"> + <div id="public_body_form"> + <%= form_for @public_body, :url => admin_body_update_path(@public_body), :html => { :class => "form form-horizontal" } do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> + <div class="form-actions"> + <%= f.submit 'Save', :accesskey => 's', :class => "btn btn-success" %></p> + </div> + <% end %> + + <div class="row"> + <div class="span8"> + <div class="well"> + <%= link_to 'Show', admin_body_show_path(@public_body), :class => "btn" %> + <%= link_to 'List all', admin_body_list_path, :class => "btn" %> + </div> + </div> + </div> + + <% if @public_body.info_requests.empty? %> + <%= form_tag(admin_body_destroy_path(@public_body), :class => "form form-inline") do %> + <%= hidden_field_tag(:public_body_id, { :value => @public_body.id } ) %> + <%= submit_tag _("Destroy {{name}}", :name => @public_body.name), :class => "btn btn-danger" %> (this is permanent!) + <% end %> + <% end %> + </div> + + </div> + <div class="span4"> + <%= render :partial => 'tag_help' %> + </div> +</div> diff --git a/app/views/admin_public_body/edit.rhtml b/app/views/admin_public_body/edit.rhtml deleted file mode 100644 index b19477a6b..000000000 --- a/app/views/admin_public_body/edit.rhtml +++ /dev/null @@ -1,30 +0,0 @@ -<h1><%=@title%></h1> - -<script type="text/javascript"> - $(function() { - $("#div-locales").tabs(); - }); -</script> - -<%= render :partial => 'tag_help' %> - -<div id="public_body_form"> - <% form_tag '../update/' + @public_body.id.to_s do %> - <%= render :partial => 'form' %> - <p><%= submit_tag 'Save', :accesskey => 's' %></p> - <% end %> - - <p> - <%= link_to 'Show', '../show/' + @public_body.id.to_s %> | - <%= link_to 'List all', '../list' %> - </p> - - <% if @public_body.info_requests.size == 0 %> - <% form_tag('../destroy/' + @public_body.id.to_s) do %> - <p> - <%= hidden_field_tag(:public_body_id, { :value => @public_body.id } ) %> - <%= submit_tag "Destroy " + @public_body.name %> (this is permanent!) - </p> - <% end %> - <% end %> -</div> diff --git a/app/views/admin_public_body/import_csv.rhtml b/app/views/admin_public_body/import_csv.html.erb index 1c6100838..18341ecf1 100644 --- a/app/views/admin_public_body/import_csv.rhtml +++ b/app/views/admin_public_body/import_csv.html.erb @@ -9,18 +9,18 @@ <pre id="error"><%=@errors %></pre> <% end %> -<% form_tag 'import_csv', :multipart => true do %> +<%= form_tag 'import_csv', :multipart => true do %> <p> - <% if @original_csv_file && @temporary_csv_file %> - CSV file: - <%= @original_csv_file %> - <%= hidden_field_tag :original_csv_file, @original_csv_file %> - <%= hidden_field_tag :temporary_csv_file, @temporary_csv_file %> - <%= link_to 'Clear current file', 'import_csv', :class => "btn btn-warning" %> - <% else %> - <label for="csv_file">CSV file:</label> - <%= file_field_tag :csv_file, :size => 40 %> - <% end %> + <% if @original_csv_file && @temporary_csv_file %> + CSV file: + <%= @original_csv_file %> + <%= hidden_field_tag :original_csv_file, @original_csv_file %> + <%= hidden_field_tag :temporary_csv_file, @temporary_csv_file %> + <%= link_to 'Clear current file', 'import_csv', :class => "btn btn-warning" %> + <% else %> + <label for="csv_file">CSV file:</label> + <%= file_field_tag :csv_file, :size => 40 %> + <% end %> </p> <p> @@ -31,15 +31,16 @@ <p> <label for="tag_behaviour">What to do with existing tags?</label> <%= select_tag 'tag_behaviour', - "<option value='add' selected>Add new tags to existing ones</option> - <option value='replace'>Replace existing tags with new ones</option>".html_safe + raw("<option value='add' selected>Add new tags to existing ones</option> + <option value='replace'>Replace existing tags with new ones</option>") %> </p> - <p><strong>CSV file format:</strong> A first row with the list of fields, - starting with '#', is optional but highly recommended. The fields 'name' - and 'request_email' are required; additionally, translated values are supported by - adding the locale name to the field name, e.g. 'name.es', 'name.de'... Example: + <p><strong>CSV file format:</strong>The first row should be a list + of fields, starting with '#'. The fields 'name' and + 'request_email' are required; additionally, translated values are + supported by adding the locale name to the field name, + e.g. 'name.es', 'name.de'... Example: </p> <blockquote> @@ -57,7 +58,7 @@ actually altering the database. Choose <strong>upload</strong> to actually make the changes. In either case, you will be shown any errors, or details of the changes. When uploading, any changes since last import will be - overwritten - e.g. email addresses changed back. + overwritten – e.g. email addresses changed back. </p> <p><strong>Note:</strong> The import tag will also be added to the imported bodies diff --git a/app/views/admin_public_body/list.html.erb b/app/views/admin_public_body/list.html.erb new file mode 100644 index 000000000..3d7d9c4cd --- /dev/null +++ b/app/views/admin_public_body/list.html.erb @@ -0,0 +1,42 @@ +<% if @query.nil? %> + <% @title = _('Listing public authorities') %> +<% else %> + <% @title = _("Listing public authorities matching '{{query}}'", :query => @query ) %> +<% end %> + +<h1><%=@title%></h1> + +<div class="btn-toolbar"> + <div class="btn-group"> + <%= link_to 'New public authority', admin_body_new_path, :class => "btn btn-primary" %> + </div> + <div class="btn-group"> + <%= link_to 'Import from CSV file', admin_body_import_csv_path, :class => "btn btn-warning" %> + </div> +</div> + +<%= form_tag({}, :method => "get", :class => "form form-search") do %> + <%= text_field_tag 'query', params[:query], { :size => 30, :class => "input-large search-query" } %> + <%= submit_tag "Search", :class => "btn" %> + <% if !@query.nil? %> + <%= link_to 'Show all', admin_body_list_path, :class => "btn" %> + <% end %><br> + (substring search in names and emails; exact match of tags) +<% end %> + + +<% if @public_bodies_by_tag.size > 0 %> + <h2>Exact tag matches (<%= @public_bodies_by_tag.size %> total)</h2> + <%= render :partial => 'one_list', :locals => { :bodies => @public_bodies_by_tag, :table_name => 'exact' } %> +<% end %> + +<% if @public_bodies.size > 0 %> + <% if @query.nil? %> + <h2>All authorities</h2> + <% else %> + <h2>Substring search matches (<%= @public_bodies.total_entries %> total)</h2> + <% end %> + <%= render :partial => 'one_list', :locals => { :bodies => @public_bodies, :table_name => 'substring' } %> +<% end %> + +<%= will_paginate(@public_bodies, :class => "paginator") %> diff --git a/app/views/admin_public_body/list.rhtml b/app/views/admin_public_body/list.rhtml deleted file mode 100644 index c28060604..000000000 --- a/app/views/admin_public_body/list.rhtml +++ /dev/null @@ -1,40 +0,0 @@ -<% if @query.nil? %> - <% @title = 'Listing public authorities' %> -<% else %> - <% @title = "Listing public authorities matching '" + @query + "'" %> -<% end %> - -<h1><%=@title%></h1> - -<p> - <% if !@query.nil? %> - <%= link_to 'Show all', 'list' %> | - <% end %> - <%= link_to 'New public authority', 'new' %> - | <%= link_to 'Import from CSV file', 'import_csv' %> -</p> - -<% form_tag("", :method => "get") do %> - <p> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> - <%= submit_tag "Search" %> (substring search in names and emails; exact match of tags) - </p> -<% end %> - - -<% if @public_bodies_by_tag.size > 0 %> - <h2>Exact tag matches (<%= @public_bodies_by_tag.size %> total)</h2> - <%= render :partial => 'one_list', :locals => { :bodies => @public_bodies_by_tag, :table_name => 'exact' } %> -<% end %> - -<% if @public_bodies.size > 0 %> - <% if @query.nil? %> - <h2>All authorities</h2> - <% else %> - <h2>Substring search matches (<%= @public_bodies.total_entries %> total)</h2> - <% end %> - <%= render :partial => 'one_list', :locals => { :bodies => @public_bodies, :table_name => 'substring' } %> -<% end %> - -<%= will_paginate(@public_bodies) %> - diff --git a/app/views/admin_public_body/missing_scheme.rhtml b/app/views/admin_public_body/missing_scheme.html.erb index 2ea55ae00..2ea55ae00 100644 --- a/app/views/admin_public_body/missing_scheme.rhtml +++ b/app/views/admin_public_body/missing_scheme.html.erb diff --git a/app/views/admin_public_body/new.html.erb b/app/views/admin_public_body/new.html.erb new file mode 100644 index 000000000..24b27d7af --- /dev/null +++ b/app/views/admin_public_body/new.html.erb @@ -0,0 +1,25 @@ +<% @title = 'New public authority' %> + +<h1><%=@title%></h1> +<div class="row"> + <div class="span8"> + <div id="public_body_form"> + <%= form_for @public_body, :as => :public_body, :url => admin_body_create_path, :html => {:class => "form form-horizontal"} do |f| %> + <%= render :partial => 'form', :locals => {:f => f} %> + + + <div class="form-actions"> + <%= f.submit "Create", :class => "btn btn-primary" %> + </div> + <% end %> + <div class="row"> + <div class="span8 well"> + <%= link_to 'List all', admin_body_list_path, :class => "btn" %> + </div> + </div> + </div> + </div> + <div class="span4"> + <%= render :partial => 'tag_help' %> + </div> +</div> diff --git a/app/views/admin_public_body/new.rhtml b/app/views/admin_public_body/new.rhtml deleted file mode 100644 index 047d5a5bb..000000000 --- a/app/views/admin_public_body/new.rhtml +++ /dev/null @@ -1,22 +0,0 @@ -<% @title = 'New public authority' %> - -<h1><%=@title%></h1> - -<script type="text/javascript"> - $(function() { - $("#div-locales").tabs(); - }); -</script> - -<%= render :partial => 'tag_help' %> - -<div id="public_body_form"> - <% form_tag './create/' + @public_body.id.to_s do %> - <%= render :partial => 'form' %> - <p><%= submit_tag "Create" %></p> - <% end %> - - <p> - <%= link_to 'List all', 'list' %> - </p> -</div> diff --git a/app/views/admin_public_body/show.html.erb b/app/views/admin_public_body/show.html.erb new file mode 100644 index 000000000..f8161db26 --- /dev/null +++ b/app/views/admin_public_body/show.html.erb @@ -0,0 +1,93 @@ +<% @title = _("Public authority – {{name}}", :name => h(@public_body.name)) %> + +<h1><%=@title%></h1> + +<table class="table table-striped table-condensed"> + <tbody> + <% @public_body.for_admin_column do |name, value, type, column_name| %> + <tr> + <td> + <b><%=name%></b> + </td> + <td> + <% if ['home_page', 'publication_scheme', 'disclosure_log'].include? column_name %> + <%= link_to(h(value), value) %> + <% elsif column_name == 'request_email' %> + <%= link_to(h(value), "mailto:#{value}") %> + <% unless @public_body.is_requestable? %> + <%=_("not requestable due to: {{reason}}", :reason => h(@public_body.not_requestable_reason))%><% if @public_body.is_followupable? %>; <%=_("but followupable")%><% end %> + <% end %> + <% else %> + <%=h value %> + <% end %> + </td> + </tr> + <% end %> + <tr> + <td> + <b><%=_("Calculated home page")%></b> + </td> + <td> + <% unless @public_body.calculated_home_page.nil? %> + <%= link_to(h(@public_body.calculated_home_page), @public_body.calculated_home_page) %> + <% else %> + <%=_("*unknown*")%> + <% end %> + </td> + </tr> + <tr> + <td> + <b><%=_("Tags")%></b> + </td> + <td> + <%= render :partial => 'tags', :locals => { :body => @public_body} %> + </td> + </tr> + </tbody> +</table> +<%= link_to _("Edit"), admin_body_edit_path(@public_body), :class => "btn btn-primary" %> +<% unless @public_body.url_name.nil? %> + <%=link_to _("Public page"), public_body_path(@public_body), :class => "btn" %> +<% else %> + <%=_("Public page not available")%> +<% end %> +<hr> +<h2>History</h2> +<%# There may be an option to versions() to specify order, but I can't find it. TB 2009-03-09 %> +<% versions = @public_body.reverse_sorted_versions; versions.each_with_index do |historic_public_body, i| %> + <div class="row"> + <div class="span2"> + <b> + <%= _("Version {{version}}", :version => historic_public_body.version)%> + </b> + </div> + <div class="span4"> + <%= I18n.l(historic_public_body.updated_at, :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(historic_public_body.updated_at)) %>) + </div> + <% if i == versions.length - 1 %> + <div class="span6"> + <p>“<%= h(historic_public_body.last_edit_comment) %>”</p> + <ul> + <li><%=_("This is the first version.")%></li> + </ul> + </div> + <% else %> + <div class="span6"> + <p>“<%= h(historic_public_body.last_edit_comment) %>”</p> + <ul> + <% historic_public_body.compare(versions[i+1]) do |change| %> + <li><%= _("{{thing_changed}} was changed from <code>{{from_value}}</code> to <code>{{to_value}}</code>", :thing_changed => change[:name], :from_value => (change[:from] or "-"), :to_value => (change[:to] or "-")) %></li> + <% end %> + </ul> + </div> + <% end %> + </div> +<% end %> +<hr> +<h2>Requests</h2> +<%= render :partial => 'admin_request/some_requests', :locals => { :info_requests => @info_requests } %> +<%= will_paginate(@info_requests, :class => "paginator") %> +<hr> +<h2>Track things</h2> +<%= render :partial => 'admin_track/some_tracks', :locals => { :track_things => @public_body.track_things, :include_destroy => true } %> diff --git a/app/views/admin_public_body/show.rhtml b/app/views/admin_public_body/show.rhtml deleted file mode 100644 index cee306988..000000000 --- a/app/views/admin_public_body/show.rhtml +++ /dev/null @@ -1,83 +0,0 @@ -<% @title = "Public authority - " + h(@public_body.name) %> - -<h1><%=@title%></h1> - -<p> -<% - columns = PublicBody.content_columns + [] # force dup - columns.delete_if {|c| ['last_edit_comment'].include?(c.name)} - - for column in columns %> - <b><%= column.human_name %>:</b> - <% if ['home_page', 'publication_scheme', 'disclosure_log'].include? column.name %> - <%= link_to(h(@public_body.send(column.name)), @public_body.send(column.name)) %> - <% elsif column.name == 'request_email' %> - <%= link_to(h(@public_body.send(column.name)), "mailto:#{@public_body.send(column.name)}") %> - <% if !@public_body.is_requestable? %> - (not requestable due to: <%=h @public_body.not_requestable_reason %><% if @public_body.is_followupable? %>; but followupable<% end %>) - <% end %> - <% else %> - <%=h @public_body.send(column.name) %> - <% end %> - <br/> -<% end %> -<b>Calculated home page:</b> -<% if !@public_body.calculated_home_page.nil? %> - <%= link_to(h(@public_body.calculated_home_page), @public_body.calculated_home_page) %> -<% else %> - *unknown* -<% end %> -<br/><b>Tags:</b> <%= render :partial => 'tags', :locals => { :body => @public_body} %> -<br/> -</p> - -<p> - <%= - # url_name can be missing if the name hasn't been set for this locale - if !@public_body.url_name.nil? - link_to 'Public page', main_url(public_body_url(@public_body)) - else - 'Public page not available' - end - %> - | <%= link_to 'Edit', '../edit/' + @public_body.id.to_s %> -</p> - -<h2>History</h2> -<table border="1"> -<tr> -<th>Updated at</th> -<% - history_columns = PublicBody.content_columns + [] # force dup - history_columns.delete_if {|c| ['created_at', 'updated_at', 'first_letter', 'api_key'].include?(c.name)} - for column in history_columns %> - <th><%= column.human_name %></th> -<% end %> -</tr> -<%# There may be an option to versions() to specify order, but I can't find it. TB 2009-03-09 %> -<% for historic_public_body in @public_body.reverse_sorted_versions %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h historic_public_body.updated_at %></td> - <% for column in history_columns %> - <% - value = h(historic_public_body.send(column.name)) - if column.name == 'last_edit_comment' - value = historic_public_body.last_edit_comment_for_html_display - end - # Highlight entries which have changed since previous version - changed = (!['version', 'last_edit_editor', 'last_edit_comment'].include?(column.name)) && ((historic_public_body.send(column.name) != @public_body.sorted_versions[historic_public_body.version - 2].send(column.name)) || (historic_public_body.version == 1)) %> - <td <%= changed ? ' class="entry_changed" '.html_safe: '' %> > - <%=value%> - </td> - <% end %> - </tr> -<% end %> -</table> - -<h2>Requests</h2> -<%= render :partial => 'admin_request/some_requests', :locals => { :info_requests => @public_body.info_requests } %> - -<h2>Track things</h2> -<%= render :partial => 'admin_track/some_tracks', :locals => { :track_things => @public_body.track_things } %> - - diff --git a/app/views/admin_public_body_change_requests/_response.html.erb b/app/views/admin_public_body_change_requests/_response.html.erb new file mode 100644 index 000000000..7fda8b7f8 --- /dev/null +++ b/app/views/admin_public_body_change_requests/_response.html.erb @@ -0,0 +1,15 @@ +<h3>Response to change request (will be emailed to user):</h3> +<%= hidden_field_tag 'change_request_id', @change_request.id %> +<div class="control-group" id="change_request_user_subject"> + <label for="change_request_user_subject_field" class="control-label">Subject of email:</label> + <div class="controls"> + <%= text_field_tag "subject", (params[:subject] || @change_request.default_response_subject), {:id => "change_request_user_subject_field", :class => "span6"} %> + </div> +</div> + +<div class="control-group" id="change_request_user_response"> + <label for="change_request_user_response_field" class="control-label">Response</label> + <div class="controls"> + <%= text_area_tag "response", (params[:response] || h(@change_request_user_response)), {:id => "change_request_user_response_field", :rows => 10, :class => 'span6'} %> + </div> +</div> diff --git a/app/views/admin_public_body_change_requests/add_accepted.txt.erb b/app/views/admin_public_body_change_requests/add_accepted.txt.erb new file mode 100644 index 000000000..fb22466b0 --- /dev/null +++ b/app/views/admin_public_body_change_requests/add_accepted.txt.erb @@ -0,0 +1,9 @@ +<%= _("Dear {{user_name}},", :user_name => @change_request.get_user_name) %> + +<%= _("Thanks for your suggestion to add {{public_body_name}}. It's been added to the site here:", :public_body_name => @change_request.public_body_name) %> + +<%= _("[Authority URL will be inserted here]")%> + +<%= _("Yours,") %> + +<%= _("The {{site_name}} team.", :site_name => site_name) %> diff --git a/app/views/admin_public_body_change_requests/edit.html.erb b/app/views/admin_public_body_change_requests/edit.html.erb new file mode 100644 index 000000000..cc9c5b5d9 --- /dev/null +++ b/app/views/admin_public_body_change_requests/edit.html.erb @@ -0,0 +1,8 @@ +<h1><%=@title%></h1> + +<%= form_tag admin_change_request_update_path(@change_request), :class => "form form-horizontal" do %> + <%= render :partial => 'admin_public_body_change_requests/response'%> + <div class="form-actions"> + <%= submit_tag 'Close', :accesskey => 'c', :class => "btn btn-primary" %> + </div> +<% end %> diff --git a/app/views/admin_public_body_change_requests/update_accepted.txt.erb b/app/views/admin_public_body_change_requests/update_accepted.txt.erb new file mode 100644 index 000000000..9c29c959b --- /dev/null +++ b/app/views/admin_public_body_change_requests/update_accepted.txt.erb @@ -0,0 +1,7 @@ +<%= _("Dear {{user_name}},", :user_name => @change_request.get_user_name) %> + +<%= _("Thanks for your suggestion to update the email address for {{public_body_name}} to {{public_body_email}}. This has now been done and any new requests will be sent to the new address.", :public_body_name => @change_request.public_body.name, :public_body_email => @change_request.public_body_email) %> + +<%= _("Yours,") %> + +<%= _("The {{site_name}} team.", :site_name => site_name) %> diff --git a/app/views/admin_request/_incoming_message_actions.html.erb b/app/views/admin_request/_incoming_message_actions.html.erb new file mode 100644 index 000000000..4cf099b53 --- /dev/null +++ b/app/views/admin_request/_incoming_message_actions.html.erb @@ -0,0 +1,45 @@ +<fieldset class="form-horizontal"> + <legend>Actions</legend> + <%= form_tag admin_incoming_redeliver_path, :class => "form form-inline" do %> + <div class="control-group"> + <label class="control-label" for="url_title_<%= incoming_message.id %>">Redeliver message to one or more other requests</label> + <div class="controls"> + <% if @info_requests && @info_requests.size == 1 %> + <%= text_field_tag 'url_title', @info_requests[0].url_title, { :size => 20, :id => "url_title_#{incoming_message.id}" } %> + <% else %> + <%= text_field_tag 'url_title', "", { :size => 20, :id => "url_title_#{incoming_message.id}" } %> + <% end %> + <%= hidden_field_tag 'redeliver_incoming_message_id', incoming_message.id, :id => nil %> + <%= submit_tag "Redeliver to another request", :class => "btn" %> + <p class="help-block"><code>id</code> or <code>url_title</code>; you can supply more than one, separated by commas</p> + </div> + </div> + <% end %> + <div class="control-group"> + <label class="control-label">Generate FOI officer upload URL</label> + <div class="controls"> + <%= link_to 'Generate and take me there', admin_request_generate_upload_url_path(incoming_message.info_request, :incoming_message_id => incoming_message.id), :class => "btn" %> + </div> + </div> + + <%= form_tag admin_incoming_destroy_path, :class => "form form-inline" do %> + <div class="control-group"> + <label class="control-label" for="destroy_message_<%= incoming_message.id %>">Destroy message</label> + <div class="controls"> + <%= hidden_field_tag 'incoming_message_id', incoming_message.id, :id => nil %> + <%= submit_tag "Destroy message", :class => "btn btn-danger", :confirm => "This is permanent! Are you sure?", :id => "destroy_message_#{incoming_message.id}" %> + </div> + </div> + <% end %> + + <% if @raw_email.nil? %> + <%# we're not on the raw_email page itself %> + <div class="control-group"> + <label class="control-label">Inspect email</label> + <div class="controls"> + <%= link_to "View raw email", admin_request_show_raw_email_path(incoming_message.raw_email_id), :class => "btn" %> + </div> + </div> + <% end %> + +</fieldset> diff --git a/app/views/admin_request/_incoming_message_actions.rhtml b/app/views/admin_request/_incoming_message_actions.rhtml deleted file mode 100644 index 569132861..000000000 --- a/app/views/admin_request/_incoming_message_actions.rhtml +++ /dev/null @@ -1,26 +0,0 @@ -<% form_tag '../redeliver_incoming' do %> - <div> - id or url_title of request (or a list of requests, comma-separated): - <% if @info_requests && @info_requests.size == 1 %> - <%= text_field_tag 'url_title', @info_requests[0].url_title, { :size => 20 } %> - <% else %> - <%= text_field_tag 'url_title', "", { :size => 20 } %> - <% end %> - <%= hidden_field_tag 'redeliver_incoming_message_id', incoming_message.id %> - <%= submit_tag "Redeliver to another request" %> - </div> -<% end %> - -<p> -<%= link_to 'FOI officer upload URL', '../generate_upload_url/' + incoming_message.info_request.id.to_s + "?incoming_message_id=" + incoming_message.id.to_s %> -</p> - -<% form_tag '../destroy_incoming' do %> - <div> - <%= hidden_field_tag 'incoming_message_id', incoming_message.id %> - Warning, this is permanent! ---> - <%= submit_tag "Destroy message" %> - </div> -<% end %> - - diff --git a/app/views/admin_request/_some_annotations.html.erb b/app/views/admin_request/_some_annotations.html.erb new file mode 100644 index 000000000..dfd46f828 --- /dev/null +++ b/app/views/admin_request/_some_annotations.html.erb @@ -0,0 +1,48 @@ +<% if comments.size > 0 %> + <div class="accordion" id="comments"> + <% for comment in comments %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#comment_<%=comment.id%>" data-toggle="collapse" data-parent="#comments"><%= chevron_right %></a> + <%= link_to admin_request_edit_comment_path(comment) do %> + #<%=comment.id%> + -- + <%=h(comment.user.name)%> + <%=admin_value(comment.created_at)%> + <% end %> + <blockquote class="incoming-message"> + <%= truncate(comment.body, :length => 400) %> + </blockquote> + </div> + <div id="comment_<%=comment.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <tr> + <td colspan="2"> + By <%= user_both_links(comment.user) %> + </td> + </tr> + <% comment.for_admin_column do |name, value, type, column_name |%> + <tr> + <td> + <b><%=name%></b> + </td> + <td> + <% if column_name == 'body' && !comment.visible %> + <s><%=h comment.send(column_name) %></s> + <% else %> + <%=h comment.send(column_name) %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + </div> +<% else %> + <p>None yet.</p> +<% end %> + diff --git a/app/views/admin_request/_some_requests.html.erb b/app/views/admin_request/_some_requests.html.erb new file mode 100644 index 000000000..cff94956d --- /dev/null +++ b/app/views/admin_request/_some_requests.html.erb @@ -0,0 +1,32 @@ +<div class="accordion" id="requests"> + <% for info_request in info_requests %> + <div class="accordion-group"> + <div class="accordion-heading accordion-toggle row"> + <span class="item-title span6"> + <a href="#request_<%=info_request.id%>" data-toggle="collapse" data-parent="requests"><%= chevron_right %></a> + <%= link_to(info_request.title, admin_request_show_path(info_request), :title => "view full details") %> + </span> + <span class="item-metadata span6"> + <%= user_admin_link_for_request(info_request) %> <%= arrow_right %> <%= link_to("#{info_request.public_body.name}", admin_body_show_path(info_request.public_body)) %>, <%= time_ago_in_words(info_request.updated_at) %> ago + </span> + </div> + <div id="request_<%=info_request.id%>" class="item-detail accordion-body collapse row"> + <% info_request.for_admin_column do | name, value, type | %> + <div> + <span class="span6"> + <%= h name %> + </span> + <span class="span6"> + <% if type == 'datetime' %> + <%= I18n.l(value, :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(value)) %>) + <% else %> + <%=h value %> + <% end %> + </span> + </div> + <% end %> + </div> + </div> + <% end %> +</div> diff --git a/app/views/admin_request/_some_requests.rhtml b/app/views/admin_request/_some_requests.rhtml deleted file mode 100644 index dc11e0f55..000000000 --- a/app/views/admin_request/_some_requests.rhtml +++ /dev/null @@ -1,31 +0,0 @@ -<table> - <tr> - <th>Title</th> - <th>Authority</th> - <th>User</th> - <% for column in InfoRequest.content_columns.map { |c| c.human_name } - [ "Url title", "Title" ] %> - <th><%= column %></th> - <% end %> - </tr> - -<% for info_request in info_requests %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%= request_both_links(info_request) %></td> - <td><%= public_body_both_links(info_request.public_body) %></td> - <% if info_request.is_external? %> - <% if info_request.external_user_name.nil? %> - <td><i><%= _("Anonymous user") %></i></td> - <% else %> - <td><%= h(info_request.external_user_name) %></td> - <% end %> - <% else %> - <td><%= user_both_links(info_request.user) %></td> - <% end %> - <% for column in InfoRequest.content_columns.map { |c| c.name } - [ "title", "url_title" ] %> - <td><%=h info_request.send(column) %></td> - <% end %> - </tr> -<% end %> -</table> - - diff --git a/app/views/admin_request/_tags.rhtml b/app/views/admin_request/_tags.html.erb index 22fbf13c8..22fbf13c8 100644 --- a/app/views/admin_request/_tags.rhtml +++ b/app/views/admin_request/_tags.html.erb diff --git a/app/views/admin_request/edit.rhtml b/app/views/admin_request/edit.html.erb index 8fa2a1fe2..0e9c68aea 100644 --- a/app/views/admin_request/edit.rhtml +++ b/app/views/admin_request/edit.html.erb @@ -2,7 +2,7 @@ <%= error_messages_for 'info_request' %> -<% form_tag '../update/' + @info_request.id.to_s do %> +<%= form_tag admin_request_update_path(@info_request) do %> <p><label for="info_request_title"><strong>Title</strong></label> (warning: editing this will break URLs right now)<br/> <%= text_field 'info_request', 'title', :size => 50 %></p> @@ -22,16 +22,14 @@ </p> <p><label for="info_request_described_state"><strong>Described state</strong></label> - <%= select( 'info_request', "described_state", InfoRequest.enumerate_states ) %>; + <%= select( 'info_request', "described_state", InfoRequest.enumerate_states) %> <label for="info_request_awaiting_description"><strong>Awaiting description</strong></label> - <%= select('info_request', "awaiting_description", [["Yes - needs state updating",true],["No - state is up to date",false]]) %> + <%= select('info_request', "awaiting_description", [["Yes – needs state updating",true],["No – state is up to date",false]]) %> <br/>(don't forget to change 'awaiting description' when you set described state)<br/> </p> - - <p><label for="info_request_comments_allowed"><strong>Are comments allowed?</strong></label> - <%= select('info_request', "comments_allowed", [["Yes – comments allowed", true], ["No – comments disabled", false]]) %> - </p> - + <p><label for="info_request_comments_allowed"><strong>Are comments allowed?</strong></label> + <%= select('info_request', "comments_allowed", [["Yes – comments allowed", true], ["No – comments disabled", false]]) %> + </p> <p><label for="info_request_tag_string"><strong>Tags</strong> <small>(space separated, can use key:value)</small></label><br/> <%= text_field 'info_request', 'tag_string', :size => 60 %></p> @@ -45,13 +43,13 @@ <% end %> <p> -<%= link_to 'Show', '../show/' + @info_request.id.to_s %> | -<%= link_to 'List all', '../list' %> +<%= link_to 'Show', admin_request_show_path(@info_request) %> | +<%= link_to 'List all', admin_request_list_path %> </p> <hr> -<% form_tag '../destroy/' + @info_request.id.to_s do %> +<%= form_tag admin_request_destroy_path(@info_request) do %> <p> <strong>This is permanent and irreversible!</strong> <%= submit_tag 'Destroy request entirely' %> <br>Use it mainly if someone posts private information, e.g. made a Data Protection request. It diff --git a/app/views/admin_request/edit_comment.rhtml b/app/views/admin_request/edit_comment.html.erb index ee43e849a..2cf49a4a8 100644 --- a/app/views/admin_request/edit_comment.rhtml +++ b/app/views/admin_request/edit_comment.html.erb @@ -2,13 +2,13 @@ <%= error_messages_for 'comment' %> -<% form_tag '../update_comment/' + @comment.id.to_s do %> +<%= form_tag admin_request_update_comment_path(@comment) do %> <p><label for="comment_body">Body of annotation</label><br/> <%= text_area 'comment', 'body', :rows => 10, :cols => 60 %></p> <p><label for="comment_visible">Visible</label> - <%= select('comment', "visible", [["Yes - show comment",true],["No - hide comment",false]]) %> + <%= select('comment', "visible", [["Yes – show comment",true],["No – hide comment",false]]) %> </p> @@ -16,7 +16,7 @@ <% end %> <p> -<%= link_to 'Show', '../show/' + @comment.info_request.id.to_s %> | -<%= link_to 'List all', '../list' %> +<%= link_to 'Show', admin_request_show_path(@comment.info_request) %> | +<%= link_to 'List all', admin_request_list_path %> </p> diff --git a/app/views/admin_request/edit_outgoing.rhtml b/app/views/admin_request/edit_outgoing.rhtml deleted file mode 100644 index 4932cb52f..000000000 --- a/app/views/admin_request/edit_outgoing.rhtml +++ /dev/null @@ -1,33 +0,0 @@ -<h1>Edit outgoing message</h1> - -<%= error_messages_for 'outgoing_message' %> - -<% form_tag '../update_outgoing/' + @outgoing_message.id.to_s do %> - - <p><label for="outgoing_message_body">Body of message</label><br/> - <%= text_area 'outgoing_message', 'body', :rows => 10, :cols => 60 %></p> - - <p><strong>Note:</strong> This is mainly to be used to excise information - that users inadvertently put in their messages, not realising it would be - public. It will already have been sent to the public authority, and their - reply may also include that information and be automatically published on - this site. You could also use this to edit a message before resending it, but - only the edited version will be shown on the public page if you do that.</p> - - <p><%= submit_tag 'Save', :accesskey => 's' %></p> -<% end %> - -<p> -<%= link_to 'Show', '../show/' + @outgoing_message.info_request.id.to_s %> | -<%= link_to 'List all', '../list' %> -</p> - -<% form_tag '../destroy_outgoing' do %> - <div> - <%= hidden_field_tag 'outgoing_message_id', @outgoing_message.id %> - Warning, this is permanent! ---> - <%= submit_tag "Destroy outgoing message" %> - </div> -<% end %> - - diff --git a/app/views/admin_request/hidden_user_explanation.html.erb b/app/views/admin_request/hidden_user_explanation.html.erb new file mode 100644 index 000000000..d0a5d727d --- /dev/null +++ b/app/views/admin_request/hidden_user_explanation.html.erb @@ -0,0 +1,10 @@ +<%= _("Dear {{name}},", :name => name_to) %> + +<%= _("Your request '{{request}}' at {{url}} has been reviewed by moderators.", :request => info_request.title.html_safe, :url => info_request_url) %> + +<%= reason == 'not_foi' ? _("We consider it is not a valid FOI request, and have therefore hidden it from other users.") : _("We consider it to be vexatious, and have therefore hidden it from other users.") %> <%= _("You will still be able to view it while logged in to the site. Please reply to this email if you would like to discuss this decision further.") %> + +<%= _("Yours,") %> + +<%= _("The {{site_name}} team.", :site_name => site_name) %> + diff --git a/app/views/admin_request/hidden_user_explanation.rhtml b/app/views/admin_request/hidden_user_explanation.rhtml deleted file mode 100644 index 64387ffee..000000000 --- a/app/views/admin_request/hidden_user_explanation.rhtml +++ /dev/null @@ -1,9 +0,0 @@ -Dear <%= name_to %>, - -Your request '<%= info_request.title %>' at <%= info_request_url %> has been reviewed by moderators. - -We consider it <% if reason == 'not_foi' %>is not a valid FOI request<% else %>to be vexatious<% end%>, and have therefore hidden it from other users. You will still be able to view it while logged in to the site. Please reply to this email if you would like to discuss this decision further. - -Yours, - -The <%= site_name %> team. diff --git a/app/views/admin_request/list.html.erb b/app/views/admin_request/list.html.erb new file mode 100644 index 000000000..2bd3e3326 --- /dev/null +++ b/app/views/admin_request/list.html.erb @@ -0,0 +1,13 @@ +<% @title = _("Listing FOI requests") %> + +<h1><%=@title%></h1> + +<%= form_tag({}, :method => "get", :class => "form form-search") do %> + <%= text_field_tag 'query', params[:query], { :size => 30, :class => "input-large search-query" } %> + <%= submit_tag "Search", :class => "btn" %> (substring search, titles only) +<% end %> + +<%= render :partial => 'some_requests', :locals => { :info_requests => @info_requests } %> + +<%= will_paginate(@info_requests, :class => "paginator") %> + diff --git a/app/views/admin_request/list.rhtml b/app/views/admin_request/list.rhtml deleted file mode 100644 index cb328543e..000000000 --- a/app/views/admin_request/list.rhtml +++ /dev/null @@ -1,15 +0,0 @@ -<% @title = 'Listing FOI/EIR requests' %> - -<h1><%=@title%></h1> - -<% form_tag("", :method => "get") do %> - <p> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> - <%= submit_tag "Search" %> (substring search, titles only) - </p> -<% end %> - -<%= render :partial => 'some_requests', :locals => { :info_requests => @info_requests } %> - -<%= will_paginate(@info_requests) %> - diff --git a/app/views/admin_request/list_old_unclassified.rhtml b/app/views/admin_request/list_old_unclassified.rhtml deleted file mode 100644 index 2e75c2174..000000000 --- a/app/views/admin_request/list_old_unclassified.rhtml +++ /dev/null @@ -1,16 +0,0 @@ -<% @title = "Unclassified responses" %> - -<h1><%=@title%></h1> - -<p>Classify responses that are still unclassified <%=InfoRequest::OLD_AGE_IN_DAYS.inspect %> after response:</p> -<ul> -<% for @request in @info_requests %> - <li> - <%= request_both_links(@request) %> - – <%=simple_date(@request.get_last_response_event.created_at)%> - </li> -<% end %> -</ul> -<%= will_paginate(@info_requests) %> - - diff --git a/app/views/admin_request/show.html.erb b/app/views/admin_request/show.html.erb new file mode 100644 index 000000000..2589e52b4 --- /dev/null +++ b/app/views/admin_request/show.html.erb @@ -0,0 +1,346 @@ +<% @title = _("FOI request – {{title}}", :title => h(@info_request.title)) %> + +<h1><%=@title%></h1> + +<%= form_tag admin_request_move_request_path, { :class => "form form-horizontal" } do %> + <%= hidden_field_tag 'info_request_id', @info_request.id %> + <div class="accordion" id="info_request"> + <div class="accordion-group"> + <div class="accordion-heading"> + <span class="item-title"> + <a href="#metadata_<%=@info_request.id%>" data-toggle="collapse" data-parent="#info_request"><%= chevron_down %></a>Request metadata + </span> + </div> + <div id="metadata_<%=@info_request.id%>" class="accordion-body collapse in"> + <table class="table table-striped table-condensed"> + <thead> + <tr> + <td colspan="2"> + <%= link_to 'Edit metadata', admin_request_edit_path(@info_request), :class => "btn" %> + </td> + </tr> + </thead> + <tbody> + <tr> + <th>Public page:</th> + <td><%= link_to request_url(@info_request), request_path(@info_request) %></td> + </tr> + <% @info_request.for_admin_column do |name, value, type, column_name|%> + <tr> + <td> + <b><%= name %>:</b> + </td> + <td> + <% if type == 'datetime' %> + <%= I18n.l(value, :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(value)) %>) + <% else %> + <%= h value %> + <% end %> + <% if column_name == 'described_state' %> + <ul> + <li><strong>Initial request last sent at:</strong> <%= @info_request.calculate_status %></li> + <li><strong>Initial request last sent at:</strong> <%=@info_request.date_initial_request_last_sent_at.to_date %></li> + <li><strong>Date response required by:</strong> <%= @info_request.date_response_required_by %></li> + <li><strong>Very overdue after:</strong> <%= @info_request.date_very_overdue_after %></li> + </ul> + <% end %> + </td> + </tr> + <% end %> + <tr> + <td> + <b>Created by</b> + </td> + <td> + <% if @info_request.is_external? %> + <%= link_to(eye, @info_request.external_url, :title => "view URL of original request on external website") %> + <%= @info_request.public_body.name %> on behalf of <%= (@info_request.user_name || _('an anonymous user'))%> (using API) + <% else %> + <%= user_both_links(@info_request.user) %> + <%= link_to _("move..."), "#", :class => "btn btn-mini btn-warning toggle-hidden" %> + <div style="display:none;"> + <strong>url_name of new user:</strong> + <%= text_field_tag 'user_url_name', "", { :size => 20 } %> + <%= submit_tag "Move request to user", :class => "btn btn-info" %> + </div> + <% end %> + </td> + </tr> + <tr> + <td> + <b>Public authority:</b> + </td> + <td> + <%= public_body_both_links(@info_request.public_body) %> + <%= link_to "move...", "#", :class => "btn btn-mini btn-warning toggle-hidden" %> + <div style="display:none;"> + <strong>url_name of new authority:</strong> + <%= text_field_tag 'public_body_url_name', "", { :size => 20 } %> + <%= submit_tag "Move request to authority", :class => "btn btn-info" %> + </div> + </td> + </tr> + <tr> + <td> + <b><%=_("Incoming email address")%></b> + </td> + <td> + <%= link_to h(@info_request.incoming_email), "mailto:#{@info_request.incoming_email}" %> + </td> + </tr> + <tr> + <td> + <b><%=_("Tags")%></b> + </td> + <td> + <%= render :partial => 'tags', :locals => { :info_request => @info_request} %> + </td> + </tr> + </tbody> + <tfoot> + <tr> + <td colspan="2"> + <%= link_to 'Edit metadata', admin_request_edit_path(@info_request), :class => "btn" %> + </td> + </tr> + </tfoot> + </table> + </div> + </div> + </div> +<% end %> +<fieldset class="form-horizontal"> + <legend>Actions</legend> + <div class="control-group"> + <label class="control-label"> + FOI officer upload URL + </label> + <div class="controls"> + <%= link_to 'Generate URL', admin_request_generate_upload_url_path(@info_request), :class => "btn" %> + <p class="help-block">(see also option to general URLs for individual incoming messages below)</p> + </div> + </div> + <%= form_tag admin_request_hide_path(@info_request), :class => "form form-inline", :id => "hide_request_form", 'data-info-request-id' => @info_request.id.to_s do %> + <div class="control-group"> + <% if @info_request.is_external? %> + <label class="control-label">Hide the request:</label> + <% else %> + <label class="control-label">Hide the request and notify the user:</label> + <% end %> + + <div class="controls" id="request_hidden_user_explanation_reasons"> + <% if ['hidden', 'requester_only'].include? @info_request.prominence %> + <p class="help-block">This request has already been hidden</p> + <% else %> + <label class="radio inline"> + <%= radio_button_tag "reason", "not_foi" %> <%= _("Not a valid FOI request") %> + </label> + <label class="radio inline"> + <%= radio_button_tag "reason", "vexatious" %> <%= _("A vexatious request") %> + </label> + <% end %> + </div> + </div> + + <% if !['hidden', 'requester_only'].include? @info_request.prominence %> + <% if ! @info_request.is_external? %> + + <div class="control-group" id="request_hidden_user_subject"> + <label for="request_hidden_user_subject_field" class="control-label">Subject of email:</label> + <div class="controls"> + <%= text_field_tag "subject", _("Your request on {{site_name}} hidden", :site_name => site_name), {:id => "request_hidden_user_subject_field", :cols => 100} %> + </div> + </div> + + <div class="control-group" id="request_hidden_user_explanation"> + <label for="request_hidden_user_explanation_field" class="control-label">Reason for hiding the request (will be emailed to user):</label> + <div class="controls"> + <%= text_area_tag "explanation", h(@request_hidden_user_explanation), {:id => "request_hidden_user_explanation_field"} %> + </div> + </div> + + <% end %> + <div class="form-actions" id="request_hide_button"> + <%= submit_tag _("Hide request"), :class => "btn" %> + </div> + <% end %> + <% end %> +</fieldset> + +<hr> +<h2>Events</h2> +<div class="accordion" id="events"> + <% for info_request_event in @info_request.info_request_events.find(:all, :order => "created_at, id") %> + <div class="accordion-group"> + <div class="accordion-heading"> + <span class="item-title"> + <a href="#event_<%=info_request_event.id%>" data-toggle="collapse" data-parent="#events"><%= chevron_right %></a> + <%= _("Event {{id}}", :id => info_request_event.id) %>: + <strong> + <%=h info_request_event.event_type.humanize %><% if !info_request_event.calculated_state.nil? %>; state: <%= info_request_event.calculated_state %><% end %> + </strong> + <em> + <%= info_request_event.created_at%> + </em> + </span> + </div> + <div id="event_<%=info_request_event.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <tr> + <td> + <% if info_request_event.described_state != 'waiting_clarification' and info_request_event.event_type == 'response' %> + <%= form_tag admin_request_clarification_path, :class => "form form-inline admin-table-form admin-inline-form" do %> + <%= hidden_field_tag 'info_request_event_id', info_request_event.id, :id => nil %> + <%= submit_tag "Was clarification request", :class => "btn btn-mini btn-primary" %> + <% end %> + <% end %> + </td> + <td></td> + </tr> + + <% info_request_event.for_admin_column do |name, value, type, column_name| %> + <tr> + <td> + <b><%=h name%></b> + </td> + <td> + <% if column_name == 'params_yaml' %> + <%= info_request_event.params_yaml_as_html.html_safe %> + <% elsif value.nil? %> + nil + <% elsif %w(text string).include?(type) %> + <%=h value.humanize %> + <% elsif type == 'datetime' %> + <%= admin_date value %> + <% else %> + <%=h value %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> +</div> +<hr> +<h2>Outgoing messages</h2> +<div class="accordion" id="outgoing_messages"> + <% for outgoing_message in @info_request.outgoing_messages.find(:all, :order => 'created_at') %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#outgoing_<%=outgoing_message.id%>" data-toggle="collapse" data-parent="#outgoing_messages"><%= chevron_right %></a> + <%= link_to admin_outgoing_edit_path(outgoing_message) do %> + #<%= outgoing_message.id %> -- <%= outgoing_message.status.humanize %> <%= outgoing_message.message_type.humanize %> + <% end %> + <blockquote> + <%= truncate(outgoing_message.body, :length => 400) %> + </blockquote> + </div> + <div id="outgoing_<%=outgoing_message.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <tr> + <td colspan="2"> + <%= form_tag admin_request_resend_path, :class => "admin-table-form" do %> + <%= hidden_field_tag 'outgoing_message_id', outgoing_message.id %> + <%= submit_tag "Resend", :class => "btn" %> + <% end %> + </td> + </tr> + <% outgoing_message.for_admin_column do |name, value, type, column_name| %> + <tr> + <td class="span3"> + <b><%=name%></b> + </td> + <td> + <% if column_name == 'body' %> + <%= simple_format(truncate(h(outgoing_message.body), :length => 400, :omission => link_to("...", "#", :class => "toggle-hidden" )).html_safe) %> + <div style="display:none;"><%= simple_format( outgoing_message.body ) %></div> + <% else %> + <%= admin_value(value) %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> +</div> +<hr> +<h2>Incoming messages</h2> +<div class="accordion" id="incoming_messages"> + <% for incoming_message in @info_request.incoming_messages %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#incoming_<%=incoming_message.id%>" data-toggle="collapse" data-parent="#incoming_messages"><%= chevron_right %></a> + <%= link_to admin_incoming_edit_path(incoming_message) do %> + <%=incoming_message.id%> + -- + <%= h(incoming_message.mail_from) %> + <%=_("at")%> <%=admin_value(incoming_message.sent_at)%> + <% end %> + <blockquote class="incoming-message"> + <% if !incoming_message.cached_main_body_text_folded.nil? %> + <%= truncate(incoming_message.cached_main_body_text_folded.gsub('FOLDED_QUOTED_SECTION', ''), :length => 400) %> + <% end %> + </blockquote> + </div> + <div id="incoming_<%=incoming_message.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <thead> + <tr> + <td colspan="2" class="well"> + <%= render :partial => 'incoming_message_actions', :locals => { :incoming_message => incoming_message } %> + </td> + </tr> + </thead> + <tbody> + <% incoming_message.for_admin_column do |name, value, type, column_name| %> + <tr> + <td> + <b><%=name%></b> + </td> + <td> + <% if column_name =~ /^cached_.*?$/ %> + <%= simple_format( truncate(h(value), :length => 400, :omission => link_to("...", "#", :class => "toggle-hidden")).html_safe) %> + <div style="display:none;"><%= simple_format(value) %></div> + <% else %> + <%= simple_format(value.to_s) %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> +</div> +<hr> +<h2>Annotations</h2> + +<%= render :partial => 'admin_request/some_annotations' , :locals => { :comments => @info_request.comments } %> + +<hr> +<h2>Mail server delivery logs</h2> + +<p><i>(Lines containing the request incoming email address, updated hourly.)</i></p> + +<% for mail_server_log_done_id, mail_server_logs in @info_request.mail_server_logs.group_by(&:mail_server_log_done_id) %> + <!-- <h3><%=h mail_server_logs[0].mail_server_log_done.filename %></h3> --> + <pre><% for mail_server_log in mail_server_logs %><%=h mail_server_log.line%><% end %></pre> +<% end %> +<% if @info_request.mail_server_logs.size == 0 %> + <p>None (perhaps this is an old or a very new request)</p> +<% end %> + +<hr> +<h2>Censor rules</h2> +<%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @info_request.censor_rules, :info_request => @info_request } %> + diff --git a/app/views/admin_request/show.rhtml b/app/views/admin_request/show.rhtml deleted file mode 100644 index 2541fd323..000000000 --- a/app/views/admin_request/show.rhtml +++ /dev/null @@ -1,230 +0,0 @@ -<% @title = "FOI request - " + h(@info_request.title) %> -<%= javascript_include_tag :defaults %> - -<h1><%=@title%></h1> - -<% form_tag '../move_request', { :class => "inline" } do %> -<p> -<%= hidden_field_tag 'info_request_id', @info_request.id %> -<% for column in InfoRequest.content_columns %> - <strong><%= column.human_name %>:</strong> <%=h @info_request.send(column.name) %> - <% if column.name == 'described_state' %> - <strong>Calculated status:</strong> <%= @info_request.calculate_status %> - <br/><strong>Initial request last sent at:</strong> <%=@info_request.date_initial_request_last_sent_at.to_date %> - <strong>Date response required by:</strong> <%= @info_request.date_response_required_by %> - <strong>Very overdue after:</strong> <%= @info_request.date_very_overdue_after %> - <% end %> - <% if ![ 'allow_new_responses_from' ].include?(column.name) %> - <br/> - <% end %> -<% end %> - <strong>Created by:</strong> - <% if @info_request.is_external? %> - <%= @info_request.public_body.name %> on behalf of <%= (@info_request.user_name || _('an anonymous user'))%> (using API) - <% else %> - <%= user_both_links(@info_request.user) %> - <% end %> - - <span> - <span> - (<%= link_to_function("move...", "$(this).up(1).childElements().invoke('toggle')") %>) - </span> - <span style="display:none;"> - <strong>url_name of new user:</strong> - <%= text_field_tag 'user_url_name', "", { :size => 20 } %> - <%= submit_tag "Move request to user" %> - </span> - </span> - <br> -<strong>Public authority:</strong> <%= public_body_both_links(@info_request.public_body) %> - <span> - <span> - (<%= link_to_function("move...", "$(this).up(1).childElements().invoke('toggle')") %>) - </span> - <span style="display:none;"> - <strong>url_name of new authority:</strong> - <%= text_field_tag 'public_body_url_name', "", { :size => 20 } %> - <%= submit_tag "Move request to authority" %> - </span> - </span> - <br> -<strong>Incoming email address:</strong> <%= link_to h(@info_request.incoming_email), "mailto:" + @info_request.incoming_email %> <br> -<b>Tags:</b> <%= render :partial => 'tags', :locals => { :info_request => @info_request} %> <br> -</p> -<% end %> - -<p> - <%= link_to 'Public page', main_url(request_url(@info_request)) %> - | <%= link_to 'Edit', '../edit/' + @info_request.id.to_s %> - | <%= link_to 'FOI officer upload URL', '../generate_upload_url/' + @info_request.id.to_s %> (see also links on incoming messages below) -</p> - -<h2>Events</h2> - -<table> - <tr> - <th>Id</th> - <% for column in InfoRequestEvent.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Actions</th> - </tr> - -<% for info_request_event in @info_request.info_request_events.find(:all, :order => "created_at, id") %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h info_request_event.id %></td> - <% for column in InfoRequestEvent.content_columns %> - <td> - <% if column.name == 'params_yaml' %> - <%= info_request_event.params_yaml_as_html %> - <% elsif column.text? %> - <%=h (info_request_event.send(column.name) || '').gsub(/_/,' ') %> - <% else %> - <%=h info_request_event.send(column.name) %> - <% end %> - </td> - <% end %> - <td> - <% if info_request_event.described_state != 'waiting_clarification' and info_request_event.event_type == 'response' %> - <% form_tag '../mark_event_as_clarification' do %> - <div> - <%= hidden_field_tag 'info_request_event_id', info_request_event.id %> - <%= submit_tag "Was clarification request" %> - </div> - <% end %> - <% end %> - </td> - </tr> -<% end %> -</table> - -<h2>Outgoing messages</h2> - -<table> - <tr> - <th>Id</th> - <% for column in OutgoingMessage.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Actions</th> - </tr> - -<% for outgoing_message in @info_request.outgoing_messages.find(:all, :order => 'created_at') %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h outgoing_message.id %></td> - <% for column in OutgoingMessage.content_columns.map { |c| c.name } %> - - <% if column == 'body' %> - <td> - <div><%= simple_format( truncate(outgoing_message.body, :length => 400, - :omission => link_to_function("...", "$(this).up('td').childElements().invoke('toggle')") - )) %></div> - <div style="display:none;"><%= simple_format( outgoing_message.body ) %></div> - </td> - <% else %> - <td><%= simple_format( outgoing_message.send(column) ) %></td> - <% end %> - - <% end %> - <td> - <% form_tag '../resend' do %> - <div> - <%= hidden_field_tag 'outgoing_message_id', outgoing_message.id %> - <%= submit_tag "Resend" %> - </div> - <% end %> - <%= link_to "Edit", '../edit_outgoing/' + outgoing_message.id.to_s %> - </td> - </tr> -<% end %> -</table> - -<h2>Incoming messages</h2> - -<table> - <tr> - <th>Id</th> - <% for column in IncomingMessage.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Actions</th> - </tr> - -<% for incoming_message in @info_request.incoming_messages.find(:all, :order => 'created_at') %> - <tr class="<%= cycle('odd', 'even') %>" id="incoming-<%=incoming_message.id.to_s%>"> - <td><%=h incoming_message.id %></td> - <% for column in IncomingMessage.content_columns.map { |c| c.name } %> - <% if column =~ /^cached_.*?$/ %> - <td> - <div><%= simple_format( truncate(incoming_message.send(column), :length => 400, - :omission => link_to_function("...", "$(this).up('td').childElements().invoke('toggle')") - )) %></div> - <div style="display:none;"><%= simple_format( incoming_message.send(column) ) %></div> - </td> - <% else %> - <td><%= simple_format( incoming_message.send(column) ) %></td> - <% end %> - <% end %> - <td> - <% if !incoming_message.raw_email_id.nil? %> - <p> - <%= link_to "View raw email", "../show_raw_email/" + incoming_message.raw_email_id.to_s %> - </p> - <% end %> - <%= render :partial => 'incoming_message_actions', :locals => { :incoming_message => incoming_message } %> - </td> - </tr> -<% end %> -</table> - -<h2>Annotations</h2> - -<% if @info_request.comments.size > 0 %> - <table> - <tr> - <th>Id</th> - <th>Posted by</th> - <% for column in Comment.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Actions</th> - </tr> - - <% for comment in @info_request.comments %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h comment.id %></td> - <td><%= user_both_links(comment.user) %></td> - <% for column in Comment.content_columns.map { |c| c.name } %> - <% if column == 'body' && !comment.visible %> - <td><s><%=h comment.send(column) %></s></td> - <% else %> - <td><%=h comment.send(column) %></td> - <% end %> - <% end %> - <td> - <%= link_to "Edit", '../edit_comment/' + comment.id.to_s %> - </td> - </tr> - <% end %> - </table> -<% else %> - <p>None yet.</p> -<% end %> - - - -<h2>Mail server delivery logs</h2> - -<p><i>(Lines containing the request incoming email address, updated hourly.)</i></p> - -<% for mail_server_log_done_id, mail_server_logs in @info_request.mail_server_logs.group_by(&:mail_server_log_done_id) %> - <!-- <h3><%=h mail_server_logs[0].mail_server_log_done.filename %></h3> --> - <pre><% for mail_server_log in mail_server_logs %><%=h mail_server_log.line%><% end %></pre> -<% end %> -<% if @info_request.mail_server_logs.size == 0 %> - <p>None (perhaps this is an old or a very new request)</p> -<% end %> - -<h2>Censor rules</h2> -<%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @info_request.censor_rules, :info_request => @info_request } %> - diff --git a/app/views/admin_request/show_raw_email.html.erb b/app/views/admin_request/show_raw_email.html.erb new file mode 100644 index 000000000..da22b6069 --- /dev/null +++ b/app/views/admin_request/show_raw_email.html.erb @@ -0,0 +1,63 @@ +<%= render :partial => 'admin_incoming_message/intro', :locals => { :incoming_message => @raw_email.incoming_message } %> + + <% if @holding_pen %> + <br>This is in the holding pen because: <strong><%= @rejected_reason %></strong> + <% if @public_bodies.size > 0 %> + <br>Guessed authority: + <% @public_bodies.each do |public_body| %> + <%=public_body_both_links(public_body)%> + <% end %> + (based on From: email domain) + <% end %> + <% if @info_requests.size > 0 %> + <div class="accordion" id="guessed-requests"> + Guessed request: + <% @info_requests.each do |info_request| %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#info_request_<%= info_request.id %>" data-toggle="collapse"><i class="icon-chevron-right"></i></a> + <%=request_both_links(info_request)%> + </div> + <div class="accordion-body collapse" id="info_request_<%= info_request.id %>"> + <table class="table table-striped table-condensed"> + <tr> + <td><strong>Last outgoing message:</strong></td> + <td><%= info_request.outgoing_messages.last.body %></td> + </tr> + <tr> + <td><strong>Created by:</strong></td> + <td><%= user_admin_link_for_request(info_request) %></td> + </tr> + <tr> + <td><strong>Authority:</strong></td> + <td> + <%= link_to(info_request.public_body.name, admin_body_show_path(info_request.public_body)) %> + </td> + </tr> + <tr> + <td><strong>url_title:</strong></td> + <td><%= info_request.url_title %></td> + </tr> + </table> + <p> + This request was guessed because it has an incoming email address of <strong><%= info_request.incoming_email %></strong> and this incoming message was sent to <strong><%= @raw_email.incoming_message.mail.to %></strong>. + </p> + </div> + </div> + <% end %> + (based on id, not hash, in To/Cc email) + </div> + <% end %> + <% end %> +</p> + +<div> + <%= render :partial => 'incoming_message_actions', :locals => { :incoming_message => @raw_email.incoming_message } %> +</div> + +<h2>Raw email</h2> + +<p><%= link_to "Download", admin_request_download_raw_email_path(@raw_email) %></p> + +<pre><%=h(@raw_email.data).gsub(/\n/, '<br>').html_safe %></pre> + diff --git a/app/views/admin_request/show_raw_email.rhtml b/app/views/admin_request/show_raw_email.rhtml deleted file mode 100644 index ea0d6017e..000000000 --- a/app/views/admin_request/show_raw_email.rhtml +++ /dev/null @@ -1,37 +0,0 @@ -<% @title = 'Incoming message ' + @raw_email.incoming_message.id.to_s + ' of FOI request \'' + h(@raw_email.incoming_message.info_request.title) + "'" %> - -<h1>Incoming message <%=@raw_email.incoming_message.id.to_s %></h1> - -<p> - FOI request: <%= request_both_links(@raw_email.incoming_message.info_request) %> - <% if @holding_pen %> - <br>This is in the holding pen because: <strong><%= @rejected_reason %></strong> - <% if @public_bodies.size > 0 %> - <br>Guessed authority: - <% for public_body in @public_bodies %> - <%=public_body_both_links(public_body)%> - <% end %> - (based on From: email domain) - <% end %> - <% if @info_requests.size > 0 %> - <br>Guessed request: - <% for info_request in @info_requests %> - <%=request_both_links(info_request)%> - <% end %> - (based on id, not hash, in To/Cc email) - <% end %> - <% end %> -</p> - -<h2>Actions</h2> - -<div> - <%= render :partial => 'incoming_message_actions', :locals => { :incoming_message => @raw_email.incoming_message } %> -</div> - -<h2>Raw email</h2> - -<p><%= link_to "Download", "../download_raw_email/" + @raw_email.id.to_s %></p> - -<pre><%= h(@raw_email.data).gsub(/\n/, '<br>').html_safe %></pre> - diff --git a/app/views/admin_track/_some_tracks.html.erb b/app/views/admin_track/_some_tracks.html.erb new file mode 100644 index 000000000..e9facfb5d --- /dev/null +++ b/app/views/admin_track/_some_tracks.html.erb @@ -0,0 +1,74 @@ +<% include_destroy = include_destroy || false %> +<% if track_things.empty? %> + <div class="row"> + <div class="span12"> + <%=_("No tracked things found.")%> + </div> + </div> +<% else %> + <div class="accordion" id="tracks"> + <% for track_thing in track_things %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#track_<%=track_thing.id%>" data-toggle="collapse" data-parent="requests"><%= chevron_right %></a> + <%=track_thing.id%>: + <% if track_thing.public_body_id %> + <%= link_to "<code>#{h track_thing.track_query}</code>".html_safe, public_body_path(track_thing.public_body) %> + <% elsif track_thing.info_request_id %> + <%= link_to "<code>#{h track_thing.track_query}</code>".html_safe, request_path(track_thing.info_request) %> + <% elsif track_thing.tracked_user_id %> + <%= link_to "<code>#{h track_thing.track_query}</code>".html_safe, user_path(track_thing.tracked_user) %> + <% else %> + <code><%=h track_thing.track_query%></code> + <% end %> + <% if @admin_user.nil? %> + <%# Do not show this on the list of tracks on the user page, because it’s rather repetitive there %> + tracked by <%= user_both_links track_thing.tracking_user %> + <% end %> + </div> + <div id="track_<%=track_thing.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% if include_destroy %> + <tr> + <td colspan="2"> + <%= form_tag admin_user_destroy_track_path, :class => "form form-inline admin-table-form" do %> + <div> + <%= hidden_field_tag 'track_id', track_thing.id %> + <%= submit_tag "Destroy track", :class => "btn btn-warning" %> + </div> + <% end %> + </td> + </tr> + + <% end %> + <% TrackThing.content_columns.each do |column| %> + <tr> + <td> + <b><%=column.human_name%></b> + </td> + <td> + <% if column.type.to_s == 'datetime' %> + <%= I18n.l(track_thing.send(column.name), :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(track_thing.send(column.name))) %>) + <% elsif column.name == 'track_medium' and track_thing.track_medium == 'feed' %> + <%= link_to track_thing.track_medium, atom_feed_path(:track_id => track_thing.id) %> + <% else %> + <%= h track_thing.send(column.name)%> + <% end %> + </td> + </tr> + <% end %> + <tr> + <td><b><%=_("Items sent in last month")%></b></td> + <td><%= track_thing.track_things_sent_emails.size %></td> + </tr> + </tbody> + </table> + </div> + </div> + <% end %> + </div> +<% end %> +</table> + diff --git a/app/views/admin_track/_some_tracks.rhtml b/app/views/admin_track/_some_tracks.rhtml deleted file mode 100644 index 72ee5fd95..000000000 --- a/app/views/admin_track/_some_tracks.rhtml +++ /dev/null @@ -1,31 +0,0 @@ -<table> - <tr> - <th>Id</th> - <th>User</th> - <% for column in TrackThing.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Items sent by email (in last month)</th> - <th>Actions</th> - </tr> - -<% for track_thing in track_things %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h track_thing.id %></td> - <td><%=user_both_links(track_thing.tracking_user) %></td> - <% for column in TrackThing.content_columns.map { |c| c.name } %> - <td><%=h track_thing.send(column) %></td> - <% end %> - <td><%= track_thing.track_things_sent_emails.size %></td> - <td> - <% form_tag '../../user/destroy_track' do %> - <div> - <%= hidden_field_tag 'track_id', track_thing.id %> - <%= submit_tag "Destroy track" %> - </div> - <% end %> - </td> - </tr> -<% end %> -</table> - diff --git a/app/views/admin_track/list.html.erb b/app/views/admin_track/list.html.erb new file mode 100644 index 000000000..5e967a926 --- /dev/null +++ b/app/views/admin_track/list.html.erb @@ -0,0 +1,20 @@ +<% @title = _('Listing tracks') %> + +<h1><%=@title%></h1> + +<%= form_tag({}, :method => "get", :class => "form form-search") do %> + <%= text_field_tag 'query', params[:query], { :size => 30, :class => "input-large search-query" } %> + <%= submit_tag "Search", :class => "btn" %> (substring search the query – so use url_names_for_a_particular_request_or_authority_or_person) +<% end %> + +<%= render :partial => 'some_tracks', :locals => { :track_things => @admin_tracks } %> + +<%= will_paginate(@admin_tracks, :class => "paginator" ) %> + +<h2>Current top tracks:</h2> +<ol> +<% for row in @popular %> + <li><%= link_to row['title'], admin_request_show_path(row['info_request_id']) %> (<%= row['count'] %> people following)</li> +<% end %> +</ol> + diff --git a/app/views/admin_track/list.rhtml b/app/views/admin_track/list.rhtml deleted file mode 100644 index 58c87ddba..000000000 --- a/app/views/admin_track/list.rhtml +++ /dev/null @@ -1,39 +0,0 @@ -<% @title = 'Listing tracks' %> - -<h1><%=@title%></h1> - -<% form_tag("", :method => "get") do %> - <p> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> - <%= submit_tag "Search" %> (substring search the query - so use url_names_for_a_particular_request_or_authority_or_person) - </p> -<% end %> - -<table> - <tr> - <th>Id</th> - <th>Tracked by</th> - <% for column in TrackThing.content_columns %> - <th><%= column.human_name %></th> - <% end %> - <th>Items sent by email (in last month)</th> - </tr> - -<% for track_thing in @admin_tracks %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h track_thing.id %></td> - <td><%= user_both_links(track_thing.tracking_user) %></td> - <% for column in TrackThing.content_columns.map { |c| c.name } %> - <% if column == 'track_medium' and track_thing.track_medium == 'feed'%> - <td><%= link_to track_thing.track_medium, atom_feed_url(:track_id => track_thing.id) %></td> - <% else %> - <td><%=h track_thing.send(column) %></td> - <% end %> - <% end %> - <td><%= track_thing.track_things_sent_emails.size %></td> - </tr> -<% end %> -</table> - -<%= will_paginate(@admin_tracks) %> - diff --git a/app/views/admin_user/_form.html.erb b/app/views/admin_user/_form.html.erb new file mode 100644 index 000000000..f1edc0927 --- /dev/null +++ b/app/views/admin_user/_form.html.erb @@ -0,0 +1,75 @@ +<%= error_messages_for 'admin_user' %> + +<!--[form:admin_user]--> + +<div class="control-group"> + <label for="admin_user_name" class="control-label">Name</label> + <div class="controls"> + <%= text_field 'admin_user', 'name', :class => "span3" %> + <div class="help-block"> + will change URL name and break URLs; unlike authorities, there is no history; you will need to rebuild the search index afterwards + </div> + </div> +</div> + +<div class="control-group"> + <label for="admin_user_email" class="control-label">Email</label> + <div class="controls"> + <%= text_field 'admin_user', 'email', :class => "span3" %> + <div class="help-block"> + <strong>you must</strong> first validate this + </div> + </div> +</div> + +<div class="control-group"> + <label for="admin_level" class="control-label">Admin level</label> + <div class="controls"> + <%= text_field 'admin_user', 'admin_level', :class => "span3" %> + <div class="help-block"> + <strong>none</strong> or <strong>super</strong>; this is for admin features and links which are in the site proper + </div> + </div> +</div> + +<div class="control-group"> + <label for="ban_text" class="control-label">Ban text</label> + <div class="controls"> + <%= text_area 'admin_user', 'ban_text', :class => "span6", :rows => 3 %> + <div class="help-block"> + if not blank will stop the + user from filing new requests, making annotations or messaging other users; + the text is shown in public on the user's page and when they try to do a + forbidden action; write in the second person (you); see + <%= link_to 'banned users', admin_user_list_banned_path %> for examples</small> + </div> + </div> +</div> + +<div class="control-group"> + <label for="about_me" class="control-label">About me</label> + <div class="controls"> + <%= text_area 'admin_user', 'about_me', :class => "span6", :rows => 3 %> + <div class="help-block"> + user's own text on their profile, format like comments + </div> + </div> +</div> +<div class="control-group"> + <label for="admin_user_no_limit" class="control-label">No rate limit</label> + <div class="controls"> + <%= check_box 'admin_user', 'no_limit' %> + <div class="help-block"> + disable the limit on daily requests + </div> + </div> +</div> +<div class="control-group"> + <label for="admin_user_can_make_batch_requests" class="control-label">Can make batch requests</label> + <div class="controls"> + <%= check_box 'admin_user', 'can_make_batch_requests' %> + <div class="help-block"> + allows the user to send a request to multiple authorities at once + </div> + </div> +</div> diff --git a/app/views/admin_user/_form.rhtml b/app/views/admin_user/_form.rhtml deleted file mode 100644 index be69d9a80..000000000 --- a/app/views/admin_user/_form.rhtml +++ /dev/null @@ -1,27 +0,0 @@ -<%= error_messages_for 'admin_user' %> - -<!--[form:admin_user]--> - -<p><label for="admin_user_name">Name</label> (will change URL name and break URLs; unlike authorities, there is no history; you will need to rebuild the search index afterwards)<br/> -<%= text_field 'admin_user', 'name', :size => 60 %></p> - -<p><label for="admin_user_email">Email</label> (<strong>you must</strong> first validate this)<br/> -<%= text_field 'admin_user', 'email', :size => 60 %></p> - -<p><label for="admin_user_admin_level">Admin level</label> (<strong>none</strong> or <strong>super</strong>; this is for admin features and links which are in the site proper)<br/> -<%= text_field 'admin_user', 'admin_level', :size => 60 %></p> - -<p><label for="admin_user_ban_text">Ban text</label> <small>(if not blank will stop the - user from filing new requests, making annotations or messaging other users; - the text is shown in public on the user's page and when they try to do a - forbidden action; write in the second person (you); see - <%= link_to 'banned users', '../banned' %> for examples</small>)<br/> - -<%= text_area 'admin_user', 'ban_text', :cols => 60, :rows => 3 %></p> - -<p><label for="admin_user_about_me">About me</label> (user's own text on their profile, format like comments):<br/> -<%= text_area 'admin_user', 'about_me', :cols => 60, :rows => 3 %></p> - -<p><%= check_box 'admin_user', 'no_limit' %> -<label for="admin_user_no_limit">No rate limit</label> (disable the limit on daily requests)</p> - diff --git a/app/views/admin_user/_user_table.html.erb b/app/views/admin_user/_user_table.html.erb new file mode 100644 index 000000000..57066bf3f --- /dev/null +++ b/app/views/admin_user/_user_table.html.erb @@ -0,0 +1,47 @@ +<div class="accordion" id="users"> + <% for user in users %> + <div class="accordion-group"> + <div class="accordion-heading accordion-toggle"> + <span class="item-title"> + <a href="#user_<%=user.id%>" data-toggle="collapse" data-parent="requests"><%= chevron_right %></a> + <% if user.admin_level == "super" %> + <span class="label">superuser</span> + <% end %> + <%= link_to("#{h(user.name)}", admin_user_show_path(user))%> + <%= link_to("(#{h(user.email)})", "mailto:#{h(user.email)}")%> + </span> + <span class="item-metadata"> + updated <%=I18n.l(user.updated_at, :format => "%e %B %Y %H:%M:%S")%> + </span> + </div> + <div id="user_<%=user.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <% if banned_column %> + <tr> + <td><b>Ban text</b></td> + <td><%= h user.ban_text %></td> + </tr> + <% end %> + <% user.for_admin_column do |name, value, type|%> + <tr> + <td><b><%=h name%></b></td> + <td> + <% if type == 'datetime' %> + <%= I18n.l(value, :format => "%e %B %Y %H:%M:%S") %> + (<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(value)) %>) + <% else %> + <%= h value %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> +</div> + +<%= will_paginate(users, :class => "paginator") %> + diff --git a/app/views/admin_user/_user_table.rhtml b/app/views/admin_user/_user_table.rhtml deleted file mode 100644 index d35c78594..000000000 --- a/app/views/admin_user/_user_table.rhtml +++ /dev/null @@ -1,22 +0,0 @@ -<table> - <tr> - <th>Id</th> - <% for column in ['Name', 'Email', 'Created at', 'Updated at', 'Email confirmed', 'Admin'] + (banned_column ? ['Ban text'] : []) %> - <th><%= column %></th> - <% end %> - </tr> - -<% for user in users %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%= user.id.to_s %></td> - <td><%= user_both_links(user) %></td> - <td><a href="mailto:<%=h user.email %>"><%=h user.email%></a></td> - <% for column in ['created_at', 'updated_at', 'email_confirmed', 'admin_level'] + (banned_column ? ['ban_text'] : []) %> - <td><%=h user.send(column) %></td> - <% end %> - </tr> -<% end %> -</table> - -<%= will_paginate(users) %> - diff --git a/app/views/admin_user/edit.html.erb b/app/views/admin_user/edit.html.erb new file mode 100644 index 000000000..e641a13d6 --- /dev/null +++ b/app/views/admin_user/edit.html.erb @@ -0,0 +1,11 @@ +<h1><%=@title%></h1> + +<%= form_tag admin_user_update_path(@admin_user), :class => "form form-horizontal" do %> + <%= render :partial => 'form' %> + <div class="form-actions"> + <%= submit_tag 'Save', :accesskey => 's', :class => "btn btn-primary" %> + </div> +<% end %> + +<%= link_to 'Show', admin_user_show_path(@admin_user), :class => "btn" %> +<%= link_to 'List all', admin_user_list_path, :class => "btn" %> diff --git a/app/views/admin_user/edit.rhtml b/app/views/admin_user/edit.rhtml deleted file mode 100644 index 06ddc57d3..000000000 --- a/app/views/admin_user/edit.rhtml +++ /dev/null @@ -1,23 +0,0 @@ - - -<h1><%=@title%></h1> - -<% form_tag '../update/' + @admin_user.id.to_s do %> - <%= render :partial => 'form' %> - <p><%= submit_tag 'Save', :accesskey => 's' %></p> -<% end %> - -<p> -<%= link_to 'Show', '../show/' + @admin_user.id.to_s %> | -<%= link_to 'List all', '../list' %> -</p> - -<% if false #@admin_user.info_requests.size == 0 %> - <% form_tag('../destroy/' + @admin_user.id.to_s) do %> - <p> - <%= hidden_field_tag(:admin_user_id, { :value => @admin_user.id } ) %> - <%= submit_tag "Destroy " + @admin_user.name %> (this is permanent!) - </p> - <% end %> -<% end %> - diff --git a/app/views/admin_user/list.html.erb b/app/views/admin_user/list.html.erb new file mode 100644 index 000000000..b1238f87a --- /dev/null +++ b/app/views/admin_user/list.html.erb @@ -0,0 +1,13 @@ +<% @title = _('Listing users') %> + +<h1><%=@title%></h1> + +<%= form_tag({}, :method => "get", :class => "form form-search") do %> + <%= text_field_tag 'query', params[:query], { :size => 30, :class => "input-large search-query"} %> + <%= submit_tag "Search", :class => "btn" %> (substring search, names and emails) + <%= link_to 'Banned users', admin_user_list_banned_path, :class => "btn btn-info" %> +<% end %> + +<%= render :partial => 'user_table', :locals => { :users => @admin_users, :banned_column => false } %> + + diff --git a/app/views/admin_user/list.rhtml b/app/views/admin_user/list.rhtml deleted file mode 100644 index 9de381ecc..000000000 --- a/app/views/admin_user/list.rhtml +++ /dev/null @@ -1,15 +0,0 @@ -<% @title = 'Listing users' %> - -<h1><%=@title%></h1> - -<% form_tag("", :method => "get") do %> - <p> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> - <%= submit_tag "Search" %> (substring search, names and emails) - <%= link_to 'Banned users', 'banned' %> - </p> -<% end %> - -<%= render :partial => 'user_table', :locals => { :users => @admin_users, :banned_column => false } %> - - diff --git a/app/views/admin_user/list_banned.rhtml b/app/views/admin_user/list_banned.html.erb index be2d45399..e535415e6 100644 --- a/app/views/admin_user/list_banned.rhtml +++ b/app/views/admin_user/list_banned.html.erb @@ -2,7 +2,7 @@ <h1><%=@title%></h1> -<p><%= link_to 'List all', 'list' %></p> +<p><%= link_to 'List all', admin_user_list_path %></p> <%= render :partial => 'user_table', :locals => { :users => @banned_users, :banned_column => true } %> diff --git a/app/views/admin_user/show.html.erb b/app/views/admin_user/show.html.erb new file mode 100644 index 000000000..6d12aeff5 --- /dev/null +++ b/app/views/admin_user/show.html.erb @@ -0,0 +1,102 @@ +<% @title = _("User – {{name}}", :name => h(@admin_user.name)) %> + +<h1><%=@title%></h1> + +<% if @admin_user.profile_photo %> + <div class="user_photo_on_admin"> + <%= form_tag admin_clear_profile_photo_path(@admin_user), :multipart => true, :class => "form" do %> + <img src="<%= get_profile_photo_url(:url_name => @admin_user.url_name) %>"> + <br> + <%= submit_tag "Clear photo", :class => "btn btn-info" %> + <% end %> + </div> +<% end %> + +<table class="table table-striped table-condensed"> + <tbody> + <tr> + <td> + <b><%=_("Id")%></b> + </td> + <td> + <%=@admin_user.id%> + </td> + </tr> + <% @admin_user.for_admin_column(:complete => true) do |name, value, type, column_name| %> + <tr> + <td> + <b><%=_(name)%></b> + </td> + <td> + <% if column_name == 'email' %> + <%=link_to @admin_user.email, "mailto:#{h @admin_user.email}"%> + <% elsif column_name == 'email_bounce_message' %> + <% unless @admin_user.email_bounce_message.empty? %> + <%= link_to _("See bounce message"), admin_user_show_bounce_path(@admin_user.id) %> + <% end %> + <% else %> + <%=h admin_value(value)%> + <% end %> + <% if column_name == 'email_bounced_at' && !@admin_user.email_bounced_at.nil? %> + <%= form_tag admin_user_clear_bounce_path(@admin_user), :class => "form form-inline" do %> + <input type="submit" name="action" value="Clear bounce" class="btn btn-info"> + <% end %> + <% end %> + </td> + </tr> + <% end %> + </tbody> +</table> + + +<%= link_to 'Edit', admin_user_edit_path(@admin_user), :class => "btn btn-primary" %> +<%= link_to 'Public page', user_path(@admin_user), :class => "btn" %> +<%= link_to "Log in as #{@admin_user.name} (also confirms their email)", admin_user_login_as_path(@admin_user), :class => "btn btn-info" %> + +<hr> + +<h2>Track things</h2> +<%= render :partial => 'admin_track/some_tracks', :locals => { :track_things => @admin_user.track_things, :include_destroy => true } %> + +<hr> + +<h2>Post redirects</h2> + +<table class="table table-condensed table-striped"> + <tr> + <th>Id</th> + <% for column in PostRedirect.content_columns %> + <th><%= column.human_name %></th> + <% end %> + </tr> + +<% for post_redirect in @admin_user.post_redirects.find(:all, :order => 'created_at desc') %> + <tr class="<%= cycle('odd', 'even') %>"> + <td><%=h post_redirect.id %></td> + <% for column in PostRedirect.content_columns.map { |c| c.name } %> + <% if column == 'email_token' %> + <td><%=link_to post_redirect.send(column), confirm_path(:email_token => post_redirect.send(column)) %></td> + <% else %> + <td><%=h post_redirect.send(column) %></td> + <% end %> + <% end %> + </tr> +<% end %> +</table> + +<hr> + +<h2>Requests</h2> +<%= render :partial => 'admin_request/some_requests', :locals => { :info_requests => @admin_user.info_requests } %> + +<hr> + +<h2>Annotations</h2> + +<%= render :partial => 'admin_request/some_annotations' , :locals => { :comments => @admin_user.comments } %> + +<hr> + +<h2>Censor rules</h2> +<%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @admin_user.censor_rules, :user => @admin_user } %> + diff --git a/app/views/admin_user/show.rhtml b/app/views/admin_user/show.rhtml deleted file mode 100644 index 4af79e45a..000000000 --- a/app/views/admin_user/show.rhtml +++ /dev/null @@ -1,74 +0,0 @@ -<% @title = "User - " + h(@admin_user.name) %> - -<h1><%=@title%></h1> - -<% if @admin_user.profile_photo %> - <div class="user_photo_on_admin"> - <% form_tag '../clear_profile_photo/' + @admin_user.id.to_s, :multipart => true do %> - <img src="<%= main_url(get_profile_photo_url(:url_name => @admin_user.url_name, :only_path => true)) %>"> - <br> - <%= submit_tag "Clear photo" %> - <% end %> - </div> -<% end %> - - -<div> -<strong>Id:</strong> <%= @admin_user.id%> <br> -<% for column in User.content_columns %> - <strong><%= column.human_name %>:</strong> - <% if column.name == 'email' %> - <a href="mailto:<%=h @admin_user.email %>"><%=h @admin_user.email%></a> - <% elsif column.name == 'email_bounce_message' %> - <% if !@admin_user.email_bounce_message.empty? %> - <a href="../show_bounce_message/<%= @admin_user.id.to_s %>">See bounce message</a> - <% end %> - <% else %> - <%=h @admin_user.send(column.name) %> - <% end %> - <% if column.name == 'email_bounced_at' && !@admin_user.email_bounced_at.nil? %> - <form action="../clear_bounce/<%= @admin_user.id.to_s %>" style="display: inline;"><input type="submit" name="action" value="Clear bounce"></form> - <% end %> - <br> -<% end %> -</div> - -<p> - <%= link_to 'Public page', main_url(user_url(@admin_user)) %> - | <%= link_to 'Edit', '../edit/' + @admin_user.id.to_s %> - | <%= link_to 'Log in as this user', '../login_as/' + @admin_user.id.to_s %> (also confirms their email) -</p> - -<h2>Track things</h2> -<%= render :partial => 'admin_track/some_tracks', :locals => { :track_things => @admin_user.track_things } %> - -<h2>Post redirects</h2> - -<table> - <tr> - <th>Id</th> - <% for column in PostRedirect.content_columns %> - <th><%= column.human_name %></th> - <% end %> - </tr> - -<% for post_redirect in @admin_user.post_redirects.find(:all, :order => 'created_at desc') %> - <tr class="<%= cycle('odd', 'even') %>"> - <td><%=h post_redirect.id %></td> - <% for column in PostRedirect.content_columns.map { |c| c.name } %> - <% if column == 'email_token' %> - <td><%=link_to post_redirect.send(column), main_url(confirm_url(:email_token => post_redirect.send(column), :only_path => true)) %></td> - <% else %> - <td><%=h post_redirect.send(column) %></td> - <% end %> - <% end %> - </tr> -<% end %> -</table> - -<h2>Requests</h2> -<%= render :partial => 'admin_request/some_requests', :locals => { :info_requests => @admin_user.info_requests } %> - -<h2>Censor rules</h2> -<%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @admin_user.censor_rules, :user => @admin_user } %> - diff --git a/app/views/admin_user/show_bounce_message.rhtml b/app/views/admin_user/show_bounce_message.html.erb index ad643a13e..ad643a13e 100644 --- a/app/views/admin_user/show_bounce_message.rhtml +++ b/app/views/admin_user/show_bounce_message.html.erb diff --git a/app/views/api/request_events.atom.builder b/app/views/api/request_events.atom.builder index 44759ae7e..648a81e5c 100644 --- a/app/views/api/request_events.atom.builder +++ b/app/views/api/request_events.atom.builder @@ -8,7 +8,7 @@ atom_feed("xmlns:alaveteli" => "http://www.alaveteli.org/API/v2/RequestEvents/At entry.updated(event.created_at.utc.iso8601) entry.tag!("alaveteli:event_type", event.event_type) - entry.tag!("alaveteli:request_url", main_url(request_url(request))) + entry.tag!("alaveteli:request_url", request_url(request)) entry.title(request.title) entry.content(event.outgoing_message.body, :type => 'text') @@ -16,7 +16,7 @@ atom_feed("xmlns:alaveteli" => "http://www.alaveteli.org/API/v2/RequestEvents/At entry.author do |author| author.name(request.user_name) if !request.user.nil? - author.uri(main_url(user_url(request.user))) + author.uri(user_url(request.user)) end author.email(request.incoming_email) end diff --git a/app/views/comment/_comment_form.rhtml b/app/views/comment/_comment_form.html.erb index 120929643..6ca3f4c9f 100644 --- a/app/views/comment/_comment_form.rhtml +++ b/app/views/comment/_comment_form.html.erb @@ -1,9 +1,9 @@ -<% form_for(:comment, @comment, :url => { :controller => "comment", :action => "new", :type => "request" }, :html => { :id => 'comment_form' } ) do |f| %> +<%= form_for(@comment, :as => :comment, :url => { :controller => "comment", :action => "new", :type => "request" }, :html => { :id => 'comment_form' } ) do |f| %> <p> <%= f.text_area :body, :rows => 10, :cols => 55 %> </p> - <% if !TrackThing.find_by_existing_track(@user, track_thing) && (!@user || @info_request.user != @user) %> + <% if !TrackThing.find_existing(@user, track_thing) && (!@user || @info_request.user != @user) %> <p> <%= check_box_tag 'subscribe_to_request', "1", params[:subscribe_to_request] ? true : false %> <label for="subscribe_to_request"><%= _('Email me future updates to this request') %></label> </p> @@ -13,7 +13,7 @@ <%= hidden_field_tag 'submitted_comment', 1 %> <%= hidden_field_tag 'preview', 1 %> <%= submit_tag _('Preview your annotation') %> - <%= raw(_(' (<strong>no ranty</strong> politics, read our <a href="%s">moderation policy</a>)') % [help_requesting_path+'#moderation']) %> + <%= _(' (<strong>no ranty</strong> politics, read our <a href="{{url}}">moderation policy</a>)', :url => (help_requesting_path+'#moderation').html_safe) %> </p> <% end %> diff --git a/app/views/comment/_single_comment.rhtml b/app/views/comment/_single_comment.html.erb index b645721cf..a6d234b34 100644 --- a/app/views/comment/_single_comment.rhtml +++ b/app/views/comment/_single_comment.html.erb @@ -1,5 +1,5 @@ <div class="comment_in_request" id="comment-<%=comment.id.to_s%>"> - <% if comment.user && comment.user.profile_photo %> + <% if comment.user && comment.user.profile_photo && !@render_to_file %> <div class="user_photo_on_comment"> <img src="<%= get_profile_photo_url(:url_name => comment.user.url_name) %>" alt=""> </div> @@ -11,17 +11,17 @@ </h2> <div class="comment_in_request_text"> <p> - <img class="comment_quote" src="/images/quote-marks.png" alt=""> + <%= image_tag "quote-marks.png", :class => "comment_quote" %> <%= comment.get_body_for_html_display %> </p> </div> <p class="event_actions"> <% if !comment.id.nil? %> - <%= link_to "Link to this", comment_url(comment), :class => "link_to_this" %> <% if !@user.nil? && @user.admin_page_links? %> - | <%= link_to "Admin", admin_url("request/edit_comment/" + comment.id.to_s) %> + <%= link_to "Admin", admin_request_edit_comment_path(comment) %> | <% end %> - <!-- | <%= link_to _('Report abuse'), comment_url(comment) %> --> + <%= link_to "Link to this", comment_path(comment), :class => "link_to_this" %> + <!-- | <%= link_to _('Report abuse'), comment_path(comment) %> --> <% end %> </p> </div> diff --git a/app/views/comment/_single_comment.text.erb b/app/views/comment/_single_comment.text.erb new file mode 100644 index 000000000..925e8b688 --- /dev/null +++ b/app/views/comment/_single_comment.text.erb @@ -0,0 +1,2 @@ +<%= _("{{username}} left an annotation:", :username =>comment.user.name) %> (<%= simple_date(comment.created_at || Time.now) %>) +<%= comment.body.strip %> diff --git a/app/views/comment/new.rhtml b/app/views/comment/new.html.erb index aa5b6051c..578732cdb 100644 --- a/app/views/comment/new.rhtml +++ b/app/views/comment/new.html.erb @@ -67,10 +67,10 @@ </ul> <p> - <big><%= _('Annotations will be posted publicly here, and are - <strong>not</strong> sent to {{public_body_name}}.',:public_body_name=>h(@info_request.public_body.name)) %></big> + <span class="big"><%= _('Annotations will be posted publicly here, and are + <strong>not</strong> sent to {{public_body_name}}.',:public_body_name=>h(@info_request.public_body.name)) %></span> <% if @info_request.is_external? %> - <big><%= _('Note that the requester will not be notified about your annotation, because the request was published by {{public_body_name}} on their behalf.', :public_body_name => @info_request.public_body.name) %></big> + <span class="big"><%= _('Note that the requester will not be notified about your annotation, because the request was published by {{public_body_name}} on their behalf.', :public_body_name => @info_request.public_body.name) %></span> <% end %> </p> diff --git a/app/views/comment/preview.rhtml b/app/views/comment/preview.html.erb index 702bd9a9b..cd966e6a5 100644 --- a/app/views/comment/preview.rhtml +++ b/app/views/comment/preview.html.erb @@ -1,6 +1,6 @@ <% @title = _("Preview new annotation on '{{info_request_title}}'",:info_request_title=>h(@info_request.title)) %> -<% form_for(:comment, @comment, :html => { :id => 'preview_form' }, :url => { :controller => "comment", :action => "new", :type => "request" } ) do |f| %> +<%= form_for(@comment, :html => { :id => 'preview_form' }, :url => { :controller => "comment", :action => "new", :type => "request" } ) do |f| %> <h1><%= _('Now preview your annotation') %></h1> diff --git a/app/views/contact_mailer/add_public_body.text.erb b/app/views/contact_mailer/add_public_body.text.erb new file mode 100644 index 000000000..5baa1fa1a --- /dev/null +++ b/app/views/contact_mailer/add_public_body.text.erb @@ -0,0 +1,19 @@ +<%= _("{{user_name}} would like a new authority added to {{site_name}}", :user_name => @change_request.get_user_name, :site_name => site_name) %> + +<%= _("Authority:") %> +<%= @change_request.get_public_body_name %> + +<%= _("Email:") %> +<%= @change_request.public_body_email %> + +<%= _("Source:") %> +<%= @change_request.source_url %> + +<%= _("Notes:") %> +<%= @change_request.notes %> + +<%= _('Add the authority:') %> +<%= admin_body_new_url(:change_request_id => @change_request.id, :only_path => false ) %> + +<%= _('Close the request and respond:') %> +<%= admin_change_request_edit_url(:id => @change_request.id, :only_path => false ) %> diff --git a/app/views/contact_mailer/from_admin_message.rhtml b/app/views/contact_mailer/from_admin_message.rhtml deleted file mode 100644 index bdb48d580..000000000 --- a/app/views/contact_mailer/from_admin_message.rhtml +++ /dev/null @@ -1,2 +0,0 @@ -<%= @message.strip %> - diff --git a/app/views/contact_mailer/from_admin_message.text.erb b/app/views/contact_mailer/from_admin_message.text.erb new file mode 100644 index 000000000..3af759c5d --- /dev/null +++ b/app/views/contact_mailer/from_admin_message.text.erb @@ -0,0 +1 @@ +<%= raw @message %> diff --git a/app/views/contact_mailer/to_admin_message.rhtml b/app/views/contact_mailer/to_admin_message.rhtml deleted file mode 100644 index 9c0a74c02..000000000 --- a/app/views/contact_mailer/to_admin_message.rhtml +++ /dev/null @@ -1,11 +0,0 @@ -<%= @message.strip %> - ---------------------------------------------------------------------- -<%= _('Message sent using {{site_name}} contact form, ', :site_name=>site_name)%> -<%=(@logged_in_user ? ("logged in as user " + main_url(user_url(@logged_in_user))) : "not logged in")%><% if !@last_request.nil? %> - -<%= _('Last request viewed: ')%><%= main_url(request_url(@last_request)) %> -<% end %> <% if !@last_body.nil? %> - -<%= _('Last authority viewed: ')%><%= main_url(public_body_url(@last_body)) %> -<% end %>--------------------------------------------------------------------- diff --git a/app/views/contact_mailer/to_admin_message.text.erb b/app/views/contact_mailer/to_admin_message.text.erb new file mode 100644 index 000000000..dc9b1090b --- /dev/null +++ b/app/views/contact_mailer/to_admin_message.text.erb @@ -0,0 +1,11 @@ +<%= raw @message.strip %> + +--------------------------------------------------------------------- +<%= _('Message sent using {{site_name}} contact form, ', :site_name=>site_name)%> +<%=(@logged_in_user ? ("logged in as user " + user_url(@logged_in_user)) : "not logged in")%><% if !@last_request.nil? %> + +<%= _('Last request viewed: ')%><%= request_url(@last_request) %> +<% end %> <% if !@last_body.nil? %> + +<%= _('Last authority viewed: ')%><%= public_body_url(@last_body) %> +<% end %>--------------------------------------------------------------------- diff --git a/app/views/contact_mailer/update_public_body_email.text.erb b/app/views/contact_mailer/update_public_body_email.text.erb new file mode 100644 index 000000000..7d5a3dae0 --- /dev/null +++ b/app/views/contact_mailer/update_public_body_email.text.erb @@ -0,0 +1,16 @@ +<%= _("{{user_name}} would like the email address for {{public_body_name}} to be updated", :user_name => @change_request.get_user_name, :public_body_name => @change_request.get_public_body_name) %> + +<%= _("Email:") %> +<%= @change_request.public_body_email %> + +<%= _("Source:") %> +<%= @change_request.source_url %> + +<%= _("Notes:") %> +<%= @change_request.notes %> + +<%= _('Update the address:') %> +<%= admin_body_edit_path(@change_request.public_body, :change_request_id => @change_request.id, :only_path => false) %> + +<%= _('Close the request and respond:') %> +<%= admin_change_request_edit_url(:id => @change_request.id, :only_path => false ) %> diff --git a/app/views/contact_mailer/user_message.rhtml b/app/views/contact_mailer/user_message.text.erb index b1d6e81ae..afa1494db 100644 --- a/app/views/contact_mailer/user_message.rhtml +++ b/app/views/contact_mailer/user_message.text.erb @@ -5,7 +5,7 @@ learn your email address. Only reply if that is okay.', :user_name => @from_user.name) %> --------------------------------------------------------------------- -<%= @message.strip %> +<%= raw @message.strip %> --------------------------------------------------------------------- <%= _('View Freedom of Information requests made by {{user_name}}:', :user_name=>@from_user.name)%> diff --git a/app/views/general/_advanced_search_tips.rhtml b/app/views/general/_advanced_search_tips.html.erb index 914abc1af..5e19c41e0 100644 --- a/app/views/general/_advanced_search_tips.rhtml +++ b/app/views/general/_advanced_search_tips.html.erb @@ -23,27 +23,30 @@ <p><%= _("All the options below can use <strong>status</strong> or <strong>latest_status</strong> before the colon. For example, <strong>status:not_held</strong> will match requests which have <em>ever</em> been marked as not held; <strong>latest_status:not_held</strong> will match only requests that are <em>currently</em> marked as not held.") %></p> <table class="status_table"> - <tr><td><strong><%=search_link('status:waiting_response', nil, nil, true)%></strong></td><td><%= _('Waiting for the public authority to reply') %></td></tr> - <tr><td><strong><%=search_link('status:not_held', nil, nil, true)%></strong></td><td><%= _('The public authority does not have the information requested') %></td></tr> - <tr><td><strong><%=search_link('status:rejected', nil, nil, true)%></strong></td><td><%= _('The request was refused by the public authority') %></td></tr> - <tr><td><strong><%=search_link('status:partially_successful', nil, nil, true)%></strong></td><td><%= _('Some of the information requested has been received') %></td></tr> - <tr><td><strong><%=search_link('status:successful', nil, nil, true)%></strong></td><td><%= _('All of the information requested has been received') %></td></tr> - <tr><td><strong><%=search_link('status:waiting_clarification', nil, nil, true)%></strong></td><td><%= _('The public authority would like part of the request explained') %></td></tr> - <tr><td><strong><%=search_link('status:gone_postal', nil, nil, true)%></strong></td><td><%= _('The public authority would like to / has responded by post') %></td></tr> - <tr><td><strong><%=search_link('status:internal_review', nil, nil, true)%></strong></td><td><%= _('Waiting for the public authority to complete an internal review of their handling of the request') %></td></tr> - <tr><td><strong><%=search_link('status:error_message', nil, nil, true)%></strong></td><td><%= _('Received an error message, such as delivery failure.') %></td></tr> - <tr><td><strong><%=search_link('status:requires_admin', nil, nil, true)%></strong></td><td><%= _('A strange reponse, required attention by the {{site_name}} team', :site_name=>site_name) %></td></tr> - <tr><td><strong><%=search_link('status:user_withdrawn', nil, nil, true)%></strong></td><td><%= _('The requester has abandoned this request for some reason') %></td></tr> + <tr><td><strong><%=search_link('status:waiting_response')%></strong></td><td><%= _('Waiting for the public authority to reply') %></td></tr> + <tr><td><strong><%=search_link('status:not_held')%></strong></td><td><%= _('The public authority does not have the information requested') %></td></tr> + <tr><td><strong><%=search_link('status:rejected')%></strong></td><td><%= _('The request was refused by the public authority') %></td></tr> + <tr><td><strong><%=search_link('status:partially_successful')%></strong></td><td><%= _('Some of the information requested has been received') %></td></tr> + <tr><td><strong><%=search_link('status:successful')%></strong></td><td><%= _('All of the information requested has been received') %></td></tr> + <tr><td><strong><%=search_link('status:waiting_clarification')%></strong></td><td><%= _('The public authority would like part of the request explained') %></td></tr> + <tr><td><strong><%=search_link('status:gone_postal')%></strong></td><td><%= _('The public authority would like to / has responded by post') %></td></tr> + <tr><td><strong><%=search_link('status:internal_review')%></strong></td><td><%= _('Waiting for the public authority to complete an internal review of their handling of the request') %></td></tr> + <tr><td><strong><%=search_link('status:error_message')%></strong></td><td><%= _('Received an error message, such as delivery failure.') %></td></tr> + <tr><td><strong><%=search_link('status:requires_admin')%></strong></td><td><%= _('A strange reponse, required attention by the {{site_name}} team', :site_name=>site_name) %></td></tr> + <tr><td><strong><%=search_link('status:user_withdrawn')%></strong></td><td><%= _('The requester has abandoned this request for some reason') %></td></tr> </table> <h2 id="varieties"><%= _('Table of varieties') %></h2> <p><%= _("All the options below can use <strong>variety</strong> or <strong>latest_variety</strong> before the colon. For example, <strong>variety:sent</strong> will match requests which have <em>ever</em> been sent; <strong>latest_variety:sent</strong> will match only requests that are <em>currently</em> marked as sent.") %></p> <table class="status_table"> - <tr><td><strong><%=search_link('variety:sent', nil, nil, true)%></strong></td><td><%= _('Original request sent') %></td></tr> - <tr><td><strong><%=search_link('variety:followup_sent', nil, nil, true)%></strong></td><td><%= _('Follow up message sent by requester') %></td></tr> - <tr><td><strong><%=search_link('variety:response', nil, nil, true)%></strong></td><td><%= _('Response from a public authority') %></td></tr> - <tr><td><strong><%=search_link('variety:comment', nil, nil, true)%></strong></td><td><%= _('Annotation added to request') %></td></tr> - <tr><td><strong><%=search_link('variety:authority', nil, nil, true)%></strong></td><td><%= _('A public authority') %></td></tr> - <tr><td><strong><%=search_link('variety:user', nil, nil, true)%></strong></td><td><%= _('A {{site_name}} user', :site_name=>site_name) %></td></tr> + <tr><td><strong><%=search_link('variety:sent')%></strong></td><td><%= _('Original request sent') %></td></tr> + <tr><td><strong><%=search_link('variety:followup_sent')%></strong></td><td><%= # TRANSLATORS: "Follow up message" in this context means a + # further message sent by the requester to the authority after + # the initial request + _('Follow up message sent by requester') %></td></tr> + <tr><td><strong><%=search_link('variety:response')%></strong></td><td><%= _('Response from a public authority') %></td></tr> + <tr><td><strong><%=search_link('variety:comment')%></strong></td><td><%= _('Annotation added to request') %></td></tr> + <tr><td><strong><%=search_link('variety:authority')%></strong></td><td><%= _('A public authority') %></td></tr> + <tr><td><strong><%=search_link('variety:user')%></strong></td><td><%= _('A {{site_name}} user', :site_name=>site_name) %></td></tr> </table> </div> diff --git a/app/views/general/_before_body_end.rhtml b/app/views/general/_before_body_end.html.erb index 25fdf4684..25fdf4684 100644 --- a/app/views/general/_before_body_end.rhtml +++ b/app/views/general/_before_body_end.html.erb diff --git a/app/views/general/_before_head_end.rhtml b/app/views/general/_before_head_end.html.erb index 8886745ab..8886745ab 100644 --- a/app/views/general/_before_head_end.rhtml +++ b/app/views/general/_before_head_end.html.erb diff --git a/app/views/general/_credits.rhtml b/app/views/general/_credits.html.erb index b1a9ce05e..b1a9ce05e 100644 --- a/app/views/general/_credits.rhtml +++ b/app/views/general/_credits.html.erb diff --git a/app/views/general/_custom_state_descriptions.rhtml b/app/views/general/_custom_state_descriptions.html.erb index 913a6d50a..913a6d50a 100644 --- a/app/views/general/_custom_state_descriptions.rhtml +++ b/app/views/general/_custom_state_descriptions.html.erb diff --git a/app/views/general/_custom_state_transitions_complete.rhtml b/app/views/general/_custom_state_transitions_complete.html.erb index e69de29bb..e69de29bb 100644 --- a/app/views/general/_custom_state_transitions_complete.rhtml +++ b/app/views/general/_custom_state_transitions_complete.html.erb diff --git a/app/views/general/_custom_state_transitions_pending.rhtml b/app/views/general/_custom_state_transitions_pending.html.erb index e69de29bb..e69de29bb 100644 --- a/app/views/general/_custom_state_transitions_pending.rhtml +++ b/app/views/general/_custom_state_transitions_pending.html.erb diff --git a/app/views/general/_footer.html.erb b/app/views/general/_footer.html.erb new file mode 100644 index 000000000..2e10a0f94 --- /dev/null +++ b/app/views/general/_footer.html.erb @@ -0,0 +1,8 @@ +<div id="footer"> + <%= link_to _("Contact {{site_name}}", :site_name => site_name), help_contact_path %> +<% unless AlaveteliConfiguration::twitter_username.blank? %> +| <%= image_tag "twitter-16.png", :alt => "twitter icon", :class => "twitter-icon" %> <a href="https://twitter.com/<%= AlaveteliConfiguration::twitter_username %>"><%= _("Follow us on twitter") %></a> +<% end %> +<%= render :partial => 'general/credits' %> +</div> +<div class="after-footer"> </div> diff --git a/app/views/general/_footer.rhtml b/app/views/general/_footer.rhtml deleted file mode 100644 index ab5ab2c47..000000000 --- a/app/views/general/_footer.rhtml +++ /dev/null @@ -1,6 +0,0 @@ -<div id="footer"> - <%= link_to _("Contact {{site_name}}", :site_name => site_name), help_contact_url %> -| <img src="/images/twitter-16.png" alt="twitter icon" class="twitter-icon"> <a href="https://twitter.com/<%= Configuration::twitter_username %>"><%= _("Follow us on twitter") %></a> -<%= render :partial => 'general/credits' %> -</div> -<div class="after-footer"> </div> diff --git a/app/views/general/_frontpage_bodies_list.rhtml b/app/views/general/_frontpage_bodies_list.html.erb index 503b38953..44321f14a 100644 --- a/app/views/general/_frontpage_bodies_list.rhtml +++ b/app/views/general/_frontpage_bodies_list.html.erb @@ -1,17 +1,18 @@ -<% if @popular_bodies.size > 0 %> +<%- popular_bodies = PublicBody.popular_bodies(@locale) %> +<% if popular_bodies.size > 0 %> <div id="examples_0"> <h3><%= _("Who can I request information from?") %></h3> <%= _("{{site_name}} covers requests to {{number_of_authorities}} authorities, including:", :site_name => site_name, :number_of_authorities => PublicBody.visible.count) %> <ul> - <% for popular_body in @popular_bodies %> + <% for popular_body in popular_bodies %> <li><%=public_body_link(popular_body)%> - <%= n_('%d request', '%d requests', popular_body.info_requests_count) % popular_body.info_requests_count %> + <%= n_('{{count}} request', '{{count}} requests', popular_body.info_requests_count, :count => popular_body.info_requests_count) %> </li> <% end%> </ul> <p><strong> - <%= link_to _('Browse all authorities...'), list_public_bodies_default %> + <%= link_to _('Browse all authorities...'), list_public_bodies_default_path %> </strong></p> </div> <% end %> diff --git a/app/views/general/_frontpage_intro_sentence.rhtml b/app/views/general/_frontpage_intro_sentence.html.erb index 70b47ad06..74b849fc9 100644 --- a/app/views/general/_frontpage_intro_sentence.rhtml +++ b/app/views/general/_frontpage_intro_sentence.html.erb @@ -1,4 +1,4 @@ <h2> Your <strong>Right to Know</strong> </h2> -<p>Every citizen has the right to access information held by public authorities. <strong>By law, they have to respond</strong>. <a href="<%= help_about_url %>">Find out more about freedom of information.</a></p> +<p>Every citizen has the right to access information held by public authorities. <strong>By law, they have to respond</strong>. <a href="<%= help_about_path %>">Find out more about freedom of information.</a></p> diff --git a/app/views/general/_frontpage_new_request.rhtml b/app/views/general/_frontpage_new_request.html.erb index 499b60eb5..499b60eb5 100644 --- a/app/views/general/_frontpage_new_request.rhtml +++ b/app/views/general/_frontpage_new_request.html.erb diff --git a/app/views/general/_frontpage_requests_list.rhtml b/app/views/general/_frontpage_requests_list.html.erb index 3b0efb65e..d7d9184c4 100644 --- a/app/views/general/_frontpage_requests_list.rhtml +++ b/app/views/general/_frontpage_requests_list.html.erb @@ -1,3 +1,4 @@ +<%- @request_events, @request_events_all_successful = InfoRequest.recent_requests %> <div id="examples_1"> <h3> <% if @request_events_all_successful %> @@ -19,17 +20,17 @@ :public_body_link => public_body_link(event.info_request.public_body)) %> <% end %> - <%=link_to h(event.info_request.title), request_url(event.info_request)%> + <%=link_to h(event.info_request.title), request_path(event.info_request)%> <%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(event.described_at)) %> - <p class="excerpt" onclick="document.location.href='<%=request_url(event.info_request)%>'"><%= excerpt(event.search_text_main(true), "", 200) %></p> + <p class="excerpt" onclick="document.location.href='<%=request_path(event.info_request)%>'"><%= excerpt(event.search_text_main(true), "", :radius => 200) %></p> </li> <% end %> </ul> <p><strong> <% if @request_events_all_successful %> - <%=link_to _('More successful requests...'), request_list_successful_url %> + <%=link_to _('More successful requests...'), request_list_successful_path %> <% else %> - <%=link_to _('More requests...'), request_list_all_url %> + <%=link_to _('More requests...'), request_list_all_path %> <% end %> </strong></p> </div> diff --git a/app/views/general/_frontpage_search_box.rhtml b/app/views/general/_frontpage_search_box.html.erb index d2718b3a3..890602416 100644 --- a/app/views/general/_frontpage_search_box.rhtml +++ b/app/views/general/_frontpage_search_box.html.erb @@ -6,7 +6,7 @@ </h2> <form id="search_form" method="post" action="<%= search_redirect_path %>"> <div> - <input id="query" type="text" size="30" name="query"> + <input id="query" type="text" size="30" name="query" title="type your search term here" > <input type="submit" value="<%= _('Search') %>"> </div> </form> diff --git a/app/views/general/_header.html.erb b/app/views/general/_header.html.erb new file mode 100644 index 000000000..55bf719e2 --- /dev/null +++ b/app/views/general/_header.html.erb @@ -0,0 +1,37 @@ +<div id="banner"> + <div id="banner_inner"> + <div class="lang"><%= render :partial => 'general/locale_switcher' %></div> + + <% if not (controller.action_name == 'signin' or controller.action_name == 'signup') %> + <div id="logged_in_bar"> + <% if @user %> + <%= _('Hello, {{username}}!', :username => h(@user.name))%> + + <% if @user %> + <%=link_to _("My requests"), show_user_requests_path(:url_name => @user.url_name) %> + <%=link_to _("My profile"), show_user_profile_path(:url_name => @user.url_name) %> + <%=link_to _("My wall"), show_user_wall_path(:url_name => @user.url_name) %> + <% end %> + + + <%= link_to _("Sign out"), signout_path(:r => request.fullpath) %> + <% else %> + <%= link_to _("Sign in or sign up"), signin_path(:r => request.fullpath) %> + <% end %> + </div> + <% end %> + + <div id="navigation_search"> + <form id="navigation_search_form" method="post" action="<%= search_redirect_path %>"> + <p> + <%= text_field_tag 'query', params[:query], { :size => 40, :id => "navigation_search_query", :title => "type your search term here" } %> + <input id="navigation_search_button" type="submit" value="search"> + </p> + </form> + </div> + + <%= render :partial => 'general/orglink' %> + + <%= render :partial => 'general/topnav' %> + </div> +</div> diff --git a/app/views/general/_locale_switcher.rhtml b/app/views/general/_locale_switcher.html.erb index 2521b5eb5..a318f61f3 100644 --- a/app/views/general/_locale_switcher.rhtml +++ b/app/views/general/_locale_switcher.html.erb @@ -2,10 +2,10 @@ <div id="user_locale_switcher"> <div class="btn-group"> <% for possible_locale in FastGettext.default_available_locales %> - <% if possible_locale == I18n.locale.to_s %> + <% if possible_locale == FastGettext.locale %> <a href="#" class="btn disabled"><%= locale_name(possible_locale) %></a> <% else %> - <a href="<%= locale_switcher(possible_locale, params) %>" class="btn"><%= locale_name(possible_locale) %></a> + <a href="<%= url_for params.merge(:locale => possible_locale) %>" class="btn"><%= locale_name(possible_locale) %></a> <% end %> <% end %> </div> diff --git a/app/views/general/_localised_datepicker.rhtml b/app/views/general/_localised_datepicker.html.erb index ec6593ea0..ee3206957 100644 --- a/app/views/general/_localised_datepicker.rhtml +++ b/app/views/general/_localised_datepicker.html.erb @@ -1,16 +1,16 @@ <script type="text/javascript"> $(function() { $(".use-datepicker").datepicker( - {closeText: '<%= _("Done") %>', - prevText: '<%= _("Prev") %>', - nextText: '<%= _("Next") %>', - currentText: '<%= _("Today") %>', + {closeText: '<%= j _("Done") %>', + prevText: '<%= j _("Prev") %>', + nextText: '<%= j _("Next") %>', + currentText: '<%= j _("Today") %>', monthNames: <%= raw I18n.translate('date.month_names')[1..-1].to_json %>, monthNamesShort: <%= raw I18n.translate('date.abbr_month_names')[1..-1].to_json %>, dayNames: <%= raw I18n.translate('date.day_names').to_json %>, dayNamesShort: <%= raw I18n.translate('date.abbr_day_names').to_json %>, dayNamesMin: <%= raw I18n.translate('date.abbr_day_names').collect{|x| x[0..0]}.to_json %>, - weekHeader: '<%= _("Wk") %>', + weekHeader: '<%= j _("Wk") %>', dateFormat: '<%= I18n.translate('date.formats.default').sub("%Y", "yy").sub("%m", "mm").sub("%d", "dd").gsub("-", "/") %>'} ); }); diff --git a/app/views/general/_orglink.html.erb b/app/views/general/_orglink.html.erb new file mode 100644 index 000000000..66002c021 --- /dev/null +++ b/app/views/general/_orglink.html.erb @@ -0,0 +1,2 @@ +<%# Put the link to your organisation here, or leave blank %> +<%= link_to image_tag('logo.png'), frontpage_path, :id=>'logo' %> diff --git a/app/views/general/_orglink.rhtml b/app/views/general/_orglink.rhtml deleted file mode 100644 index fbe688d85..000000000 --- a/app/views/general/_orglink.rhtml +++ /dev/null @@ -1,2 +0,0 @@ -<%# Put the link to your organisation here, or leave blank %> -<%= link_to image_tag('logo.png'), frontpage_url, :id=>'logo' %> diff --git a/app/views/admin_general/_admin_navbar.rhtml b/app/views/general/_popup_banner.html.erb index 8b1378917..8b1378917 100644 --- a/app/views/admin_general/_admin_navbar.rhtml +++ b/app/views/general/_popup_banner.html.erb diff --git a/app/views/general/_popup_banner.rhtml b/app/views/general/_popup_banner.rhtml deleted file mode 100644 index 8b1378917..000000000 --- a/app/views/general/_popup_banner.rhtml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/views/general/_stylesheet_includes.html.erb b/app/views/general/_stylesheet_includes.html.erb new file mode 100644 index 000000000..7a1648efd --- /dev/null +++ b/app/views/general/_stylesheet_includes.html.erb @@ -0,0 +1,22 @@ +<%- if @render_to_file %> + <style> + <%= Rails.application.assets["main.css"].to_s %> + <%= Rails.application.assets["print.css"].to_s %> + </style> +<%- else %> + <%= stylesheet_link_tag 'application', :title => "Main", :rel => "stylesheet", :media => "all" %> + <%= stylesheet_link_tag 'fonts', :rel => "stylesheet", :media => "all" %> + <%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "print" %> + <% if !params[:print_stylesheet].nil? %> + <%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "all" %> + <% end %> + <!--[if LT IE 7]> + <%= stylesheet_link_tag 'ie6.css' %> + <![endif]--> + <!--[if LT IE 8]> + <%= stylesheet_link_tag 'ie7.css' %> + <![endif]--> + <% if AlaveteliConfiguration::force_registration_on_new_request %> + <%= stylesheet_link_tag 'jquery.fancybox-1.3.4.css', :rel => "stylesheet" %> + <% end %> +<% end %> diff --git a/app/views/general/_stylesheet_includes.rhtml b/app/views/general/_stylesheet_includes.rhtml deleted file mode 100644 index 7a03680f8..000000000 --- a/app/views/general/_stylesheet_includes.rhtml +++ /dev/null @@ -1,21 +0,0 @@ - <%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet", :media => "all" %> - <%= stylesheet_link_tag 'fonts', :rel => "stylesheet", :media => "all" %> - <%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "print" %> - <% if !params[:print_stylesheet].nil? %> - <%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "all" %> - <% end %> - <%= stylesheet_link_tag 'admin-theme/jquery-ui-1.8.15.custom.css', :rel => 'stylesheet'%> - <!--[if LT IE 7]> - <style type="text/css">@import url("/stylesheets/ie6.css");</style> - <![endif]--> - <!--[if LT IE 7]> - <style type="text/css">@import url("/stylesheets/ie6-custom.css");</style> - <![endif]--> - <!--[if LT IE 8]> - <style type="text/css">@import url("/stylesheets/ie7.css");</style> - <![endif]--> - <!-- the following method for customising CSS is deprecated; see `doc/THEMES.md` for detail --> - <%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %> - <% if Configuration::force_registration_on_new_request %> - <%= stylesheet_link_tag 'jquery.fancybox-1.3.4', :rel => "stylesheet" %> - <% end %> diff --git a/app/views/general/_topnav.rhtml b/app/views/general/_topnav.html.erb index 8ef928bba..d37bd97d1 100644 --- a/app/views/general/_topnav.rhtml +++ b/app/views/general/_topnav.html.erb @@ -1,10 +1,12 @@ <div id="topnav"> <ul id="navigation"> - <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' and params[:action] != 'search' %>"><%= link_to _("Home"), frontpage_url %></li> - <li class="<%= 'selected' if params[:controller] == 'request' and ['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("Make a request"), select_authority_url, :id => 'make-request-link' %></li> - <li class="<%= 'selected' if params[:controller] == 'request' and !['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("View requests"), request_list_successful_url %></li> - <li class="<%= 'selected' if params[:controller] == 'public_body' %>"><%= link_to _("View authorities"), list_public_bodies_default %></li> - <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] == 'blog' %>"><%= link_to _("Read blog"), blog_url %></li> - <li class="<%= 'selected' if params[:controller] == 'help' %>"><%= link_to _("Help"), help_about_url %></li> + <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' and params[:action] != 'search' %>"><%= link_to _("Home"), frontpage_path %></li> + <li class="<%= 'selected' if params[:controller] == 'request' and ['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("Make a request"), select_authority_path, :id => 'make-request-link' %></li> + <li class="<%= 'selected' if params[:controller] == 'request' and !['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("View requests"), request_list_successful_path %></li> + <li class="<%= 'selected' if params[:controller] == 'public_body' %>"><%= link_to _("View authorities"), list_public_bodies_default_path %></li> +<% unless AlaveteliConfiguration::blog_feed.empty? %> + <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] == 'blog' %>"><%= link_to _("Read blog"), blog_path %></li> +<% end %> + <li class="<%= 'selected' if params[:controller] == 'help' %>"><%= link_to _("Help"), help_about_path %></li> </ul> </div> diff --git a/app/views/general/blog.rhtml b/app/views/general/blog.html.erb index 07d6d2f14..b81989ca8 100644 --- a/app/views/general/blog.rhtml +++ b/app/views/general/blog.html.erb @@ -4,12 +4,12 @@ <div id="right_column"> <div class="act_link"> <h2><%= _("Stay up to date") %></h2> - <img src="/images/twitter-16.png" alt="twitter icon" class="twitter-icon"> <a href="https://twitter.com/<%= @twitter_user %>"><%= _("Follow us on twitter") %></a><br/><br/> - <img src="/images/feed-16.png" alt="RSS icon" valign="middle"> <a href="<%= @feed_url %>"><%= _("Subscribe to blog") %></a> + <%= image_tag "twitter-16.png", :alt => "twitter icon", :class => "twitter-icon" %> <a href="https://twitter.com/<%= @twitter_user %>"><%= _("Follow us on twitter") %></a><br/><br/> + <%= image_tag "feed-16.png", :alt => "RSS icon" %> <a href="<%= @feed_url %>"><%= _("Subscribe to blog") %></a> </div> - <% if Configuration::twitter_widget_id %> + <% if AlaveteliConfiguration::twitter_widget_id %> <div id="twitter"> - <a class="twitter-timeline" data-dnt=true href="https://twitter.com/<%= Configuration::twitter_username %>" data-widget-id="<%= Configuration::twitter_widget_id %>">Tweets by @<%= Configuration::twitter_username %></a> + <a class="twitter-timeline" data-dnt=true href="https://twitter.com/<%= AlaveteliConfiguration::twitter_username %>" data-widget-id="<%= AlaveteliConfiguration::twitter_widget_id %>">Tweets by @<%= AlaveteliConfiguration::twitter_username %></a> <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script> </div> <% end %> @@ -20,15 +20,15 @@ <h1><%=@title %></h1> <div id="blog"> - <% for item in @blog_items: %> + <% @blog_items.each do |item| %> <div class="blog_post"> - <h2 id="<%= Time.parse(item['pubDate'][0]).to_i %>"><a href="<%=item['link']%>"><%=h item['title'] %></a></h2> - <p class="subtitle"><%= _("Posted on {{date}} by {{author}}", :date=>simple_date(Time.parse(item['pubDate'][0])), :author=>item['creator']) %></p> + <h2 id="<%= Time.parse(item['pubDate'][0]).to_i %>"><a href="<%=item['link'][0]%>"><%=h item['title'][0] %></a></h2> + <p class="subtitle"><%= _("Posted on {{date}} by {{author}}", :date=>simple_date(Time.parse(item['pubDate'][0])), :author=> item['creator'] ? item['creator'][0] : item['author'][0]) %></p> <div> <% if item['encoded'] %> - <%= raw item['encoded'] %> + <%= raw item['encoded'][0] %> <% elsif item['description'] %> - <%= raw item['description'] %> + <%= raw item['description'][0] %> <% end %> </div> <p><em> diff --git a/app/views/general/custom_css.rhtml b/app/views/general/custom_css.rhtml deleted file mode 100644 index 0def82ed0..000000000 --- a/app/views/general/custom_css.rhtml +++ /dev/null @@ -1 +0,0 @@ -// this should be overridden in a local "theme" plugin diff --git a/app/views/general/exception_caught.rhtml b/app/views/general/exception_caught.html.erb index 5f0dfe13d..8d78e2e92 100644 --- a/app/views/general/exception_caught.rhtml +++ b/app/views/general/exception_caught.html.erb @@ -7,8 +7,8 @@ <ul> <li><%= _("Check for mistakes if you typed or copied the address.")%></li> <li><%= _("Search the site to find what you were looking for.")%> - <% form_tag({:controller => "general", :action => "search_redirect"}, {:id => "search_form"}) do %> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> + <%= form_tag({:controller => "general", :action => "search_redirect"}, {:id => "search_form" }) do %> + <%= text_field_tag 'query', params[:query], { :size => 30, :title => "type your search term here" } %> <%= submit_tag _("Search") %> <% end %> </li> diff --git a/app/views/general/frontpage.rhtml b/app/views/general/frontpage.html.erb index 6eceb3b28..8bb32bdf2 100644 --- a/app/views/general/frontpage.rhtml +++ b/app/views/general/frontpage.html.erb @@ -1,4 +1,4 @@ -<%# TODO: Cache for 5 minutes %> +<% cache_if_caching_fragments("frontpage-#{@locale}", :expires_in => 5.minutes) do %> <div id="frontpage_splash"> <div id="left_column"> <%= render :partial => "frontpage_new_request" %> @@ -17,3 +17,4 @@ <%= render :partial => "frontpage_bodies_list" %> <%= render :partial => "frontpage_requests_list" %> </div> +<% end %> diff --git a/app/views/general/search.rhtml b/app/views/general/search.html.erb index 6df12d980..18f258444 100644 --- a/app/views/general/search.rhtml +++ b/app/views/general/search.html.erb @@ -16,13 +16,13 @@ <% if @query.nil? %> <h1><%= _("Search") %></h1> <% else %> - <h1><%= _("Search results") %></h1> + <h1><%= _("Search results") %></h1> <% end%> <% if @advanced %> <div id="advanced-search"> <p><%= _('To use the advanced search, combine phrases and labels as described in the search tips below.') %></p> - <% form_tag(advanced_search_url, :method => "get") do %> + <%= form_tag(advanced_search_url, :method => "get") do %> <p> <%= text_field_tag :query, @query, { :size => 60 } %> <%= hidden_field_tag 'sortby', @inputted_sortby %> @@ -35,15 +35,23 @@ <% end %> </div> <% else %> - <% form_tag(request.url, {:method => "get", :id => "search_form"}) do %> + + + <%= form_tag(search_redirect_path, {:method => "get", :id => "search_form"}) do %> + <p> + <%= text_field_tag 'query', params[:query], { :size => 40, :title => "type your search term here" } %> + + <%= submit_tag _("Search") %> + <%= link_to(_("Advanced search"), advanced_search_path) %> + </p> + <% end %> + <%= form_tag(request.url, {:method => "get", :id => "filter_form"}) do %> <p> - <%= text_field_tag 'query', params[:query], { :size => 40 } %> <%= hidden_field_tag 'sortby', @inputted_sortby %> <% if @bodies %> <%= hidden_field_tag 'bodies', 1 %> <% end %> - <%= submit_tag _("Search") %> - <%= link_to(_("Advanced search"), advanced_search_url) %> + </p> <div id="common-subfilters"> @@ -56,25 +64,13 @@ ["all", _("everything")]]%> <% for variety, label in labels %> <% if @variety_postfix != variety %> - <%= link_to label, search_url([params[:query], variety, @sort_postfix]) %> + <%= link_to label, search_path([params[:query], variety, @sort_postfix]) %> <% else %> <%= label %> <% end %> <%= "|" unless variety == labels.last[0]%> <% end %> </div> - - <% if false %> - <%-# Commented out for now as tags are of limited use when users can't see them. This will change in the future! -%> - <% if @variety_postfix != "users" %> - <div> - <%= label_tag(:query, _("Tags (separated by a space):")) %><%= text_field_tag(:tags, params[:tags], { :size => 20 }) %> - <% for tag in InfoRequest.get_tags %> - <%= tag.name_and_value %> - <% end %> - </div> - <% end %> - <% end %> </div> <% if @variety_postfix == "requests" %> @@ -94,7 +90,7 @@ <div> <h3 class="title"><%= _("Search in") %></h3> - <% [["sent", _("messages from users")], + <% [["sent", _("messages from users")], ["response", _("messages from authorities")], ["comment", _("comments")]].each_with_index do |item, index| variety, title = item %> @@ -110,21 +106,21 @@ <label class="form_label" for="query"> <%= _("and") %></label> <%= text_field_tag(:request_date_before, params[:request_date_before], {:class => "use-datepicker", :size => 10}) %> </div> - </div> + </div> <% end %> - + <div> <%= submit_tag _("Filter") if @variety_postfix == "requests"%> </div> <% end # Search form%> - + <% end # if @advanced %> - <% if !@query.nil? %> + <% if !@query.nil? && @total_hits > 0 %> <p id="search_controls"> - <%=link_to_unless @sortby == 'relevant', _("Show most relevant results first"), search_url([params[:query], @variety_postfix, 'relevant'], params) %> + <%=link_to_unless @sortby == 'relevant', _("Show most relevant results first"), search_path([params[:query], @variety_postfix, 'relevant'], params) %> | - <%=link_to_unless @sortby == 'newest', _("Newest results first"), search_url([params[:query], @variety_postfix, 'newest'], params) %> + <%=link_to_unless @sortby == 'newest', _("Newest results first"), search_path([params[:query], @variety_postfix, 'newest'], params) %> <% if @sortby == 'described' %> | <%= _('Recently described results first') %> <% end %> @@ -139,9 +135,11 @@ <% end %> <div style="clear:both;"></div> - <% if @total_hits == 0 %> <h2><%=@title %></h2> + <% if @spelling_correction %> + <p id="did_you_mean"><%= _('Did you mean: {{correction}}', :correction => search_link(@spelling_correction)) %></p> + <% end %> <% end %> <% if not @query.nil? %> @@ -160,11 +158,8 @@ </div> <%= will_paginate WillPaginate::Collection.new(@page, @bodies_per_page, @xapian_bodies.matches_estimated) %> - <% elsif @bodies && !@query.nil? && @xapian_bodies.results.size == 0 && @page == 1 %> - <% if @spelling_correction %> - <p id="did_you_mean"><%= _('Did you mean: {{correction}}', :correction => search_link(@spelling_correction, @postfix)) %></p> - <% end %> - <p><%= raw(_('<a href="%s">Browse all</a> or <a href="%s">ask us to add one</a>.') % [list_public_bodies_default, help_requesting_path + '#missing_body']) %></p> + <% elsif @variety_postfix == 'bodies' %> + <p><%= raw(_('<a href="{{browse_url}}">Browse all</a> or <a href="{{add_url}}">ask us to add one</a>.', :browse_url => list_public_bodies_default_path.html_safe, :add_url => (help_requesting_path + '#missing_body').html_safe)) %></p> <% end %> </div> diff --git a/app/views/help/_sidebar.rhtml b/app/views/help/_sidebar.html.erb index 2b7ed5647..2b7ed5647 100644 --- a/app/views/help/_sidebar.rhtml +++ b/app/views/help/_sidebar.html.erb diff --git a/app/views/help/_why_they_should_reply_by_email.rhtml b/app/views/help/_why_they_should_reply_by_email.html.erb index faaa2b2e2..faaa2b2e2 100644 --- a/app/views/help/_why_they_should_reply_by_email.rhtml +++ b/app/views/help/_why_they_should_reply_by_email.html.erb diff --git a/app/views/help/about.rhtml b/app/views/help/about.html.erb index 477f0e750..477f0e750 100644 --- a/app/views/help/about.rhtml +++ b/app/views/help/about.html.erb diff --git a/app/views/help/alaveteli.rhtml b/app/views/help/alaveteli.html.erb index 6210f9f24..6210f9f24 100644 --- a/app/views/help/alaveteli.rhtml +++ b/app/views/help/alaveteli.html.erb diff --git a/app/views/help/api.rhtml b/app/views/help/api.html.erb index da6253f87..c6488f93e 100644 --- a/app/views/help/api.rhtml +++ b/app/views/help/api.html.erb @@ -19,7 +19,7 @@ <dt>Linking to new requests</dt> <dd> <p>To encourage your users to make links to a particular public authority, use URLs of the form - <%= link_to new_request_to_body_url(:url_name => "liverpool_city_council") , new_request_to_body_url(:url_name => "liverpool_city_council") %>. + <%= link_to new_request_to_body_path(:url_name => "liverpool_city_council") , new_request_to_body_path(:url_name => "liverpool_city_council") %>. These are the parameters you can add to those URLs, either in the URL or from a form. <ul> @@ -30,15 +30,15 @@ </ul> </dd> - <dt>RSS (actually, Atom) feeds</h2> + <dt>RSS (actually, Atom) feeds</dt> <dd> <p>There are Atom feeds on most pages which list FOI requests, which you can use to get updates and links in XML format. Find the URL of the Atom feed in one of these ways: <ul> - <li>Look for the <img src="/images/feed-16.png" alt=""> RSS feed links.</li> - <li>Examine the <tt><link rel="alternate" type="application/atom+xml"></tt> tag in the head of the HTML. </li> - <li>Add <tt>/feed</tt> to the start of another URL. + <li>Look for the <%= image_tag "feed-16.png", :alt => "RSS icon" %> RSS feed links.</li> + <li>Examine the <code><link rel="alternate" type="application/atom+xml"></code> tag in the head of the HTML. </li> + <li>Add <code>/feed</code> to the start of another URL. </ul> <p>In particular, even complicated search queries have Atom feeds. @@ -53,8 +53,8 @@ objects in a structured form. Find them by: </p> <ul> - <li>Adding <tt>.json</tt> to the end of the URL. </li> - <li>Look for the <tt><link rel="alternate" type="application/json"></tt> tag in the head of the HTML. </li> + <li>Adding <code>.json</code> to the end of the URL. </li> + <li>Look for the <code><link rel="alternate" type="application/json"></code> tag in the head of the HTML. </li> </ul> <p>Requests, users and authorities all have JSON versions containing basic @@ -67,11 +67,19 @@ <dd> <p> A spreadsheet file listing every body in WhatDoTheyKnow is available: - <%= link_to "all-authorities.csv", all_public_bodies_csv_url() %> + <%= link_to "all-authorities.csv", all_public_bodies_csv_path %> </p> </dd> - </dl> + + + <dt> 5. Write API </dt> + <dd> + <p> + The write API is designed to be used by authorities to create their own requests in the system. The API is currently used by mySociety's <a href="https://github.com/mysociety/foi-register">FOI Register software</a> to support using Alaveteli as a disclosure log for all FOI activity at a particular public body. More technical information about the write API is available on the <a href="https://github.com/mysociety/alaveteli/wiki/API#write-api">Alaveteli wiki</a>. + </p> + </dd> +</dl> <p>Please <a href="<%= help_contact_path %>">contact us</a> if you need an API feature that isn't there yet. It's very much a work in progress, and we do add things when people ask us to.</p> diff --git a/app/views/help/contact.rhtml b/app/views/help/contact.html.erb index fab5017b8..ad89db9ec 100644 --- a/app/views/help/contact.rhtml +++ b/app/views/help/contact.html.erb @@ -40,13 +40,13 @@ <% end %> </div> -<% form_for :contact do |f| %> +<%= form_for :contact do |f| %> <% if not @user %> <p> <label class="form_label" for="contact_name">Your name:</label> <%= f.text_field :name, :size => 20 %> - (or <%= link_to "sign in", signin_url(:r => request.request_uri) %>) + (or <%= link_to "sign in", signin_path(:r => request.fullpath) %>) </p> <p> diff --git a/app/views/help/credits.rhtml b/app/views/help/credits.html.erb index 02f1e40e8..02f1e40e8 100644 --- a/app/views/help/credits.rhtml +++ b/app/views/help/credits.html.erb diff --git a/app/views/help/officers.rhtml b/app/views/help/officers.html.erb index b13e225fe..a227b01c5 100644 --- a/app/views/help/officers.rhtml +++ b/app/views/help/officers.html.erb @@ -52,7 +52,7 @@ <dt id="email_only">An email isn't a sufficient address for an FOI request! <a href="#email_only">#</a> </dt> <dd>Yes it is. This - <a href="http://www.whatdotheyknow.com/request/1142/response/2894/attach/5/20080806100741260.pdf">letter from the ICO to Rother District Council</a> gives guidance on the matter, specifically + <a href="https://www.whatdotheyknow.com/request/1142/response/2894/attach/5/20080806100741260.pdf">letter from the ICO to Rother District Council</a> gives guidance on the matter, specifically in the context of requests made via WhatDoTheyKnow. </dd> @@ -66,7 +66,7 @@ via WhatDoTheyKnow. Moreover, since all requests are public it is much easier for you to see if one of our users is making vexatious requests. </p> <p>If that isn't enough for you, the - <a href="http://www.whatdotheyknow.com/request/1142/response/2894/attach/5/20080806100741260.pdf">letter from the ICO to Rother District Council</a> gives some guidance on the matter.</p> + <a href="https://www.whatdotheyknow.com/request/1142/response/2894/attach/5/20080806100741260.pdf">letter from the ICO to Rother District Council</a> gives some guidance on the matter.</p> </dd> <dt id="spam_problems">I can see a request on WhatDoTheyKnow, but we never got it by email!<a href="#spam_problems">#</a> </dt> @@ -135,7 +135,7 @@ <li>If the day the request email was delivered was a non-working day, we count the next working day as "day one". Delivery is delivery, even if it happened on - the weekend. Some authorities <a href="http://www.whatdotheyknow.com/request/policy_regarding_body_scans#incoming-1100">disagree with this</a>, our lawyer disagrees with them. </li> + the weekend. Some authorities <a href="https://www.whatdotheyknow.com/request/policy_regarding_body_scans#incoming-1100">disagree with this</a>, our lawyer disagrees with them. </li> <li>Requesters are encouraged to mark when they have <strong>clarified</strong> their request so the clock resets, but sometimes they get this wrong. If you @@ -222,7 +222,7 @@ form in an annotation. </dd> - <dt id="copyright"><a name="commercial"></a>What is your policy on copyright of documents?<a href="#copyright">#</a> </dt> + <dt id="copyright"><a id="commercial"></a>What is your policy on copyright of documents?<a href="#copyright">#</a> </dt> <dd>Our Freedom of Information law is "applicant blind", so anyone in the world can request the same document and get a copy of it. @@ -235,9 +235,6 @@ </dl> - - </dl> - <p><strong>If you haven't already</strong>, read <a href="<%= help_about_path %>">the introduction</a> --> <br><strong>Otherwise</strong>, the <a href="<%= help_credits_path %>">credits</a> or the <a href="<%= help_api_path %>">programmers API</a> --> diff --git a/app/views/help/privacy.rhtml b/app/views/help/privacy.html.erb index 8e5293892..f0e82ab13 100644 --- a/app/views/help/privacy.rhtml +++ b/app/views/help/privacy.html.erb @@ -84,6 +84,11 @@ ask a friend to. We don't have the resources to do this for everyone. </dd> +<dt id="anonymous">Why are there anonymous requests on the site? <a href="#anonymous">#</a> </dt> +<dd> +Some public authorities are using mySociety's <a href="https://github.com/mysociety/foi-register">FOI Register</a> software in order to use WhatDoTheyKnow as a disclosure log for all their FOI activity. When people make requests to the authority their names will usually be withheld from publication just as they would in an authority disclosure log on an authority website. +</dd> + <dt id="full_address">They've asked for my postal address! <a href="#full_address">#</a> </dt> <dd> @@ -111,7 +116,7 @@ address should be treated as the return address." <dd> <p>If an authority only has a paper copy of the information that you want, they may ask you for a postal address. To start with, try persuading them -to scan in the documents for you. You can even <a href="http://www.whatdotheyknow.com/request/car_parking_charges_policy_and_a#outgoing-532">offer to gift them a scanner</a>, which in that particular case +to scan in the documents for you. You can even <a href="https://www.whatdotheyknow.com/request/car_parking_charges_policy_and_a#outgoing-532">offer to gift them a scanner</a>, which in that particular case embarrassed the authority into finding one they had already.</p> <p>If that doesn't work, and you want to provide your postal address privately diff --git a/app/views/help/requesting.rhtml b/app/views/help/requesting.html.erb index e7cfdd199..51358c6d9 100644 --- a/app/views/help/requesting.rhtml +++ b/app/views/help/requesting.html.erb @@ -30,7 +30,7 @@ <dt id="missing_body">You're missing the public authority that I want to request from! <a href="#missing_body">#</a> </dt> <dd> - <p>Please <a href="<%= help_contact_path %>">contact us</a> with the name of the public authority and, + <p>Please <a href="<%= new_change_request_path %>">contact us</a> with the name of the public authority and, if you can find it, their contact email address for Freedom of Information requests. </p> <p>If you'd like to help add a whole category of public authority to the site, we'd love @@ -235,7 +235,7 @@ <dt id="eir">Why can I only request information about the environment from some authorities? <a href="#eir">#</a> </dt> <dd> - <p>Some public authorities, such as <a href="http://www.whatdotheyknow.com/body/south_east_water">South East Water</a>, + <p>Some public authorities, such as <a href="https://www.whatdotheyknow.com/body/south_east_water">South East Water</a>, don't come under the Freedom of Information Act, but do come under another law called the Environmental Information Regulations (EIR). </p> diff --git a/app/views/help/unhappy.rhtml b/app/views/help/unhappy.html.erb index 2b00341c2..79e3f8273 100644 --- a/app/views/help/unhappy.rhtml +++ b/app/views/help/unhappy.html.erb @@ -29,7 +29,7 @@ to your request '<%=request_link(@info_request) %>'? <p> <% if !@info_request.nil? %> - <%= link_to "Request an internal review", show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup", :class => 'link_button_green' %> and then write a message asking the authority to review your request. + <%= link_to "Request an internal review", show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup", :class => 'link_button_green' %> and then write a message asking the authority to review your request. <% else %> At the bottom of the relevant request page on <%= site_name %> choose "request an internal review". Then write a message asking for an internal @@ -62,7 +62,7 @@ to the Scottish Information Commissioner</a>. Information Commissioner, either <% if !@info_request.nil? %> include a link to your request - <strong><%=h main_url(request_url(@info_request)) %></strong> + <strong><%=h request_url(@info_request) %></strong> <% else %> include a link to your request on <%= site_name %> <% end %> diff --git a/app/views/holiday/due_date.rhtml b/app/views/holiday/due_date.html.erb index 6f8c2e51a..6f8c2e51a 100644 --- a/app/views/holiday/due_date.rhtml +++ b/app/views/holiday/due_date.html.erb diff --git a/app/views/info_request_batch/_info_request_batch.html.erb b/app/views/info_request_batch/_info_request_batch.html.erb new file mode 100644 index 000000000..86ef90654 --- /dev/null +++ b/app/views/info_request_batch/_info_request_batch.html.erb @@ -0,0 +1,15 @@ +<div class="request_listing"> + <div class="request_left"> + <span class="head"> + <%= link_to highlight_words(info_request_batch.title, @highlight_words), info_request_batch_path(info_request_batch) %> + </span> + <div class="requester"> + <%= _('Batch created by {{info_request_user}} on {{date}}.', :info_request_user => user_link_absolute(info_request_batch.user),:date=>simple_date(info_request_batch.created_at)) %> + </div> + </div> + <div class="request_right"> + <span class="desc"> + <%= excerpt(info_request_batch.body, '', :radius => 150) %> + </span> + </div> +</div> diff --git a/app/views/info_request_batch/show.html.erb b/app/views/info_request_batch/show.html.erb new file mode 100644 index 000000000..aaecdd45d --- /dev/null +++ b/app/views/info_request_batch/show.html.erb @@ -0,0 +1,25 @@ +<% @title = _("{{title}} - a batch request", :title => @info_request_batch.title) %> +<h1><%= @title %></h1> +<% if @info_request_batch.sent_at %> + <%= n_('Sent to one authority by {{info_request_user}} on {{date}}.', 'Sent to {{authority_count}} authorities by {{info_request_user}} on {{date}}.', @info_request_batch.info_requests.size, :authority_count=> @info_request_batch.info_requests.size, :info_request_user => user_link(@info_request_batch.user), :date => simple_date(@info_request_batch.sent_at)) %> + <div class="results_section"> + <div class="results_block"> + <% @info_requests.each do |info_request| %> + <%= render :partial => 'request/request_listing_via_event', :locals => { :event => info_request.last_event_forming_initial_request, :info_request => info_request } %> + <% end %> + </div> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @info_request_batch.info_requests.visible.count) %> + </div> + +<% else %> + <%= _('Created by {{info_request_user}} on {{date}}.', :info_request_user => user_link(@info_request_batch.user), :date => simple_date(@info_request_batch.created_at)) %> + <%= _('Requests will be sent to the following bodies:') %> + <div class="results_section"> + <div class="results_block"> + <%= render :partial => 'public_body/body_listing', :locals => { :public_bodies => @public_bodies } %> + </div> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @info_request_batch.public_bodies.count) %> + </div> + +<% end %> + diff --git a/app/views/info_request_batch_mailer/batch_sent.text.erb b/app/views/info_request_batch_mailer/batch_sent.text.erb new file mode 100644 index 000000000..25fb7d06a --- /dev/null +++ b/app/views/info_request_batch_mailer/batch_sent.text.erb @@ -0,0 +1,15 @@ +<%= _('Your batch request "{{title}}" has been sent', :title => @info_request_batch.title) %> + + +<%= _('Follow this link to see the requests:')%> + +<%= @url %> + +<% if !@unrequestable.empty? %> +<%= _('Unfortunately, we do not have a working address for {{public_body_names}}.', :public_body_names => @unrequestable.map{|body| body.name}.join(",")) %> +<%= _('You may be able to find one on their website, or by phoning them up and asking. If you manage to find one, then please send it to us:') %> + +<%= help_contact_url %> + +<% end %> +-- <%= _('the {{site_name}} team', :site_name => site_name) %> diff --git a/app/views/layouts/_favicon.html.erb b/app/views/layouts/_favicon.html.erb new file mode 100644 index 000000000..0ee7d48e3 --- /dev/null +++ b/app/views/layouts/_favicon.html.erb @@ -0,0 +1 @@ +<%= favicon_link_tag 'favicon.ico' %> diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb new file mode 100644 index 000000000..c1f9335b1 --- /dev/null +++ b/app/views/layouts/admin.html.erb @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html lang="en-gb"> + <head> + <meta http-equiv="content-type" content="text/html;charset=UTF-8" > + <title><%= site_name %> admin<%= @title ? ":" : "" %> <%=@title%></title> + + <%= javascript_include_tag "admin" %> + <%= stylesheet_link_tag "admin", :title => "Main", :rel => "stylesheet" %> + + </head> + <body class="admin"> + <div id="main" class="container"> + <%= render :partial => 'admin_general/admin_navbar' %> + <% if flash[:error] %> + <div class="row"> + <div class="span12"> + <div class="alert alert-error"> + <%= flash[:error] %> + </div> + </div> + </div> + <% end %> + + <% if flash[:notice] %> + <div class="row"> + <div class="span12"> + <div class="alert alert-info"> + <%= flash[:notice] %> + </div> + </div> + </div> + <% end %> + + <%= yield %> + </div> + </body> +</html> diff --git a/app/views/layouts/admin.rhtml b/app/views/layouts/admin.rhtml deleted file mode 100644 index d85eecbf2..000000000 --- a/app/views/layouts/admin.rhtml +++ /dev/null @@ -1,39 +0,0 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> -<html lang="en-gb"> - <head> - <meta http-equiv="content-type" content="text/html;charset=UTF-8" > - <title><%= site_name %> admin<%= @title ? ":" : "" %> <%=@title%></title> - - <%= javascript_include_tag 'jquery.js', 'jquery-ui.min' %> - - <%= stylesheet_link_tag 'admin-theme/jquery-ui-1.8.15.custom.css', :rel => 'stylesheet'%> - <%= stylesheet_link_tag 'admin', :title => "Main", :rel => "stylesheet" %> - </head> - <body class="admin"> - - <p> - <strong><%= link_to 'Alaveteli', main_url('/') %> admin:</strong> - <%= link_to 'Summary', admin_url("") %> - | <%= link_to 'Timeline', admin_url("timeline") %> - | <%= link_to 'Stats', admin_url("stats") %> - | <%= link_to 'Debug', admin_url("debug") %> - <strong>View:</strong> - <%= link_to 'Authorities', admin_url("body/list") %> - | <%= link_to 'Requests', admin_url("request/list") %> - | <%= link_to 'Users', admin_url("user/list") %> - | <%= link_to 'Tracks', admin_url("track/list") %> - </p> - <%= render :partial => 'general/locale_switcher' %> - - <% if flash[:error] %> - <p id="error"><%= flash[:error] %></p> - <% end %> - - <% if flash[:notice] %> - <p id="notice"><%= flash[:notice] %></p> - <% end %> - - <%= yield %> - - </body> -</html> diff --git a/app/views/layouts/contact_mailer.rhtml b/app/views/layouts/contact_mailer.html.erb index 3cdc75009..3cdc75009 100644 --- a/app/views/layouts/contact_mailer.rhtml +++ b/app/views/layouts/contact_mailer.html.erb diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.html.erb index 6ac7064a7..52b718be8 100644 --- a/app/views/layouts/default.rhtml +++ b/app/views/layouts/default.html.erb @@ -1,6 +1,7 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<!DOCTYPE html> <html lang="<%= I18n.locale %>"> <head> + <meta charset="utf-8"> <title> <% if @title %> <%=@title%> - <%= site_name %> @@ -9,18 +10,17 @@ <% end %> </title> - <link rel="shortcut icon" href="/favicon.ico"> + <%= render :partial => 'layouts/favicon' %> <%= render :partial => 'general/stylesheet_includes' %> <% if is_admin? %> - <%= stylesheet_link_tag "/adminbootstraptheme/stylesheets/admin", :title => "Main", :rel => "stylesheet" %> + <%= stylesheet_link_tag "admin", :title => "Main", :rel => "stylesheet" %> <% end %> - <%= javascript_include_tag 'jquery.js', 'jquery-ui.min','jquery.cookie.js', 'general.js' %> + <%= javascript_include_tag "application" %> <% if @profile_photo_javascript %> - <script type="text/javascript" src="/javascripts/jquery.Jcrop.js"></script> - <script type="text/javascript" src="/javascripts/profile_photo.js"></script> - <link rel="stylesheet" href="/stylesheets/jquery.Jcrop.css" type="text/css" > + <%= javascript_include_tag "profile-photos" %> + <%= stylesheet_link_tag "jquery.Jcrop.css" %> <% end %> <% if @feed_autodetect %> @@ -32,7 +32,7 @@ <% end %> <% end %> <% if @has_json %> - <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h main_url(request.request_uri, '.json') %>"> + <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h url_for(request.params.merge(:format => 'json')) %>"> <% end %> <% if @no_crawl %> @@ -42,8 +42,8 @@ <%= render :partial => 'general/before_head_end' %> </head> <body class="<%= 'front' if params[:action] == 'frontpage' %>"> - <% if Configuration::force_registration_on_new_request && !@user %> - <%= javascript_include_tag 'jquery.fancybox-1.3.4.pack' %> + <% if AlaveteliConfiguration::force_registration_on_new_request && !@user %> + <%= javascript_include_tag 'jquery.fancybox-1.3.4.pack.js' %> <script type="text/javascript"> $(document).ready(function() { $("#make-request-link").fancybox({ @@ -51,7 +51,7 @@ 'width': 920, 'height': 400, 'type': 'iframe', - 'href': '/<%= I18n.locale %>/profile/sign_in?modal=1', + 'href': '/<%= FastGettext.locale %>/profile/sign_in?modal=1', 'onClosed': function() { // modal_signin_successful variable set by modal dialog box if (typeof modal_signin_successful != 'undefined' ) { @@ -67,52 +67,22 @@ <%= render :partial => 'admin_general/admin_navbar' %> <% end %> -<% if !@popup_banner.blank? %> -<div id="everypage" class="jshide"> - <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p> - <%= @popup_banner %> - <p class="popup-close"><a href="#top" onclick="$.cookie('seen_foi2', 1, { expires: 7, path: '/' }); $('#everypage').hide('slow'); return false;"><%= _('Close') %></a></p> -</div> -<% end %> - <div class="entirebody"> - <div id="banner"> - <div id="banner_inner"> - <div class="lang"><%= render :partial => 'general/locale_switcher' %></div> - - <% if not (controller.action_name == 'signin' or controller.action_name == 'signup') %> - <div id="logged_in_bar"> - <% if @user %> - <%= _('Hello, {{username}}!', :username => h(@user.name))%> - - <% if @user %> - <%=link_to _("My requests"), show_user_requests_path(:url_name => @user.url_name) %> - <%=link_to _("My profile"), show_user_profile_path(:url_name => @user.url_name) %> - <%=link_to _("My wall"), show_user_wall_path(:url_name => @user.url_name) %> - <% end %> - - - <%= link_to _("Sign out"), signout_url(:r => request.request_uri) %> - <% else %> - <%= link_to _("Sign in or sign up"), signin_url(:r => request.request_uri) %> - <% end %> - </div> - <% end %> - - <div id="navigation_search"> - <form id="navigation_search_form" method="post" action="<%= search_redirect_path %>"> - <p> - <%= text_field_tag 'query', params[:query], { :size => 40, :id => "navigation_search_query" } %> - <input id="navigation_search_button" type="submit" value="search"> - </p> - </form> - </div> - - <%= render :partial => 'general/orglink' %> - - <%= render :partial => 'general/topnav' %> - </div> + <% popup_banner = render(:partial => "general/popup_banner").strip %> + <% if popup_banner.present? and ! @render_to_file %> + <div id="everypage" class="popup"> + <span class="popup-content"> + <%= raw popup_banner %> + </span> + <span class="popup-close"><a href="#top" ><%= _('Close') %></a></span> + </div> + <% end %> + <div id="other-country-notice" class="popup"> + <span class="popup-content"> + </span> + <span class="popup-close"><a href="#top" ><%= _('Close') %></a></span> </div> + <%= render :partial => 'general/header' %> <div id="wrapper"> <div id="content"> <% if flash[:notice] %> @@ -132,20 +102,19 @@ <%= render :partial => 'general/footer' %> </div> -<div id="other-country-notice"></div> <div id="link_box"><span class="close-button">X</span> <%= _("Paste this link into emails, tweets, and anywhere else:") %> <br /> <input type="text"> </div> <% - unless Configuration::ga_code.empty? || (@user && @user.super?) %> + unless AlaveteliConfiguration::ga_code.empty? || (@user && @user.super?) %> <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> - var pageTracker = _gat._getTracker("<%= Configuration::ga_code %>"); + var pageTracker = _gat._getTracker("<%= AlaveteliConfiguration::ga_code %>"); pageTracker._trackPageview(); </script> diff --git a/app/views/layouts/no_chrome.rhtml b/app/views/layouts/no_chrome.html.erb index 74c79b701..e613b8ca2 100644 --- a/app/views/layouts/no_chrome.rhtml +++ b/app/views/layouts/no_chrome.html.erb @@ -1,6 +1,7 @@ -<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> +<!DOCTYPE html> <html lang="<%= I18n.locale %>"> <head> + <meta charset="utf-8"> <title> <% if @title %> <%=@title%> - <%= site_name %> @@ -9,21 +10,16 @@ <% end %> </title> - <script type="text/javascript" src="/javascripts/jquery.js"></script> + <%= javascript_include_tag "application" %> - <%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet" %> - <%= stylesheet_link_tag 'fonts', :rel => "stylesheet" %> - <%= stylesheet_link_tag 'theme', :rel => "stylesheet" %> - <!--[if LT IE 7]> - <style type="text/css">@import url("/stylesheets/ie6.css");</style> + <%= stylesheet_link_tag 'application', :title => "Main", :rel => "stylesheet" %> + <%= stylesheet_link_tag 'fonts', :rel => "stylesheet" %> + <!--[if LT IE 7]> + <%= stylesheet_link_tag 'ie6', :rel => "stylesheet" %> <![endif]--> - <!--[if LT IE 7]> - <style type="text/css">@import url("/stylesheets/ie6-custom.css");</style> - <![endif]--> - <%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %> </head> <body> - <div class="entirebody"> + <div class="entirebody"> <div id="content"> <% if flash[:notice] %> <div id="notice"><%= flash[:notice] %></div> @@ -38,4 +34,4 @@ </div> </div> </body> -</html>
\ No newline at end of file +</html> diff --git a/app/views/layouts/outgoing_mailer.rhtml b/app/views/layouts/outgoing_mailer.html.erb index 8bf8ef216..8bf8ef216 100644 --- a/app/views/layouts/outgoing_mailer.rhtml +++ b/app/views/layouts/outgoing_mailer.html.erb diff --git a/app/views/layouts/request_mailer.rhtml b/app/views/layouts/request_mailer.html.erb index 3cdc75009..3cdc75009 100644 --- a/app/views/layouts/request_mailer.rhtml +++ b/app/views/layouts/request_mailer.html.erb diff --git a/app/views/layouts/user_mailer.rhtml b/app/views/layouts/user_mailer.html.erb index 3cdc75009..3cdc75009 100644 --- a/app/views/layouts/user_mailer.rhtml +++ b/app/views/layouts/user_mailer.html.erb diff --git a/app/views/outgoing_mailer/_followup_footer.rhtml b/app/views/outgoing_mailer/_followup_footer.text.erb index d7bc7c5aa..d7bc7c5aa 100644 --- a/app/views/outgoing_mailer/_followup_footer.rhtml +++ b/app/views/outgoing_mailer/_followup_footer.text.erb diff --git a/app/views/outgoing_mailer/followup.rhtml b/app/views/outgoing_mailer/followup.text.erb index 7050a295b..049ebc881 100644 --- a/app/views/outgoing_mailer/followup.rhtml +++ b/app/views/outgoing_mailer/followup.text.erb @@ -1,6 +1,6 @@ -<%= @outgoing_message.body.strip %> +<%= raw @outgoing_message.body.strip %> -<%= @outgoing_message.quoted_part_to_append_to_email.strip %> +<%= raw @outgoing_message.quoted_part_to_append_to_email.strip %> ------------------------------------------------------------------- <%= _('Please use this email address for all replies to this request:')%> diff --git a/app/views/outgoing_mailer/initial_request.rhtml b/app/views/outgoing_mailer/initial_request.text.erb index d537a20bc..b0f1dc9e8 100644 --- a/app/views/outgoing_mailer/initial_request.rhtml +++ b/app/views/outgoing_mailer/initial_request.text.erb @@ -1,4 +1,4 @@ -<%= @outgoing_message.body.strip %> +<%= raw @outgoing_message.body.strip %> ------------------------------------------------------------------- @@ -6,7 +6,7 @@ <%= @info_request.incoming_email %> <%= _('Is {{email_address}} the wrong address for {{type_of_request}} requests to {{public_body_name}}? If so, please contact us using this form:', :email_address => @info_request.public_body.request_email, :type_of_request => @info_request.law_used_full, :public_body_name => @info_request.public_body.name)%> -<%= help_contact_url %> +<%= new_change_request_url(:body => @info_request.public_body.url_name) %> <%= render :partial => 'followup_footer' %> diff --git a/app/views/public_body/_alphabet.html.erb b/app/views/public_body/_alphabet.html.erb new file mode 100644 index 000000000..63b24e0fb --- /dev/null +++ b/app/views/public_body/_alphabet.html.erb @@ -0,0 +1,3 @@ +<%- "A".upto("Z") do |l| -%> + <%= link_to_unless (@tag == l), l, list_public_bodies_path(:tag => l.downcase) %> +<% end %> diff --git a/app/views/public_body/_alphabet.rhtml b/app/views/public_body/_alphabet.rhtml deleted file mode 100644 index 92674b8aa..000000000 --- a/app/views/public_body/_alphabet.rhtml +++ /dev/null @@ -1,3 +0,0 @@ -<% "A".upto("Z") do |l| -%> - <%= link_to_unless (@tag == l), l, list_public_bodies_url(:tag => l.downcase) %> -<% end %> diff --git a/app/views/public_body/_body_listing.rhtml b/app/views/public_body/_body_listing.html.erb index 864ab8c9b..864ab8c9b 100644 --- a/app/views/public_body/_body_listing.rhtml +++ b/app/views/public_body/_body_listing.html.erb diff --git a/app/views/public_body/_body_listing_single.rhtml b/app/views/public_body/_body_listing_single.html.erb index b01d2ebb2..91a07d09c 100644 --- a/app/views/public_body/_body_listing_single.rhtml +++ b/app/views/public_body/_body_listing_single.html.erb @@ -4,7 +4,7 @@ <div class="body_listing"> <span class="head"> - <%= link_to highlight_words(public_body.name, @highlight_words), public_body_url(public_body) %> + <%= link_to highlight_words(public_body.name, @highlight_words), public_body_path(public_body) %> </span> <span class="desc"> <% if !public_body.short_name.empty? || !public_body.notes_without_html.empty? %> @@ -18,9 +18,16 @@ <% end %> </span> <span class="bottomline"> - <%= n_('%d request made.', '%d requests made.', public_body.info_requests.size) % public_body.info_requests.size %> - <% if !@include_request_link_in_authority_listing.nil? %> - <%= link_to _("Make your own request"), public_body_url(public_body) %>. + <%= n_('{{count}} request made.', '{{count}} requests made.', public_body.info_requests.size, + :count => public_body.info_requests.size) %> + <% if !public_body.is_requestable? && public_body.not_requestable_reason != 'bad_contact' %> + <% if public_body.not_requestable_reason == 'defunct' %> + <%= _('Defunct.') %> + <% end %> + <% else %> + <% if !@include_request_link_in_authority_listing.nil? %> + <%= link_to _("Make your own request"), public_body_path(public_body) %>. + <% end %> <% end %> <br> <span class="date_added"> diff --git a/app/views/public_body/_list_sidebar_extra.html.erb b/app/views/public_body/_list_sidebar_extra.html.erb new file mode 100644 index 000000000..290593d6a --- /dev/null +++ b/app/views/public_body/_list_sidebar_extra.html.erb @@ -0,0 +1,6 @@ +<p> + <%= link_to _('Are we missing a public authority?'), help_requesting_path + '#missing_body' %> +</p> +<p> + <%= link_to _('List of all authorities (CSV)'), all_public_bodies_csv_path %> +</p> diff --git a/app/views/public_body/_list_sidebar_extra.rhtml b/app/views/public_body/_list_sidebar_extra.rhtml deleted file mode 100644 index 54f20a736..000000000 --- a/app/views/public_body/_list_sidebar_extra.rhtml +++ /dev/null @@ -1,6 +0,0 @@ -<p> - <%= raw(_('<a href="%s">Are we missing a public authority?</a>') % [help_requesting_path + '#missing_body']) %> -</p> -<p> - <%= link_to _('List of all authorities (CSV)'), all_public_bodies_csv_url() %> -</p> diff --git a/app/views/public_body/_search_ahead.rhtml b/app/views/public_body/_search_ahead.html.erb index 7ade89b8e..2de638034 100644 --- a/app/views/public_body/_search_ahead.rhtml +++ b/app/views/public_body/_search_ahead.html.erb @@ -1,4 +1,4 @@ -<div> + <% if !@xapian_requests.nil? %> <% if @xapian_requests.results.size > 0 %> <h3><%= _('Top search results:') %></h3> @@ -10,12 +10,9 @@ <% end %> <div id="authority_search_ahead_results"> <% for result in @xapian_requests.results %> - <%= render :partial => 'body_listing_single', :locals => { :public_body => result[:model] } %> + <%= render :partial => 'public_body/body_listing_single', :locals => { :public_body => result[:model] } %> <% end %> </div> - <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @xapian_requests.matches_estimated) %> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @xapian_requests.matches_estimated), :params => {:controller=>"request", :action => "select_authority"} %> + <p><%= raw(_('<a href="{{browse_url}}">Browse all</a> or <a href="{{add_url}}">ask us to add one</a>.', :browse_url => list_public_bodies_default_path.html_safe, :add_url => (help_requesting_path + '#missing_body').html_safe)) %></p> <% end %> -</div> - - - diff --git a/app/views/public_body/list.rhtml b/app/views/public_body/list.html.erb index 94fbb759c..ce24daaf9 100644 --- a/app/views/public_body/list.rhtml +++ b/app/views/public_body/list.html.erb @@ -10,7 +10,7 @@ <% for row in PublicBodyCategories::get().with_headings() %> <% if row.instance_of?(Array) %> <li> - <%= link_to_unless (@tag == row[0]), row[1], list_public_bodies_url(:tag => row[0]) %> + <%= link_to_unless (@tag == row[0]), row[1], list_public_bodies_path(:tag => row[0]) %> </li> <% else %> <% if not first_row %> @@ -23,7 +23,7 @@ <% end %> <% end %> <% if not first_row %> - </ul> + </ul> <% end %> <%= render :partial => "list_sidebar_extra" %> </div> @@ -32,16 +32,22 @@ <div id="left_column_flip"> <h1><%= _('Public authorities') %></h1> -<% form_tag(list_public_bodies_default_url, :method => "get", :id=>"search_form") do %> +<%= form_tag(list_public_bodies_default_url, :method => "get", :id=>"search_form") do %> <div> - <%= text_field_tag(:public_body_query, params[:public_body_query]) %> + <%= text_field_tag(:public_body_query, params[:public_body_query], { :title => "type your search term here" } ) %> <%= submit_tag(_("Search")) %> </div> <% end %> -<h2 class="publicbody_results"><%= n_('Found %d public authority %s', 'Found %d public authorities %s', @public_bodies.total_entries) % [@public_bodies.total_entries, @description] %></h2> +<h2 class="publicbody_results"> + <%= n_('Found {{count}} public authority {{description}}', + 'Found {{count}} public authorities {{description}}', + @public_bodies.total_entries, + :count => @public_bodies.total_entries, + :description => @description) %> +</h2> <%= render :partial => 'body_listing', :locals => { :public_bodies => @public_bodies } %> <%= will_paginate(@public_bodies) %><br/> - <%= raw _('<a href="%s">Can\'t find the one you want?</a>') % [help_requesting_path + '#missing_body'] %> + <%= link_to _("Can't find the one you want?"), help_requesting_path + '#missing_body' %> </div> diff --git a/app/views/public_body/show.rhtml b/app/views/public_body/show.html.erb index 6431b4742..c36396149 100644 --- a/app/views/public_body/show.rhtml +++ b/app/views/public_body/show.html.erb @@ -4,7 +4,12 @@ <h2><%= _('Follow this authority')%></h2> <% follower_count = TrackThing.count(:all, :conditions => ["public_body_id = ?", @public_body.id]) %> - <p><%= raw(n_("<span id='follow_count'>%d</span> person is following this authority", "<span id='follow_count'>%d</span> people are following this authority", follower_count) % follower_count) %></p> + <p> + <%= n_("{{count}} person is following this authority", + "{{count}} people are following this authority", + follower_count, + :count => content_tag(:span, follower_count, :id => "follow_count")) %> + </p> <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %> <h2><%= _('More about this authority')%></h2> @@ -26,46 +31,45 @@ <% end %> <% end %> <% end %> - <%= link_to _('View FOI email address'), view_public_body_email_url(@public_body.url_name) %><br> - </div> + <%= link_to _('View FOI email address'), view_public_body_email_path(@public_body.url_name) %><br> + <%= link_to _("Ask us to update FOI email"), new_change_request_path(:body => @public_body.url_name) %><br> + </div> <div id="header_left"> <p class="public-body-name-prefix"><%= _("Freedom of information requests to") %></p> <h1><%=h(@public_body.name)%></h1> <p class="subtitle"> - <%=@public_body.type_of_authority(true)%><% if not @public_body.short_name.empty? %>, + <%=@public_body.type_of_authority(true)%><% if not @public_body.short_name.empty? %>, <%= _('also called {{public_body_short_name}}', :public_body_short_name => h(@public_body.short_name))%><% end %> <% if !@user.nil? && @user.admin_page_links? %> - (<%= link_to _("admin"), public_body_admin_url(@public_body) %>) + (<%= link_to _("admin"), admin_body_show_path(@public_body) %>) <% end %> </p> - <% if @public_body.has_notes? && (@public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact') %> - <p><%= @public_body.notes_as_html %></p> - <% end %> - - <% if @public_body.eir_only? %> - <p><%= _('You can only request information about the environment from this authority.')%></p> + <% if @public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact' %> + <% if @public_body.has_notes? %> + <p><%= @public_body.notes_as_html.html_safe %></p> + <% end %> + <% if @public_body.eir_only? %> + <p><%= _('You can only request information about the environment from this authority.')%></p> + <% end %> + <% else %> + <% if @public_body.not_requestable_reason == 'not_apply' %> + <p><%= _('Freedom of Information law does not apply to this authority, so you cannot make + a request to it.')%></p> + <% elsif @public_body.not_requestable_reason == 'defunct' %> + <p><%= _('This authority no longer exists, so you cannot make a request to it.')%></p> + <% else %> + <p><%= _('For an unknown reason, it is not possible to make a request to this authority.')%></p> + <% end %> <% end %> <div id="stepwise_make_request"> <% if @public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact' %> - <% if @public_body.eir_only? %> - <%= _('Make a new <strong>Environmental Information</strong> request')%> - <% else %> - <%= _('Make a new <strong>Freedom of Information</strong> request to {{public_body}}', :public_body => h(@public_body.name))%> - <% end %> - <%= _('<a class="link_button_green" href="{{url}}">{{text}}</a>', :url=>new_request_to_body_url(:url_name => @public_body.url_name), :text=>_("Start"))%> + <%= link_to _("Make a request to this authority"), new_request_to_body_path(:url_name => @public_body.url_name), :class => "link_button_green" %> <% elsif @public_body.has_notes? %> - <%= @public_body.notes_as_html %> - <% elsif @public_body.not_requestable_reason == 'not_apply' %> - <%= _('Freedom of Information law does not apply to this authority, so you cannot make - a request to it.')%> - <% elsif @public_body.not_requestable_reason == 'defunct' %> - <%= _('This authority no longer exists, so you cannot make a request to it.')%> - <% else %> - <%= _('For an unknown reason, it is not possible to make a request to this authority.')%> + <%= @public_body.notes_as_html.html_safe %> <% end %> <% if @public_body.override_request_email %> @@ -78,12 +82,14 @@ <div id="foi_results_section"> <% if @public_body.info_requests.size == 0 %> - <% if @public_body.eir_only? %> - <h2><%= _('Environmental Information Regulations requests made using this site') %></h2> - <p>Nobody has made any Environmental Information Regulations requests to <%=h(@public_body.name)%> using this site yet.</p> - <% else %> - <h2><%= _('Freedom of Information requests made using this site')%></h2> - <p><%= _('Nobody has made any Freedom of Information requests to {{public_body_name}} using this site yet.', :public_body_name => h(@public_body.name))%></p> + <% if @public_body.is_requestable? or @public_body.not_requestable_reason != 'defunct' %> + <% if @public_body.eir_only? %> + <h2><%= _('Environmental Information Regulations requests made using this site') %></h2> + <p>Nobody has made any Environmental Information Regulations requests to <%=h(@public_body.name)%> using this site yet.</p> + <% else %> + <h2><%= _('Freedom of Information requests made using this site')%></h2> + <p><%= _('Nobody has made any Freedom of Information requests to {{public_body_name}} using this site yet.', :public_body_name => h(@public_body.name))%></p> + <% end %> <% end %> <% else %> <h2 class="foi_results"> @@ -91,15 +97,19 @@ <%= pluralize(@public_body.info_requests.size, "Environmental Information Regulations request made using this site") %> <% else %> <% if @public_body.info_requests.size > 4 %> - <%= n_('Search within the %d Freedom of Information requests to %s', 'Search within the %d Freedom of Information requests made to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %> + <%= n_('Search within the {{count}} Freedom of Information requests to {{public_body_name}}', 'Search within the {{count}} Freedom of Information requests made to {{public_body_name}}', @public_body.info_requests.size, :count => @public_body.info_requests.size, :public_body_name => @public_body.name) %> <% else %> - <%= n_('%d Freedom of Information request to %s', '%d Freedom of Information requests to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %> + <%= n_('{{count}} Freedom of Information request to {{public_body_name}}', + '{{count}} Freedom of Information requests to {{public_body_name}}', + @public_body.info_requests.size, + :count => @public_body.info_requests.size, + :public_body_name => @public_body.name) %> <% end %> <% end %> <%= @page_desc %> </h2> <a name="results"></a> - + <% if @public_body.info_requests.size > 4 %> <%= render :partial => 'request/request_filter_form' %> <% end %> @@ -120,7 +130,7 @@ <p> <%= _('Only requests made using {{site_name}} are shown.', :site_name => site_name) %></p> <% end %> - <% else %> + <% else %> <% if @public_body.eir_only? %> <h2><%= _('Environmental Information Regulations requests made') %></h2> <% else %> diff --git a/app/views/public_body/statistics.html.erb b/app/views/public_body/statistics.html.erb new file mode 100644 index 000000000..d935a9e47 --- /dev/null +++ b/app/views/public_body/statistics.html.erb @@ -0,0 +1,75 @@ +<% @title = _("Public Body Statistics") %> +<div id="main_content"> + <h1>Public Body Statistics</h1> + + <p><%= _("This page of public body statistics is currently \ +experimental, so there are some caveats that should be borne \ +in mind:") %></p> + + <ul> + + <li><%= _("The percentages are calculated with respect to \ +the total number of requests, which includes invalid \ +requests; this is a known problem that will be fixed in a \ +later release.") %></li> + + <li><%= _("The classification of requests (e.g. to say \ +whether they were successful or not) is done manually by users \ +and administrators of the site, which means that they are \ +subject to error.") %></li> + + <li><%= _("Requests are considered successful if they were \ +classified as either 'Successful' or 'Partially Successful'.") %></li> + + <li><%= _("Requests are considered overdue if they are in \ +the 'Overdue' or 'Very Overdue' states.") %></li> + + <li><%= _("The error bars shown are 95% confidence intervals \ +for the hypothesized underlying proportion (i.e. that which \ +you would obtain by making an infinite number of requests \ +through this site to that authority). In other words, the \ +population being sampled is all the current and future \ +requests to the authority through this site, rather than, \ +say, all requests that have been made to the public body by \ +any means.") %></li> + + </ul> + + <p><%= _("These graphs were partly inspired by \ +<a href=\"http://mark.goodge.co.uk/2011/08/number-crunching-whatdotheyknow/\">some \ +statistics that Mark Goodge produced for WhatDoTheyKnow</a>, so thanks \ +are due to him.") %></p> + + <% @graph_list.each do |graph_data| %> + <h3 class="public-body-ranking-title"><%= graph_data['title']%></h3> + <div class="public-body-ranking" id="<%= graph_data['id'] %>"> + <% if graph_data['x_values'] %> + <table border=0> + <thead> + <tr> + <th>Public Body</th> + <th><%= graph_data['y_axis'] %></th> + </tr> + </thead> + <tbody> + <% graph_data['public_bodies'].each_with_index do |pb, i| %> + <tr> + <td><%= link_to pb['name'], pb['url'] %></td> + <td class="statistic"><%= graph_data['y_values'][i].round %></td> + </tr> + <% end %> + </tbody> + </table> + <% else %> + <%= _("There was no data calculated for this graph yet.") %> + <% end %> + </div> + <% end %> + +<script type="text/javascript"> + var graphs_data = <%= @graph_list.to_json.html_safe %>; +</script> +<!--[if lte IE 8]><%= javascript_include_tag "excanvas.min.js" %><![endif]--> +<%= javascript_include_tag "stats" %> + +</div> diff --git a/app/views/public_body/view_email.rhtml b/app/views/public_body/view_email.html.erb index 79d7f7f4c..5f4bc95f4 100644 --- a/app/views/public_body/view_email.rhtml +++ b/app/views/public_body/view_email.html.erb @@ -1,4 +1,4 @@ -<% @title = "FOI email address for '" + h(@public_body.name) + "'" %> +<% @title = _("FOI email address for {{public_body}}", :public_body => h(@public_body.name)) %> <h1><%= _('FOI email address for {{public_body}}',:public_body=> public_body_link(@public_body))%></h1> @@ -25,19 +25,19 @@ <p> <% if @public_body.is_requestable? || @public_body.not_requestable_reason != 'bad_contact' %> - <%= raw _('If the address is wrong, or you know a better address, please <a href="%s">contact us</a>.')% [help_contact_path]%> + <%= raw(_('If the address is wrong, or you know a better address, please <a href="{{url}}">contact us</a>.', :url => help_contact_path.html_safe)) %> <% else %> - <%= raw _(' If you know the address to use, then please <a href="%s">send it to us</a>. - You may be able to find the address on their website, or by phoning them up and asking.')% [help_contact_path] %> + <%= raw(_(' If you know the address to use, then please <a href="{{url}}">send it to us</a>. + You may be able to find the address on their website, or by phoning them up and asking.', :url =>help_contact_path.html_safe)) %> <% end %> </p> <div id="stepwise_make_request_view_email"> <strong> <% if @public_body.eir_only? %> - <%= link_to "Make a new EIR request", new_request_to_body_url(:url_name => @public_body.url_name)%> + <%= link_to _("Make a new EIR request"), new_request_to_body_path(:url_name => @public_body.url_name)%> <% else %> - <%= link_to "Make a new FOI request", new_request_to_body_url(:url_name => @public_body.url_name)%> + <%= link_to _("Make a new FOI request"), new_request_to_body_path(:url_name => @public_body.url_name)%> <% end %> to <%= h(@public_body.name) %> </strong> diff --git a/app/views/public_body/view_email_captcha.rhtml b/app/views/public_body/view_email_captcha.html.erb index 6f301e055..4f6db5b67 100644 --- a/app/views/public_body/view_email_captcha.rhtml +++ b/app/views/public_body/view_email_captcha.html.erb @@ -4,7 +4,7 @@ <p><%= _('To view the email address that we use to send FOI requests to {{public_body_name}}, please enter these words.', :public_body_name => h(@public_body.name))%></p> -<% form_for :contact do |f| %> +<%= form_for :contact do |f| %> <%= recaptcha_tags %> <%= hidden_field_tag(:submitted_view_email, { :value => 1 } ) %> diff --git a/app/views/public_body_change_requests/new.html.erb b/app/views/public_body_change_requests/new.html.erb new file mode 100644 index 000000000..7079cd868 --- /dev/null +++ b/app/views/public_body_change_requests/new.html.erb @@ -0,0 +1,61 @@ +<h1><%= @title %></h1> +<%= foi_error_messages_for :change_request %> + <%= form_for(@change_request, :url => change_request_path) do |f| %> +<% if not @user %> + <p> + <label class="form_label" for="user_name"> + <%= _("Your name:") %> + </label> + <%= f.text_field :user_name %> + <%= _('(or <a href="{{url}}">sign in</a>)', :url => signin_path(:r => request.fullpath)) %> + </p> + + <p> + <label class="form_label" for="user_email"> + <%= ("Your email:") %> + </label> + <%= f.text_field :user_email %> + </p> +<% end %> +<% if @change_request.public_body %> + <%= f.hidden_field :public_body_id %> +<% else %> + <p> + <label class="form_label" for="public_body_name"> + <%= _("Authority:") %> + </label> + <%= f.text_field :public_body_name %> + </p> +<% end %> + <p> + <label class="form_label" for="public_body_email"> + <%= _("Authority email:") %> + </label> + <%= f.text_field :public_body_email %> + <div class="form_item_note"> + <%= _("The contact email address for FOI requests to the authority.") %> + </div> + </p> + + <p> + <label class="form_label" for="source_url"> + <%= _("Source URL:") %> + </label> + <%= f.text_field :source_url %> + <div class="form_item_note"> + <%= _("The URL where you found the email address. This field is optional, but it would help us a lot if you can provide a link to a specific page on the authority's website that gives this address, as it will make it much easier for us to check.") %> + </div> + </p> + + <p> + <label class="form_label" for="notes"> + <%= _("Notes:") %> + </label> + <%= f.text_area :notes, :rows => 10, :cols => 60 %> + </p> + + <div class="form_button"> + <%= submit_tag _("Submit request") %> + </div> + +<% end %> diff --git a/app/views/reports/new.html.erb b/app/views/reports/new.html.erb new file mode 100644 index 000000000..1197b2255 --- /dev/null +++ b/app/views/reports/new.html.erb @@ -0,0 +1,26 @@ +<h1>Report request: <%= @info_request.title %></h1> + +<% if @info_request.attention_requested %> + <p><%= _("This request has already been reported for administrator attention") %></p> +<% else %> + <p> + <%= _("Reporting a request notifies the site administrators. They will respond as soon as possible.") %> + </p> + <p><%= _("Why specifically do you consider this request unsuitable?") %></p> + + <%= form_tag request_report_path(:request_id => @info_request.url_title) do %> + <p> + <label class="form_label" for="reason">Reason:</label> + <%= select_tag :reason, options_for_select(@info_request.report_reasons, @reason), :prompt => _("Choose a reason") %> + </p> + <p> + <label class="form_label" for="message"><%= _("Please tell us more:") %></label> + <%= text_area_tag :message, @message, :rows => 10, :cols => 60 %> + </p> + + <div class="form_button"> + <%= submit_tag _("Report request") %> + </div> + + <% end %> +<% end %> diff --git a/app/views/request/_act.html.erb b/app/views/request/_act.html.erb new file mode 100644 index 000000000..1199cb4a2 --- /dev/null +++ b/app/views/request/_act.html.erb @@ -0,0 +1,15 @@ +<h2><%= _("Act on what you've learnt") %></h2> + +<div class="act_link"> + <% tweet_link = "https://twitter.com/share?" + {:url => request.url, :via => AlaveteliConfiguration::twitter_username, :text => "'#{@info_request.title}'", :related => _('alaveteli_foi:The software that runs {{site_name}}', :site_name => site_name)}.to_query %> + <% link_to tweet_link do %> + <%= image_tag "twitter-16.png", :alt => "twitter icon" %> + <% end %> + <%= link_to _("Tweet this request"), tweet_link %> +</div> +<div class="act_link"> + <%= link_to "http://wordpress.com/" do %> + <%= image_tag "wordpress.png", :class => "rss" %> + <% end %> + <%= link_to _("Start your own blog"), "http://wordpress.com/"%> +</div> diff --git a/app/views/request/_after_actions.rhtml b/app/views/request/_after_actions.html.erb index 3d74cf42d..f780e3a37 100644 --- a/app/views/request/_after_actions.rhtml +++ b/app/views/request/_after_actions.html.erb @@ -7,7 +7,7 @@ <ul> <% if @info_request.comments_allowed? %> <li> - <%= raw(_('<a href="%s">Add an annotation</a> (to help the requester or others)') % [new_comment_url(:url_title => @info_request.url_title)]) %> + <%= raw(_('<a href="{{url}}">Add an annotation</a> (to help the requester or others)', :url => new_comment_url(:url_title => @info_request.url_title).html_safe)) %> </li> <% end %> <% if @old_unclassified %> @@ -15,11 +15,9 @@ <%= link_to _('Update the status of this request'), '#describe_state_form_1' %> </li> <% end %> - <% if @info_request.all_can_view? %> <li> - <%= link_to _("Download a zip file of all correspondence"), download_entire_request_url(:url_title => @info_request.url_title) %> + <%= link_to _("Download a zip file of all correspondence"), download_entire_request_path(:url_title => @info_request.url_title) %> </li> - <% end %> </ul> </div> <% if ! @info_request.is_external? %> @@ -29,18 +27,18 @@ <li> <% if @last_response.nil? %> - <%= link_to _("Send a followup"), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil) + "#followup" %> + <%= link_to _("Send a followup"), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil) + "#followup" %> <% else %> - <%= link_to _("Write a reply"), show_response_url(:id => @info_request.id, :incoming_message_id => @last_response.id) + "#followup" %> + <%= link_to _("Write a reply"), show_response_path(:id => @info_request.id, :incoming_message_id => @last_response.id) + "#followup" %> <% end %> </li> <% if !@old_unclassified %> <li> - <%= link_to _("Update the status of this request"), request_url(@info_request, :update_status => 1) %> + <%= link_to _("Update the status of this request"), request_path(@info_request, :update_status => 1) %> </li> <% end %> <li> - <%= link_to _("Request an internal review"), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup" %> + <%= link_to _("Request an internal review"), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup" %> </li> </ul> </div> @@ -50,7 +48,7 @@ <strong><%= _('{{public_body_name}} only:',:public_body_name=>h(@info_request.public_body.name) ) %> </strong> <ul> <li> - <%= link_to _("Respond to request"), upload_response_url(:url_title => @info_request.url_title) %> + <%= link_to _("Respond to request"), upload_response_path(:url_title => @info_request.url_title) %> </li> </ul> </div> diff --git a/app/views/request/_bubble.rhtml b/app/views/request/_bubble.html.erb index 747e2aa1f..e038bb3dc 100644 --- a/app/views/request/_bubble.rhtml +++ b/app/views/request/_bubble.html.erb @@ -5,26 +5,26 @@ <% attachments.each do |a| %> <p class="attachment"> <% - attachment_url = get_attachment_url(:id => incoming_message.info_request_id, + attachment_path = get_attachment_path(:id => incoming_message.info_request_id, :incoming_message_id => incoming_message.id, :part => a.url_part_number, :file_name => a.display_filename) - attachment_as_html_url = get_attachment_as_html_url(:id => incoming_message.info_request_id, + attachment_as_html_path = get_attachment_as_html_path(:id => incoming_message.info_request_id, :incoming_message_id => incoming_message.id, :part => a.url_part_number, :file_name => a.display_filename + '.html') %> <% img_filename = "icon_" + a.content_type.sub('/', '_') + "_large.png" - full_filename = File.expand_path(File.join(File.dirname(__FILE__), "../../../public/images", img_filename)) + full_filename = File.expand_path(Rails.root.join('app', 'assets', 'images', img_filename)) if File.exist?(full_filename) %> - <a href="<%=attachment_url%>"><img class="attachment_image" alt="Attachment" src="/images/<%=img_filename%>"></a> + <%= link_to image_tag(img_filename, :class => "attachment_image", :alt => "Attachment"), attachment_path %> <% else %> - <a href="<%=attachment_url%>"><img class="attachment_image" alt="Attachment" src="/images/icon_unknown.png"></a> + <%= link_to image_tag("icon_unknown.png", :class => "attachment_image", :alt => "Attachment"), attachment_path %> <% end %> <strong><%= h a.display_filename %></strong> <br> <%= a.display_size %> - <%= link_to "Download", attachment_url %> + <%= link_to "Download", attachment_path %> <% if a.has_body_as_html? && incoming_message.info_request.all_can_view? %> - <%= link_to "View as HTML", attachment_as_html_url %> + <%= link_to "View as HTML", attachment_as_html_path %> <% end %> <!-- (<%= a.content_type %>) --> <%= a.extra_note %> diff --git a/app/views/request/_correspondence.html.erb b/app/views/request/_correspondence.html.erb new file mode 100644 index 000000000..c39625c3c --- /dev/null +++ b/app/views/request/_correspondence.html.erb @@ -0,0 +1,12 @@ +<div class="ff-print-fix"></div> +<% case info_request_event.event_type %> +<% when 'response' %> + <%= render :partial => 'request/incoming_correspondence', :locals => { :incoming_message => info_request_event.incoming_message } %> +<% when 'sent', 'followup_sent' %> + <%= render :partial => 'request/outgoing_correspondence', :locals => { :outgoing_message => info_request_event.outgoing_message, :info_request_event => info_request_event }%> +<% when 'resent', 'followup_resent' %> + <%= render :partial => 'request/resent_outgoing_correspondence', :locals => { :outgoing_message => info_request_event.outgoing_message, :info_request_event => info_request_event }%> +<% when 'comment' %> + <%= render :partial => 'comment/single_comment', :locals => { :comment => info_request_event.comment } %> +<% end %> + diff --git a/app/views/request/_correspondence.rhtml b/app/views/request/_correspondence.rhtml deleted file mode 100644 index 99c6c7d26..000000000 --- a/app/views/request/_correspondence.rhtml +++ /dev/null @@ -1,80 +0,0 @@ -<div class="ff-print-fix"></div> -<% -if !info_request_event.nil? && info_request_event.event_type == 'response' - incoming_message = info_request_event.incoming_message -end - -if not incoming_message.nil? -%> - <div class="incoming correspondence" id="incoming-<%=incoming_message.id.to_s%>"> - <h2> - <% if !incoming_message.safe_mail_from.nil? && incoming_message.safe_mail_from.strip != @info_request.public_body.name.strip %> - <%= _("From:") %> <%=h incoming_message.safe_mail_from %><br> - <% end %> - <% if incoming_message.safe_mail_from.nil? || (incoming_message.mail_from_domain == @info_request.public_body.request_email_domain) %> - <%=h @info_request.public_body.name %><br> - <% end %> - <br><%= simple_date(incoming_message.sent_at) %> - </h2> - - <%= render :partial => 'bubble', :locals => { :incoming_message => incoming_message, :body => incoming_message.get_body_for_html_display(@collapse_quotes), :attachments => incoming_message.get_attachments_for_display } %> - - <p class="event_actions"> - <% if !@user.nil? && @user.admin_page_links? %> - <%= link_to "Admin", admin_url("request/show_raw_email/" + incoming_message.raw_email_id.to_s) %> | - <% end %> - <%= link_to _("Link to this"), incoming_message_url(incoming_message), :class => "link_to_this" %> - </p> - </div> -<% -elsif [ 'sent', 'followup_sent' ].include?(info_request_event.event_type) - outgoing_message = info_request_event.outgoing_message - %> - <div class="outgoing correspondence" id="outgoing-<%=outgoing_message.id.to_s%>"> - - <h2> - <%= _("From:") %> <%=h @info_request.user_name %><br> - <br><%= simple_date(info_request_event.created_at) %> - </h2> - - <%= render :partial => 'bubble', :locals => { :body => outgoing_message.get_body_for_html_display(), :attachments => nil } %> - - <p class="event_actions"> - <% if outgoing_message.status == 'ready' && !@info_request.is_external? %> - <strong>Warning:</strong> This message has <strong>not yet been sent</strong> for an unknown reason. - <% end %> - - <!-- Can use this to get name of who followup was too, if say you - play with proper from display, but not sure needed - <% if outgoing_message.message_type == 'followup' && !outgoing_message.incoming_message_followup.nil? && !outgoing_message.incoming_message_followup.safe_mail_from.nil? %> - Follow up sent to: <%=h outgoing_message.incoming_message_followup.safe_mail_from %> - <% end %> - --> - - <%= link_to _("Link to this"), outgoing_message_url(outgoing_message), :class => "link_to_this" %> - </p> - </div> -<% elsif [ 'resent', 'followup_resent' ].include?(info_request_event.event_type) %> - <div class="outgoing correspondence" id="outgoing-<%=info_request_event.outgoing_message.id.to_s%>"> - <h2> - <%= simple_date(info_request_event.created_at) %> - </h2> - <p class="event_plain"> - Sent - <% if info_request_event.outgoing_message.message_type == 'initial_request' %> - request - <% elsif info_request_event.outgoing_message.message_type == 'followup' %> - a follow up - <% else %> - <% raise "unknown message_type" %> - <% end %> - - to <%= public_body_link(@info_request.public_body) %> again<% if not info_request_event.same_email_as_previous_send? %>, using a new contact address<% end %>. - </p> - </div> -<% elsif info_request_event.event_type == 'comment' - comment = info_request_event.comment -%> - <%= render :partial => 'comment/single_comment', :locals => { :comment => comment } %> -<% end %> - diff --git a/app/views/request/_describe_state.rhtml b/app/views/request/_describe_state.html.erb index 5b6004e81..7b6fa9683 100644 --- a/app/views/request/_describe_state.rhtml +++ b/app/views/request/_describe_state.html.erb @@ -1,9 +1,9 @@ <% if @is_owning_user %> - <% form_for(:incoming_message, @info_request, :url => describe_state_url(:id => @info_request.id)) do |f| %> + <%= form_for(@info_request, :as => :incoming_message, :url => describe_state_url(:id => @info_request.id), :html => {:id => "describe_form_#{id_suffix}"}) do |f| %> <h2><%= _('What best describes the status of this request now?') %></h2> - <hr> <!------------------------------------------------> + <hr> <h3><%= _('This request is still in progress:') %></h3> <% if @info_request.described_state != 'internal_review' %> <div> @@ -40,7 +40,7 @@ <%= render :partial => 'general/custom_state_transitions_pending', :locals => {:id_suffix => id_suffix } %> - <hr> <!------------------------------------------------> + <hr> <h3><%= _('This particular request is finished:') %></h3> <% if @info_request.described_state == 'internal_review' %> @@ -67,7 +67,7 @@ <%= render :partial => 'general/custom_state_transitions_complete', :locals => {:id_suffix => id_suffix } %> - <hr> <!------------------------------------------------> + <hr> <h3><%= _('Other:') %></h3> <div> @@ -80,7 +80,7 @@ <% if @update_status %> <div> <%= radio_button "incoming_message", "described_state", "requires_admin", :id => 'requires_admin' + id_suffix %> - <label for="error_message<%=id_suffix%>"> + <label for="requires_admin<%=id_suffix%>"> <%= _('This request <strong>requires administrator attention</strong>') %> </label> </div> @@ -97,18 +97,17 @@ <p> <%= hidden_field_tag 'last_info_request_event_id', @last_info_request_event_id, :id => 'last_info_request_event_id' + id_suffix %> - <%= hidden_field_tag 'submitted_describe_state', 1, :id => 'submitted_describe_state' + id_suffix %> <%= submit_tag _("Submit status") %> (<%= _('and we\'ll suggest <strong>what to do next</strong>') %>) </p> <% end %> <% elsif @old_unclassified %> - <%= render :partial => 'other_describe_state', :locals => {:id_suffix => id_suffix } %> + <%= render :partial => 'request/other_describe_state', :locals => {:id_suffix => id_suffix } %> <% else %> <% if !@info_request.is_external? %> <%= _('We don\'t know whether the most recent response to this request contains information or not – - if you are {{user_link}} please <a href="{{url}}">sign in</a> and let everyone know.',:user_link=>user_link(@info_request.user), :url=>signin_url(:r => request.request_uri)) %> + if you are {{user_link}} please <a href="{{url}}">sign in</a> and let everyone know.',:user_link=>user_link(@info_request.user), :url=>signin_url(:r => request.fullpath)) %> <% end %> <% end %> diff --git a/app/views/request/_followup.rhtml b/app/views/request/_followup.html.erb index 045bcd9ba..2643b767f 100644 --- a/app/views/request/_followup.rhtml +++ b/app/views/request/_followup.html.erb @@ -25,18 +25,18 @@ <ul> <% @info_request.who_can_followup_to(incoming_message).each do |name, email, id| %> <% if id.nil? && !incoming_message.nil? && incoming_message.valid_to_reply_to? %> - <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil)) %></li> + <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil)) %></li> <% else %> <% if !id.nil? %> <% if @info_request.public_body.request_email == email %> <% if !incoming_message.nil? %> - <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil)) %></li> + <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil)) %></li> <% end %> <% else %> - <li><%= link_to name, show_response_url(:id => @info_request.id, :incoming_message_id => id)%></li> + <li><%= link_to name, show_response_path(:id => @info_request.id, :incoming_message_id => id)%></li> <% end %> <% else %> - <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil)) %></li> + <li><%= link_to(_("the main FOI contact address for {{public_body}}", :public_body => name), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil)) %></li> <% end %> <% end %> @@ -45,13 +45,18 @@ </div> <% end %> <% if @info_request.allow_new_responses_from == 'nobody' %> - <p><%= _('Follow ups and new responses to this request have been stopped to prevent spam. Please <a href="{{url}}">contact us</a> if you are {{user_link}} and need to send a follow up.',:user_link=>user_link(@info_request.user), :url=>help_contact_path) %></p> + + <p><%= + # TRANSLATORS: "Follow ups" in this context means further + # messages sent by the requester to the authority after + # the initial request + _('Follow ups and new responses to this request have been stopped to prevent spam. Please <a href="{{url}}">contact us</a> if you are {{user_link}} and need to send a follow up.',:user_link=>user_link(@info_request.user), :url=>help_contact_path) %></p> <% else %> <% if @internal_review %> <p> - <%= raw(_('If you are dissatisfied by the response you got from + <%= _('If you are dissatisfied by the response you got from the public authority, you have the right to - complain (<a href="%s">details</a>).') % "http://foiwiki.com/foiwiki/index.php/Internal_reviews") %> + complain (<a href="{{url}}">details</a>).', :url => "http://foiwiki.com/foiwiki/index.php/Internal_reviews".html_safe) %> </p> <% end %> @@ -61,25 +66,24 @@ <% status = @info_request.calculate_status %> <% if status == 'waiting_response_overdue' %> - <p><%= _('The response to your request has been <strong>delayed</strong>. You can say that, + <p><%= _('The response to your request has been <strong>delayed</strong>. You can say that, by law, the authority should normally have responded <strong>promptly</strong> and') %> <% if @info_request.public_body.is_school? %> <%= _('in term time') %> <% end %> <%= _('by <strong>{{date}}</strong>',:date=>simple_date(@info_request.date_response_required_by)) %> - (<%= raw(_('<a href="%s">details</a>') % ["#{help_requesting_path}#quickly_response"]) %>). - + (<%= link_to _('details'), "#{help_requesting_path}#quickly_response" %>). </p> <% elsif status == 'waiting_response_very_overdue' %> <p> - <%= _('The response to your request is <strong>long overdue</strong>. You can say that, by + <%= _('The response to your request is <strong>long overdue</strong>. You can say that, by law, under all circumstances, the authority should have responded - by now') %> (<%= raw(_('<a href="%s">details</a>') % ["#{help_requesting_path}#quickly_response"]) %>). + by now') %> (<%= link_to _('details'), "#{help_requesting_path}#quickly_response" %>). </p> <% end %> - <% form_for(:outgoing_message, @outgoing_message, :html => { :id => 'followup_form' }, :url => incoming_message.nil? ? show_response_no_followup_url(:id => @info_request.id) : show_response_url(:id => @info_request.id, :incoming_message_id => incoming_message.id)) do |o| %> + <%= form_for(@outgoing_message, :html => { :id => 'followup_form' }, :url => incoming_message.nil? ? show_response_no_followup_url(:id => @info_request.id) : show_response_url(:id => @info_request.id, :incoming_message_id => incoming_message.id)) do |o| %> <p> <%= o.text_area :body, :rows => 15, :cols => 55 %> </p> @@ -103,7 +107,7 @@ <div> <%= radio_button "outgoing_message", "what_doing", "internal_review", :id => "internal_review" %> <label for="internal_review"><%= _('I am requesting an <strong>internal review</strong>') %> - <%= raw(_('<a href="%s">what\'s that?</a>') % ["/help/unhappy"]) %> + <%= link_to _("what's that?"), "/help/unhappy" %> </label> </div> <div> diff --git a/app/views/request/_hidden_correspondence.html.erb b/app/views/request/_hidden_correspondence.html.erb new file mode 100644 index 000000000..153164278 --- /dev/null +++ b/app/views/request/_hidden_correspondence.html.erb @@ -0,0 +1,13 @@ +<p id="hidden_message"> + <%- if !message.prominence_reason.blank? %> + <%= _('This message has been hidden.') %> + <%= message.prominence_reason %> + <%= _('Please <a href="{{url}}">contact us</a> if you have any questions.', :url => help_contact_path.html_safe) %> + <%- else %> + <%= _("This message has been hidden. There are various reasons why we might have done this, sorry we can't be more specific here.") %> + <%= _('Please <a href="{{url}}">contact us</a> if you have any questions.', :url => help_contact_path.html_safe) %> + <%- end %> + <% if message.prominence == 'requester_only' %> + <%= _('If you are the requester, then you may <a href="{{url}}">sign in</a> to view the message.', :url => signin_url(:r => request.fullpath).html_safe) %> + <% end %> +</p> diff --git a/app/views/request/_hidden_correspondence.rhtml b/app/views/request/_hidden_correspondence.rhtml deleted file mode 100644 index 0873b312f..000000000 --- a/app/views/request/_hidden_correspondence.rhtml +++ /dev/null @@ -1,33 +0,0 @@ -<% if info_request_event.prominence == 'requester_only' %> - <% - if !info_request_event.nil? && info_request_event.event_type == 'response' - incoming_message = info_request_event.incoming_message - end - if not incoming_message.nil? - %> - <div class="correspondence" id="incoming-<%=incoming_message.id.to_s%>"> - <p> - <%= raw(_('This response has been hidden. See annotations to find out why. - If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %> - </p> - </div> - <% elsif [ 'sent', 'followup_sent', 'resent', 'followup_resent' ].include?(info_request_event.event_type) %> - <div class="correspondence" id="outgoing-<%=outgoing_message.id.to_s%>"> - <p> - <%= raw(_('This outgoing message has been hidden. See annotations to - find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %> - </p> - </div> - <% elsif info_request_event.event_type == 'comment' %> - <div class="comment_in_request" id="comment-<%=comment.id.to_s%>"> - <p><%= raw(_('This comment has been hidden. See annotations to - find out why. If you are the requester, then you may <a href="%s">sign in</a> to view the response.') % [signin_url(:r => request.request_uri)]) %> - </p> - </div> - <% end %> - -<% elsif info_request_event.prominence == 'hidden' %> - <% # show nothing when hidden %> -<% else %> - <% raise _("unexpected prominence on request event") %> -<% end %> diff --git a/app/views/request/_hidden_correspondence.text.erb b/app/views/request/_hidden_correspondence.text.erb new file mode 100644 index 000000000..010b6b66d --- /dev/null +++ b/app/views/request/_hidden_correspondence.text.erb @@ -0,0 +1,5 @@ +<%- if !message.prominence_reason.blank? %> + <%= _('This message has been hidden.') %> <%= message.prominence_reason %> +<%- else %> + <%= _("This message has been hidden. There are various reasons why we might have done this, sorry we can't be more specific here.") %> +<%- end %> diff --git a/app/views/request/_incoming_correspondence.html.erb b/app/views/request/_incoming_correspondence.html.erb new file mode 100644 index 000000000..f39d650d8 --- /dev/null +++ b/app/views/request/_incoming_correspondence.html.erb @@ -0,0 +1,26 @@ +<div class="incoming correspondence <%= incoming_message.prominence %>" id="incoming-<%=incoming_message.id.to_s%>"> + <%- if not incoming_message.user_can_view?(@user) %> + <%= render :partial => 'request/hidden_correspondence', :locals => { :message => incoming_message }%> + <%- else %> + <%= render :partial => 'request/restricted_correspondence', :locals => {:message => incoming_message } %> + <h2> + <% if incoming_message.specific_from_name? %> + <%= _("From:") %> <%= incoming_message.safe_mail_from %><br> + <% end %> + <% if incoming_message.from_public_body? %> + <%= @info_request.public_body.name %><br> + <% end %> + <br><%= simple_date(incoming_message.sent_at) %> + </h2> + + <%= render :partial => 'bubble', :locals => { :incoming_message => incoming_message, :body => incoming_message.get_body_for_html_display(@collapse_quotes), :attachments => incoming_message.get_attachments_for_display } %> + + <p class="event_actions"> + <% if !@user.nil? && @user.admin_page_links? %> + <%= link_to "Admin", admin_incoming_edit_path(incoming_message.id) %> | + <% end %> + <%= link_to _("Link to this"), incoming_message_path(incoming_message), :class => "link_to_this" %> + </p> + <%- end %> +</div> + diff --git a/app/views/request/_incoming_correspondence.text.erb b/app/views/request/_incoming_correspondence.text.erb new file mode 100644 index 000000000..c5e648d28 --- /dev/null +++ b/app/views/request/_incoming_correspondence.text.erb @@ -0,0 +1,12 @@ +<%- if not incoming_message.user_can_view?(@user) %> + <%= render :partial => 'request/hidden_correspondence', :formats => 'text', :locals => { :message => incoming_message }%> +<%- else %> +<%= _('From:') %><% if incoming_message.specific_from_name? %> <%= incoming_message.safe_mail_from %><% end %><% if incoming_message.from_public_body? %>, <%= @info_request.public_body.name %><% end %> +<%= _('To:') %> <% if @info_request.user_name %><%= @info_request.user_name %><% else %><%= "[#{_('An anonymous user')}]"%><% end %> +<%= _('Date:') %> <%= simple_date(incoming_message.sent_at) %> + + <%= incoming_message.get_body_for_quoting %> + <% incoming_message.get_attachments_for_display.each do |a| %> +<%= _('Attachment:') %> <%= a.display_filename %> (<%= a.display_size %>) + <% end %> +<% end %> diff --git a/app/views/request/_list_results.html.erb b/app/views/request/_list_results.html.erb new file mode 100644 index 000000000..4da042816 --- /dev/null +++ b/app/views/request/_list_results.html.erb @@ -0,0 +1,12 @@ + <% @results = InfoRequest.request_list(@filters, @page, @per_page, @max_results) %> + <% if @results[:results].empty? %> + <p> <%= _('No requests of this sort yet.')%></p> + <% else %> + <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @results[:matches_estimated]) %></h2> + <div class="results_block"> + <% @results[:results].each do |result| %> + <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %> + <% end %> + </div> + <% end %> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @results[:show_no_more_than]) %> diff --git a/app/views/request/_next_actions.rhtml b/app/views/request/_next_actions.html.erb index f318df6e4..f318df6e4 100644 --- a/app/views/request/_next_actions.rhtml +++ b/app/views/request/_next_actions.html.erb diff --git a/app/views/request/_other_describe_state.rhtml b/app/views/request/_other_describe_state.html.erb index e274fe8c9..e49f9ecb3 100644 --- a/app/views/request/_other_describe_state.rhtml +++ b/app/views/request/_other_describe_state.html.erb @@ -1,6 +1,6 @@ -<% form_for(:incoming_message, @info_request, :url => describe_state_url(:id => @info_request.id)) do |f| %> +<%= form_for(@info_request, :as => :incoming_message, :url => describe_state_url(:id => @info_request.id), :html => {:id => "describe_form_#{id_suffix}"}) do |f| %> <h2><%= _('Hi! We need your help. The person who made the following request hasn\'t told us whether or not it was successful. Would you mind taking a moment to read it and help us keep the place tidy for everyone? @@ -75,7 +75,6 @@ <p> <%= hidden_field_tag 'last_info_request_event_id', @last_info_request_event_id, :id => 'last_info_request_event_id' + id_suffix %> - <%= hidden_field_tag 'submitted_describe_state', 1, :id => 'submitted_describe_state' + id_suffix %> <%= submit_tag "Submit status" %> </p> <% end %> diff --git a/app/views/request/_outgoing_correspondence.html.erb b/app/views/request/_outgoing_correspondence.html.erb new file mode 100644 index 000000000..dced5c94c --- /dev/null +++ b/app/views/request/_outgoing_correspondence.html.erb @@ -0,0 +1,18 @@ +<div class="outgoing correspondence" id="outgoing-<%=outgoing_message.id.to_s%>"> + <%- if not outgoing_message.user_can_view?(@user) %> + <%= render :partial => 'request/hidden_correspondence', :locals => { :message => outgoing_message }%> + <%- else %> + <%= render :partial => 'request/restricted_correspondence', :locals => {:message => outgoing_message } %> + <h2> + <%= _("From:") %> <%= @info_request.user_name %><br> + <br><%= simple_date(info_request_event.created_at) %> + </h2> + <%= render :partial => 'bubble', :locals => { :body => outgoing_message.get_body_for_html_display(), :attachments => nil } %> + <p class="event_actions"> + <% if outgoing_message.status == 'ready' && !@info_request.is_external? %> + <strong>Warning:</strong> This message has <strong>not yet been sent</strong> for an unknown reason. + <% end %> + <%= link_to _("Link to this"), outgoing_message_path(outgoing_message), :class => "link_to_this" %> + </p> + <%- end %> +</div> diff --git a/app/views/request/_outgoing_correspondence.text.erb b/app/views/request/_outgoing_correspondence.text.erb new file mode 100644 index 000000000..5375ef81b --- /dev/null +++ b/app/views/request/_outgoing_correspondence.text.erb @@ -0,0 +1,8 @@ +<%- if not outgoing_message.user_can_view?(@user) %> + <%= render :partial => 'request/hidden_correspondence', :formats => 'text', :locals => { :message => outgoing_message }%> +<%- else %> + <%= _('From:') %> <% if @info_request.user_name %><%= @info_request.user_name %><% else %><%= "[#{_('An anonymous user')}]"%><% end %> + <%= _('To:') %> <%= @info_request.public_body.name %> + <%= _('Date:') %> <%= simple_date(info_request_event.created_at) %> + <%= outgoing_message.get_body_for_text_display %> +<%- end %> diff --git a/app/views/request/_request_filter_form.rhtml b/app/views/request/_request_filter_form.html.erb index 0c215a9b6..090db01df 100644 --- a/app/views/request/_request_filter_form.rhtml +++ b/app/views/request/_request_filter_form.html.erb @@ -1,7 +1,7 @@ <%= render :partial => 'general/localised_datepicker' %> <div id="list-filter"> - <% form_tag(request.path, :method => "get", :id=>"filter_requests_form") do %> + <%= form_tag(request.path, :method => "get", :id=>"filter_requests_form") do %> <div class="list-filter-item"> <%= label_tag(:query, _("Keywords"), :class=>"form_label title") %> <%= text_field_tag(:query, params[:query]) %> diff --git a/app/views/request/_request_listing.rhtml b/app/views/request/_request_listing.html.erb index 492f874f3..492f874f3 100644 --- a/app/views/request/_request_listing.rhtml +++ b/app/views/request/_request_listing.html.erb diff --git a/app/views/request/_request_listing_short_via_event.rhtml b/app/views/request/_request_listing_short_via_event.html.erb index d93a91070..c2f6474a1 100644 --- a/app/views/request/_request_listing_short_via_event.rhtml +++ b/app/views/request/_request_listing_short_via_event.html.erb @@ -3,11 +3,11 @@ end %> <div class="request_short_listing"> - <h3><%= link_to highlight_words(info_request.title, @highlight_words), request_url(info_request) %></h3> + <h3><%= link_to highlight_words(info_request.title, @highlight_words), request_path(info_request) %></h3> <p> -<%= _('To {{public_body_link_absolute}}',:public_body_link_absolute => public_body_link_absolute(info_request.public_body))%> -<%= _('by {{user_link_absolute}}',:user_link_absolute => request_user_link_absolute(info_request))%> +<%= _('To {{public_body_link_absolute}}',:public_body_link_absolute => public_body_link(info_request.public_body))%> +<%= _('by {{user_link_absolute}}',:user_link_absolute => request_user_link(info_request))%> <%= simple_date(info_request.created_at) %> </p> </div> diff --git a/app/views/request/_request_listing_single.rhtml b/app/views/request/_request_listing_single.html.erb index e8c1a393f..50f889d75 100644 --- a/app/views/request/_request_listing_single.rhtml +++ b/app/views/request/_request_listing_single.html.erb @@ -1,9 +1,9 @@ <div class="request_listing"> - <span class="head"> - <%= link_to h(info_request.title), (@play_urls ? request_path(:url_title => info_request.url_title) : request_url(info_request)) %> - </span> - <span class="desc"> - <%= excerpt(info_request.initial_request_text, "", 150) %> + <span class="head"> + <%= link_to h(info_request.title), (@play_urls ? categorise_request_path(:url_title => info_request.url_title) : request_path(info_request)) %> + </span> + <span class="desc"> + <%= excerpt(info_request.initial_request_text, "", :radius => 150) %> </span> <span class="bottomline icon_<%= info_request.calculate_status %>"> <strong> diff --git a/app/views/request/_request_listing_via_event.rhtml b/app/views/request/_request_listing_via_event.html.erb index 2ba9613e5..cc8bae8a9 100644 --- a/app/views/request/_request_listing_via_event.rhtml +++ b/app/views/request/_request_listing_via_event.html.erb @@ -6,13 +6,13 @@ end %> <div class="request_left"> <span class="head"> <% if event.is_incoming_message? %> - <%= link_to highlight_words(info_request.title, @highlight_words), incoming_message_url(event.incoming_message_selective_columns("incoming_messages.id")) %> + <%= link_to highlight_words(info_request.title, @highlight_words), incoming_message_path(event.incoming_message_selective_columns("incoming_messages.id")) %> <% elsif event.is_outgoing_message? and event.event_type == 'followup_sent' %> - <%= link_to highlight_words(info_request.title, @highlight_words), outgoing_message_url(event.outgoing_message) %> + <%= link_to highlight_words(info_request.title, @highlight_words), outgoing_message_path(event.outgoing_message) %> <% elsif event.is_comment? %> - <%= link_to highlight_words(info_request.title, @highlight_words), comment_url(event.comment) %> + <%= link_to highlight_words(info_request.title, @highlight_words), comment_path(event.comment) %> <% else %> - <%= link_to highlight_words(info_request.title, @highlight_words), request_url(info_request) %> + <%= link_to highlight_words(info_request.title, @highlight_words), request_path(info_request) %> <% end %> </span> <div class="requester"> diff --git a/app/views/request/_resent_outgoing_correspondence.html.erb b/app/views/request/_resent_outgoing_correspondence.html.erb new file mode 100644 index 000000000..17b6b635b --- /dev/null +++ b/app/views/request/_resent_outgoing_correspondence.html.erb @@ -0,0 +1,16 @@ +<div class="outgoing correspondence" id="outgoing-<%=outgoing_message.id.to_s%>"> + <h2> + <%= simple_date(info_request_event.created_at) %> + </h2> + <p class="event_plain"> + Sent + <% if outgoing_message.message_type == 'initial_request' %> + request + <% elsif outgoing_message.message_type == 'followup' %> + a follow up + <% else %> + <% raise "unknown message_type" %> + <% end %> + to <%= public_body_link(@info_request.public_body) %> again<% if not info_request_event.same_email_as_previous_send? %>, using a new contact address<% end %>. + </p> +</div> diff --git a/app/views/request/_resent_outgoing_correspondence.text.erb b/app/views/request/_resent_outgoing_correspondence.text.erb new file mode 100644 index 000000000..d645e9488 --- /dev/null +++ b/app/views/request/_resent_outgoing_correspondence.text.erb @@ -0,0 +1,2 @@ +<%= _('Date:') %> <%= simple_date(info_request_event.created_at) %> +Sent <% if outgoing_message.message_type == 'initial_request' %> request <% elsif outgoing_message.message_type == 'followup' %> a follow up <% else %> <% raise "unknown message_type" %><% end %> to <%= public_body_link(@info_request.public_body) %> again<% if not info_request_event.same_email_as_previous_send? %>, using a new contact address<% end %>. diff --git a/app/views/request/_restricted_correspondence.html.erb b/app/views/request/_restricted_correspondence.html.erb new file mode 100644 index 000000000..745c4ff0e --- /dev/null +++ b/app/views/request/_restricted_correspondence.html.erb @@ -0,0 +1,18 @@ +<% if message.prominence == 'hidden' %> + <p id="hidden_message"> + <%- if !message.prominence_reason.blank? %> + <%= _('This message has prominence \'hidden\'. {{reason}} You can only see it because you are logged in as a super user.', :reason => message.prominence_reason) %> + <%- else %> + <%= _('This message has prominence \'hidden\'. You can only see it because you are logged in as a super user.') %> + <%- end %> + </p> +<% end %> +<% if message.prominence == 'requester_only' %> + <p id="hidden_message"> + <%- if !message.prominence_reason.blank? %> + <%= _('This message is hidden, so that only you, the requester, can see it. {{reason}}', :reason => message.prominence_reason) %> + <%- else %> + <%= _('This message is hidden, so that only you, the requester, can see it. Please <a href="{{url}}">contact us</a> if you are not sure why.', :url => help_requesting_path.html_safe) %> + <%- end %> + </p> +<% end %> diff --git a/app/views/request/_search_ahead.rhtml b/app/views/request/_search_ahead.html.erb index 1e65a5458..1e65a5458 100644 --- a/app/views/request/_search_ahead.rhtml +++ b/app/views/request/_search_ahead.html.erb diff --git a/app/views/request/_sidebar.html.erb b/app/views/request/_sidebar.html.erb new file mode 100644 index 000000000..0f7965ffa --- /dev/null +++ b/app/views/request/_sidebar.html.erb @@ -0,0 +1,58 @@ +<div id="right_column"> + <div id="follow_box"> + <h2><%= _('Follow this request') %></h2> + + <% follower_count = TrackThing.count(:all, :conditions => ["info_request_id = ?", @info_request.id]) + 1 %> + <p> + <%= n_("There is {{count}} person following this request", + "There are {{count}} people following this request", + follower_count, + :count => follower_count) %> + </p> + <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => @info_request.user && @info_request.user == @user, :location => 'sidebar' } %> + </div> + <% if @info_request.described_state != "attention_requested" %> + <h2><%= _('Offensive? Unsuitable?') %></h2> + <% if @info_request.attention_requested %> + <% if @info_request.prominence == 'hidden' %> + <%# The eccentric formatting of the following string is in order that it be identical + to the corresponding string in request/show.html.erb %> + <p><%= _('This request has prominence \'hidden\'. You can only see it because you are logged + in as a super user.') %></p> + <% elsif @info_request.prominence == 'requester_only' %> + <%# The eccentric formatting of the following string is in order that it be identical + to the corresponding string in request/show.html.erb %> + <p><%= _('This request is hidden, so that only you the requester can see it. Please + <a href="{{url}}">contact us</a> if you are not sure why.', :url => help_requesting_path.html_safe) %></p> + <% else %> + <p><%= _('This request has been marked for review by the site administrators, who have not hidden it at this time. If you believe it should be hidden, please <a href="{{url}}">contact us</a>.', :url => help_requesting_path.html_safe) %></p> + <% end %> + <% else %> + <p><%= _('Requests for personal information and vexatious requests are not considered valid for FOI purposes (<a href="/help/about">read more</a>).') %></p> + <p><%= _('If you believe this request is not suitable, you can report it for attention by the site administrators') %></p> + <%= link_to _("Report this request"), new_request_report_path(:request_id => @info_request.url_title) %> + <% end %> + <% end %> + <%= render :partial => 'request/act' %> + <%= render :partial => 'request/next_actions' %> + + <% cache_if_caching_fragments(@similar_cache_key, :expires_in => 1.day) do %> + <% xapian_similar, xapian_similar_more = @info_request.similar_requests %> + <% if !xapian_similar.nil? && xapian_similar.results.size > 0 %> + <h2><%= _('Similar requests')%></h2> + <% for result in xapian_similar.results %> + <%= render :partial => 'request/request_listing_short_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %> + <% end %> + <% if xapian_similar_more %> + <p><%= link_to _("More similar requests"), similar_request_path(@info_request.url_title) %></p> + <% end %> + <% end %> + <% end %> + + <p><%= link_to _('Event history details'), request_details_path(@info_request) %></p> + + <!-- this link with this wording is here for legal reasons, discuss with + board and our lawyer before changing or removing it --> + <p><small><%= link_to _('Are you the owner of any commercial copyright on this page?'), help_officers_path+"#copyright" %></small></p> + +</div> diff --git a/app/views/request/_sidebar.rhtml b/app/views/request/_sidebar.rhtml deleted file mode 100644 index 5e0c6fd2d..000000000 --- a/app/views/request/_sidebar.rhtml +++ /dev/null @@ -1,68 +0,0 @@ -<div id="right_column"> - <div id="follow_box"> - <h2><%= _('Follow this request') %></h2> - - <% follower_count = TrackThing.count(:all, :conditions => ["info_request_id = ?", @info_request.id]) + 1 %> - <p><%= n_("There is %d person following this request", "There are %d people following this request", follower_count) % follower_count %></p> - <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => @info_request.user == @user, :location => 'sidebar' } %> - </div> - <% if @info_request.described_state != "attention_requested" %> - <h2><%= _('Offensive? Unsuitable?') %></h2> - <% if @info_request.attention_requested %> - <% if @info_request.prominence == 'hidden' %> - <%# The eccentric formatting of the following string is in order that it be identical - to the corresponding string in request/show.rhtml %> - <p><%= _('This request has prominence \'hidden\'. You can only see it because you are logged - in as a super user.') %></p> - <% elsif @info_request.prominence == 'requester_only' %> - <%# The eccentric formatting of the following string is in order that it be identical - to the corresponding string in request/show.rhtml %> - <p><%= raw(_('This request is hidden, so that only you the requester can see it. Please - <a href="%s">contact us</a> if you are not sure why.') % [help_requesting_path]) %></p> - <% else %> - <p><%= raw(_('This request has been marked for review by the site administrators, who have not hidden it at this time. If you believe it should be hidden, please <a href="%s">contact us</a>.') % [help_requesting_path]) %></p> - <% end %> - <% else %> - <p><%= _('Requests for personal information and vexatious requests are not considered valid for FOI purposes (<a href="/help/about">read more</a>).') %></p> - <p><%= _('If you believe this request is not suitable, you can report it for attention by the site administrators') %></p> - <%= button_to _("Report this request"), report_path, :class => "link_button_green" %> - <% end %> - <% end %> - <h2><%= _("Act on what you've learnt") %></h2> - - <div class="act_link"> - <% tweet_link = "https://twitter.com/share?url=#{h(request.url)}&via=#{h(Configuration::twitter_username)}&text='#{h(@info_request.title)}'&related=#{_('alaveteli_foi:The software that runs {{site_name}}', :site_name => h(site_name))}" %> - <% link_to tweet_link do %> - <%= image_tag "twitter-16.png", :alt => "twitter icon" %> - <% end %> - <%= link_to _("Tweet this request"), tweet_link %> - </div> - <div class="act_link"> - <% link_to "http://wordpress.com/" do %> - <%= image_tag "wordpress.png", :class => "rss" %> - <% end %> - <%= link_to _("Start your own blog"), "http://wordpress.com/"%> - </div> - - <%= render :partial => 'request/next_actions' %> - - <% # TODO: Cache for 1 day %> - <% if !@xapian_similar.nil? && @xapian_similar.results.size > 0 %> - <h2><%= _('Similar requests')%></h2> - <% for result in @xapian_similar.results %> - <%= render :partial => 'request/request_listing_short_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %> - <% end %> - <% if @xapian_similar_more %> - <p><%= link_to _("More similar requests"), request_similar_url(@info_request) %></p> - <% end %> - <!-- Important terms: <%= @xapian_similar.important_terms.join(" ") %> --> - <% end %> - - <p><%= link_to _('Event history details'), request_details_url(@info_request) %></p> - - <!-- this link with this wording is here for legal reasons, discuss with - board and our lawyer before changing or removing it --> - <p><small><%= raw(_('<a href="%s">Are you the owner of - any commercial copyright on this page?</a>') % [help_officers_path+"#copyright"]) %></small></p> - -</div> diff --git a/app/views/request/_sidebar_request_listing.rhtml b/app/views/request/_sidebar_request_listing.html.erb index ec5a5813d..ec5a5813d 100644 --- a/app/views/request/_sidebar_request_listing.rhtml +++ b/app/views/request/_sidebar_request_listing.html.erb diff --git a/app/views/request/_summary_suggestion.rhtml b/app/views/request/_summary_suggestion.html.erb index a5da09cda..a5da09cda 100644 --- a/app/views/request/_summary_suggestion.rhtml +++ b/app/views/request/_summary_suggestion.html.erb diff --git a/app/views/request/_view_html_prefix.rhtml b/app/views/request/_view_html_prefix.html.erb index b29830ac7..63fac7c6d 100644 --- a/app/views/request/_view_html_prefix.rhtml +++ b/app/views/request/_view_html_prefix.html.erb @@ -1,12 +1,12 @@ <div class="view_html_prefix"> <div class="view_html_logo"> - <a href="/"><img src="/images/navimg/logo-trans-small.png" alt="<%= site_name %>"></a> + <a href="/"><img src="/assets/navimg/logo-trans-small.png" alt="<%= site_name %>"></a> </div> <div class="view_html_download_link"> - <%=link_to _("Download original attachment"), @attachment_url %> + <%=link_to _("Download original attachment"), @attachment_url %> <br>(<%=h @attachment.name_of_content_type %>) </div> <%= _('This is an HTML version of an attachment to the Freedom of Information request')%> - '<%=link_to h(@info_request.title), incoming_message_url(@incoming_message)%>'. + '<%=link_to h(@info_request.title), incoming_message_path(@incoming_message)%>'. </div> diff --git a/app/views/request/_view_html_stylesheet.html.erb b/app/views/request/_view_html_stylesheet.html.erb new file mode 100644 index 000000000..125ce66ec --- /dev/null +++ b/app/views/request/_view_html_stylesheet.html.erb @@ -0,0 +1 @@ +<link type="text/css" title="Main" rel="stylesheet" media="screen" href="/assets/application.css"> diff --git a/app/views/request/_view_html_stylesheet.rhtml b/app/views/request/_view_html_stylesheet.rhtml deleted file mode 100644 index d6cb932a8..000000000 --- a/app/views/request/_view_html_stylesheet.rhtml +++ /dev/null @@ -1 +0,0 @@ -<%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet" %> diff --git a/app/views/request/_wall_listing.rhtml b/app/views/request/_wall_listing.html.erb index 26d34e1a1..b6b4b38b1 100644 --- a/app/views/request/_wall_listing.rhtml +++ b/app/views/request/_wall_listing.html.erb @@ -6,11 +6,11 @@ end %> <div class="request_left"> <div class="requester"> <% if event.event_type == 'sent' %> - <%= _('A new request, <em><a href="{{request_url}}">{{request_title}}</a></em>, was sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>request_url(info_request),:request_title=>info_request.title) %> + <%= _('A new request, <em><a href="{{request_url}}">{{request_title}}</a></em>, was sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>request_path(info_request),:request_title=>info_request.title) %> <% elsif event.event_type == 'followup_sent' %> - <%= _('A <a href="{{request_url}}">follow up</a> to <em>{{request_title}}</em> was sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>outgoing_message_url(event.outgoing_message),:request_title=>info_request.title) %> + <%= _('A <a href="{{request_url}}">follow up</a> to <em>{{request_title}}</em> was sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>outgoing_message_path(event.outgoing_message),:request_title=>info_request.title) %> <% elsif event.event_type == 'response' %> - <%= _('A <a href="{{request_url}}">response</a> to <em>{{request_title}}</em> was sent by {{public_body_name}} to {{info_request_user}} on {{date}}. The request status is: {{request_status}}',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>incoming_message_url(event.incoming_message_selective_columns("incoming_messages.id")),:request_title=>info_request.title,:request_status=>info_request.display_status) %> + <%= _('A <a href="{{request_url}}">response</a> to <em>{{request_title}}</em> was sent by {{public_body_name}} to {{info_request_user}} on {{date}}. The request status is: {{request_status}}',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at),:request_url=>incoming_message_path(event.incoming_message_selective_columns("incoming_messages.id")),:request_title=>info_request.title,:request_status=>info_request.display_status) %> <% elsif event.event_type == 'comment' %> <%= _('An <a href="{{request_url}}">annotation</a> to <em>{{request_title}}</em> was made by {{event_comment_user}} on {{date}}',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:event_comment_user=>user_link_absolute(event.comment.user),:date=>simple_date(event.created_at),:request_url=>comment_url(event.comment),:request_title=>info_request.title) %> <% else %> diff --git a/app/views/request/batch_not_allowed.html.erb b/app/views/request/batch_not_allowed.html.erb new file mode 100644 index 000000000..156fa9ae1 --- /dev/null +++ b/app/views/request/batch_not_allowed.html.erb @@ -0,0 +1 @@ +<%= _('Users cannot usually make batch requests to multiple authorities at once because we don’t want public authorities to be bombarded with large numbers of inappropriate requests. Please <a href="{{url}}">contact us</a> if you think you have good reason to send the same request to multiple authorities at once.', :url => help_contact_path.html_safe) %> diff --git a/app/views/request/describe_state_message.html.erb b/app/views/request/describe_state_message.html.erb new file mode 100644 index 000000000..73237759f --- /dev/null +++ b/app/views/request/describe_state_message.html.erb @@ -0,0 +1,30 @@ +<h1><%= @title %></h1> + + +<p> + <% if @described_state == "error_message" %> + <%= _("If the error was a delivery failure, and you can find an up to date FOI email address for the authority, please tell us using the form below.") %> + <% else %> + <%= _("Just one more thing") %> + <% end %> +</p> + +<%= form_for :incoming_message, :url => describe_state_url(:id => @info_request.id) do |f| %> + + <p> + <label class="form_label" for="incoming_message_message">Please tell us more:</label> + <%= f.text_area :message, :rows => 10, :cols => 60 %> + </p> + + <div> + <%= hidden_field_tag "incoming_message[described_state]", @described_state %> + <%= hidden_field_tag :last_info_request_event_id, @last_info_request_event_id %> + </div> + + <div class="form_button"> + <%= submit_tag _("Submit status and send message") %> + </div> + +<% end %> + + diff --git a/app/views/request/details.rhtml b/app/views/request/details.html.erb index d4c63902f..3cb2f5afe 100644 --- a/app/views/request/details.rhtml +++ b/app/views/request/details.html.erb @@ -36,10 +36,10 @@ way authorities use it. Plus you\'ll need to be an elite statistician. Please <% end %> <td> <% if info_request_event.outgoing_message %> - <%= link_to "outgoing", outgoing_message_url(info_request_event.outgoing_message) %> + <%= link_to "outgoing", outgoing_message_path(info_request_event.outgoing_message) %> <% end %> <% if info_request_event.incoming_message %> - <%= link_to "incoming", incoming_message_url(info_request_event.incoming_message) %> + <%= link_to "incoming", incoming_message_path(info_request_event.incoming_message) %> <% end %> </td> </tr> diff --git a/app/views/request/followup_bad.rhtml b/app/views/request/followup_bad.html.erb index c892263e6..ea2400c5d 100644 --- a/app/views/request/followup_bad.rhtml +++ b/app/views/request/followup_bad.html.erb @@ -9,21 +9,21 @@ <% if @reason == 'not_apply' %> <!-- we should never get here, but just in case give a sensible message --> <p><%= _('Freedom of Information law no longer applies to') %> <%=h @info_request.public_body.name %>. - <%= raw(_('From the request page, try replying to a particular message, rather than sending + <%= _('From the request page, try replying to a particular message, rather than sending a general followup. If you need to make a general followup, and know - an email which will go to the right place, please <a href="%s">send it to us</a>.') % [help_contact_path]) %> + an email which will go to the right place, please <a href="{{url}}">send it to us</a>.', :url => help_contact_path.html_safe) %> </p> <% elsif @reason == 'defunct' %> <!-- we should never get here, but just in case give a sensible message --> - <p><%=h @info_request.public_body.name %> <%= raw(_('no longer exists. If you are trying to make + <p><%=h @info_request.public_body.name %> <%= _('no longer exists. If you are trying to make From the request page, try replying to a particular message, rather than sending a general followup. If you need to make a general followup, and know - an email which will go to the right place, please <a href="%s">send it to us</a>.') % [help_contact_path]) %> + an email which will go to the right place, please <a href="{{url}}">send it to us</a>.', :url => help_contact_path.html_safe) %> </p> <% elsif @reason == 'bad_contact' %> - <p><%= _('We do not have a working {{law_used_full}} address for {{public_body_name}}.',:law_used_full=>h(@info_request.law_used_full),:public_body_name=>h(@info_request.public_body.name)) %> <%= raw(_('You may be able to find + <p><%= _('We do not have a working {{law_used_full}} address for {{public_body_name}}.',:law_used_full=>h(@info_request.law_used_full),:public_body_name=>h(@info_request.public_body.name)) %> <%= _('You may be able to find one on their website, or by phoning them up and asking. If you manage - to find one, then please <a href="%s">send it to us</a>.') % [help_contact_path]) %> + to find one, then please <a href="{{url}}">send it to us</a>.', :url => help_contact_path.html_safe) %> </p> <% elsif @reason == 'external' %> <p><%= _("Followups cannot be sent for this request, as it was made externally, and published here by {{public_body_name}} on the requester's behalf.", :public_body_name => h(@info_request.public_body.name)) %> diff --git a/app/views/request/followup_preview.rhtml b/app/views/request/followup_preview.html.erb index 50c64138f..55afc0245 100644 --- a/app/views/request/followup_preview.rhtml +++ b/app/views/request/followup_preview.html.erb @@ -2,7 +2,7 @@ <div id="followup"> -<% form_for(:outgoing_message, @outgoing_message, :html => { :id => 'preview_form' }, :url => (@incoming_message.nil? ? show_response_no_followup_url(:id => @info_request.id) : show_response_url(:id => @info_request.id, :incoming_message_id => @incoming_message.id)) + "#followup" ) do |o| %> +<%= form_for(@outgoing_message, :html => { :id => 'preview_form' }, :url => (@incoming_message.nil? ? show_response_no_followup_url(:id => @info_request.id) : show_response_url(:id => @info_request.id, :incoming_message_id => @incoming_message.id)) + "#followup" ) do |o| %> <% if @internal_review %> <h1><%= _('Now preview your message asking for an internal review') %></h1> @@ -15,7 +15,7 @@ <li><%= _('Your message will appear in <strong>search engines</strong>') %></li> </ul> - <% fields_for :outgoing_message do |o| %> + <%= fields_for :outgoing_message do |o| %> <div class="correspondence" id="outgoing-0"> <p class="preview_subject"> diff --git a/app/views/request/hidden.html.erb b/app/views/request/hidden.html.erb new file mode 100644 index 000000000..f2f76a817 --- /dev/null +++ b/app/views/request/hidden.html.erb @@ -0,0 +1,19 @@ +<% @title = _("Request has been removed") %> + +<h1><%=@title%></h1> + +<p> +<%=@details%> +</p> + +<p><%= _('The request you have tried to view has been removed. There are +various reasons why we might have done this, sorry we can\'t be more specific here. Please <a + href="{{url}}">contact us</a> if you have any questions.', :url => help_contact_path.html_safe) %> +</p> +<% if @info_request.prominence == 'requester_only' %> + <p> + <%= _('If you are the requester, then you may <a href="{{url}}">sign in</a> to view the request.', :url => signin_url(:r => request.fullpath).html_safe) %> + </p> +<% end %> + + diff --git a/app/views/request/hidden.rhtml b/app/views/request/hidden.rhtml deleted file mode 100644 index 2d038a663..000000000 --- a/app/views/request/hidden.rhtml +++ /dev/null @@ -1,19 +0,0 @@ -<% @title = _("Request has been removed") %> - -<h1><%=@title%></h1> - -<p> -<%=@details%> -</p> - -<p><%= raw(_('The request you have tried to view has been removed. There are -various reasons why we might have done this, sorry we can\'t be more specific here. Please <a - href="%s">contact us</a> if you have any questions.') % [help_contact_path]) %> -</p> -<% if @info_request.prominence == 'requester_only' %> - <p> - <%= raw(_('If you are the requester, then you may <a href="%s">sign in</a> to view the request.') % [signin_url(:r => request.request_uri)]) %> - </p> -<% end %> - - diff --git a/app/views/request/hidden_correspondence.html.erb b/app/views/request/hidden_correspondence.html.erb new file mode 100644 index 000000000..46bf3ee37 --- /dev/null +++ b/app/views/request/hidden_correspondence.html.erb @@ -0,0 +1,4 @@ +<% @title = _("Message has been removed") %> + +<h1><%=@title%></h1> +<%= render :partial => 'request/hidden_correspondence', :locals => { :message => @incoming_message } %> diff --git a/app/views/request/list.html.erb b/app/views/request/list.html.erb new file mode 100644 index 000000000..a465f03ba --- /dev/null +++ b/app/views/request/list.html.erb @@ -0,0 +1,24 @@ + +<div id="header_left"> + <h1><%=@title%></h1> + <%= render :partial => 'request/request_filter_form' %> +</div> + +<div id="header_right"> + <h2><%= _("Follow these requests") %></h2> + <% if @track_thing %> + <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'main' } %> + <% end %> +</div> + +<div style="clear:both"></div> + +<div class="results_section"> + <% if key = request_list_cache_key %> + <% cache_if_caching_fragments(key, :expires_in => 5.minutes) do %> + <%= render :partial => 'list_results' %> + <% end %> + <% else %> + <%= render :partial => 'list_results' %> + <% end %> +</div> diff --git a/app/views/request/list.rhtml b/app/views/request/list.rhtml deleted file mode 100644 index 062b77c3e..000000000 --- a/app/views/request/list.rhtml +++ /dev/null @@ -1,34 +0,0 @@ - -<div id="header_left"> - <h1><%=@title%></h1> - <%= render :partial => 'request/request_filter_form' %> -</div> - -<div id="header_right"> - <h2><%= _("Follow these requests") %></h2> - <% if @track_thing %> - <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'main' } %> - <% end %> -</div> - -<div style="clear:both"></div> - -<div class="results_section"> - <% # TODO: Cache for 5 minutes %> - <% if @list_results.empty? %> - <p> <%= _('No requests of this sort yet.')%></p> - <% else %> - <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @matches_estimated) %></h2> - <div class="results_block"> - <% for result in @list_results%> - <% if result.class.to_s == 'InfoRequestEvent' %> - <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %> - <% else %> - <p><strong><%= _('Unexpected search result type') %> <%=result.class.to_s%></strong></p> - <% end %> - <% end %> - </div> - <% end %> - - <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @show_no_more_than) %> -</div> diff --git a/app/views/request/new.html.erb b/app/views/request/new.html.erb new file mode 100644 index 000000000..7f1332464 --- /dev/null +++ b/app/views/request/new.html.erb @@ -0,0 +1,194 @@ +<% unless @batch %> + <script type="text/javascript"> + $(document).ready(function(){ + // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin: + // http://benalman.com/projects/jquery-throttle-debounce-plugin/ + $("#typeahead_search").keypress($.debounce( 300, function() { + $("#typeahead_response").load("<%=search_ahead_url%>?q="+encodeURI(this.value), function() { + // When following links in typeahead results, open new tab/window + $("#typeahead_response a").attr("target","_blank"); + + // Update the public body site search link + $("#body-site-search-link").attr("href", "http://www.google.com/#q="+encodeURI($("#typeahead_search").val())+ + "+site:<%= @info_request.public_body.calculated_home_page %>"); + }); + })); + + }); + </script> +<% end %> +<% if @batch %> + <% @title = _("Make an {{law_used_short}} request", :law_used_short=>h(@info_request.law_used_short)) %> +<% else %> + <% @title = _("Make an {{law_used_short}} request to '{{public_body_name}}'",:law_used_short=>h(@info_request.law_used_short),:public_body_name=>h(@info_request.public_body.name)) %> +<% end %> + <h1><%= _('2. Ask for Information') %></h1> + + <% if @existing_request %> + <div class="errorExplanation" id="errorExplanation"><ul> + <li> + <%= _('{{existing_request_user}} already + created the same request on {{date}}. You can either view the <a href="{{existing_request}}">existing request</a>, + or edit the details below to make a new but similar request.',:existing_request_user=>user_or_you_capital_link(@existing_request.user), :date=>simple_date(@existing_request.created_at), :existing_request=>request_path(@existing_request)) %> + </li> + </ul></div> + <% end %> + <% if @existing_batch %> + <div class="errorExplanation" id="errorExplanation"><ul> + <li> + <%= _('You already created the same batch of requests on {{date}}. You can either view the <a href="{{existing_batch}}">existing batch</a>, or edit the details below to make a new but similar batch of requests.', :date=>simple_date(@existing_batch.created_at), :existing_batch=>info_request_batch_path(@existing_batch)) %> + </li> + </ul></div> + <% end %> + + <%= foi_error_messages_for :info_request, :outgoing_message %> + + <%= form_for(@info_request, :url => (@batch ? new_batch_path : new_request_path), :html => { :id => 'write_form' } ) do |f| %> + + <div id="request_header"> + <div id="request_header_body"> + <label class="form_label"><%= _('To:') %></label> + + <% if @batch %> + <span id="to_public_body"> + <%= _("Your selected authorities") %> + <span class="batch_public_body_toggle" data-hidetext="<%= _("(hide)") %>" data-showtext="<%= _("(show)") %>"><a class="toggle-message"></a></span> + </span> + + <div class="batch_public_body_list"> + <ul> + <% @public_bodies.each do |public_body| %> + <li><%= public_body.name %></li> + <% end %> + </ul> + </div> + + <% else %> + <span id="to_public_body"> + <%=h(@info_request.public_body.name)%> + </span> + <% end %> + + <% unless @batch %> + <div class="form_item_note"> + <% if @info_request.public_body.info_requests.size > 0 %> + <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_path(@info_request.public_body)) %> + <% else %> + <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_url) %> + <% end %> + </div> + + <% if @info_request.public_body.has_notes? %> + <div id="request_header_text"> + <h3><%= _('Special note for this authority!') %></h3> + <p><%= @info_request.public_body.notes_as_html.html_safe %></p> + </div> + <% end %> + + <% if @info_request.public_body.eir_only? %> + <h3><%= _('Please ask for environmental information only') %></h3> + + <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>. + <%= _('However, you have the right to request environmental + information under a different law') %> (<a href="/help/requesting#eir">explanation</a>). + <%= _('This covers a very wide spectrum of information about the state of + the <strong>natural and built environment</strong>, such as:') %> + + <ul> + <li><%= _('Air, water, soil, land, flora and fauna (including how these effect + human beings)') %></li> + <li><%= _('Information on emissions and discharges (e.g. noise, energy, + radiation, waste materials)') %></li> + <li><%= _('Human health and safety') %></li> + <li><%= _('Cultural sites and built structures (as they may be affected by the + environmental factors listed above)') %></li> + <li><%= _('Plans and administrative measures that affect these matters') %></li> + </ul> + + <p><%= _('Please only request information that comes under those categories, <strong>do not waste your + time</strong> or the time of the public authority by requesting unrelated information.') %></p> + <% end %> + <% end %> + </div> + + <div id="request_header_subject"> + <p> + <label class="form_label" for="typeahead_search"><%= _('Summary:') %></label> + <%= f.text_field :title, :size => 50, :id =>"typeahead_search" %> + </p> + <div class="form_item_note"> + (<%= _("a one line summary of the information you are requesting, \n\t\t\te.g.") %> + <%= render :partial => "summary_suggestion" %>) + </div> + </div> + + <div id="typeahead_response"> + </div> + </div> + + <div id="request_advice"> + <ul> + <li><%= _('Write your request in <strong>simple, precise language</strong>.') %></li> + <li><%= _('Ask for <strong>specific</strong> documents or information, this site is not suitable for general enquiries.') %></li> + <li><%= _('Keep it <strong>focused</strong>, you\'ll be more likely to get what you want (<a href="{{url}}">why?</a>).', :url => (help_requesting_path + '#focused').html_safe) %></li> + </ul> + </div> + + <div id="request_form"> + <%= fields_for :outgoing_message do |o| %> + <p> + <label class="form_label" for="outgoing_message_body"><%= _('Your request:') %></label> + <%= o.text_area :body, :rows => 20, :cols => 60 %> + </p> + <% end %> + + <% if !@user %> + <p class="form_note"> + + <%= raw(_('Everything that you enter on this page, including <strong>your name</strong>, + will be <strong>displayed publicly</strong> on + this website forever (<a href="{{url}}">why?</a>).', :url => (help_privacy_path+"#public_request").html_safe)) %> + <%= raw(_('If you are thinking of using a pseudonym, + please <a href="{{url}}">read this first</a>.', :url => (help_privacy_path+"#real_name").html_safe)) %> + </p> + <% else %> + <p class="form_note"> + <%= raw(_('Everything that you enter on this page + will be <strong>displayed publicly</strong> on + this website forever (<a href="{{url}}">why?</a>).', :url => (help_privacy_path+"#public_request").html_safe)) %> + </p> + <% end %> + + <p class="form_note"> + <%= raw(_("<strong> Can I request information about myself?</strong>\n" + + "\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>", :url => (help_requesting_path+"#data_protection").html_safe)) %> + </p> + + <div class="form_button"> + <% if @batch %> + <% params[:public_body_ids].each do |public_body_id| %> + <%= hidden_field_tag("public_body_ids[]", public_body_id)%> + <% end %> + <% else %> + <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% end %> + <%= hidden_field_tag(:submitted_new_request, 1 ) %> + <%= hidden_field_tag(:preview, 1 ) %> + <%= submit_tag _("Preview your public request") %> + </div> + + <% if !@info_request.tag_string.empty? %> + <p class="form_note"> + <!-- <label class="form_label" for="info_request_tag_string"><%= _("Tags (separated by a space):") %></label> + <%= f.text_field :tag_string, :size => 50 %> --> + + <%= f.hidden_field(:tag_string) %> + <strong>Tags:</strong> <%=h @info_request.tag_string %> + </p> + <% end %> + + </div> +<% end %> +<% if @batch %> + <%= javascript_include_tag 'new-request.js' %> +<% end %> diff --git a/app/views/request/new.rhtml b/app/views/request/new.rhtml deleted file mode 100644 index f396ea9ec..000000000 --- a/app/views/request/new.rhtml +++ /dev/null @@ -1,154 +0,0 @@ -<script type="text/javascript" src="/javascripts/ba-throttle-debounce.js"></script> -<script type="text/javascript"> - $(document).ready(function(){ - // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin: - // http://benalman.com/projects/jquery-throttle-debounce-plugin/ - $("#typeahead_search").keypress($.debounce( 300, function() { - $("#typeahead_response").load("<%=search_ahead_url%>?q="+encodeURI(this.value), function() { - // When following links in typeahead results, open new tab/window - $("#typeahead_response a").attr("target","_blank"); - - // Update the public body site search link - $("#body-site-search-link").attr("href", "http://www.google.com/#q="+encodeURI($("#typeahead_search").val())+ - "+site:<%= @info_request.public_body.calculated_home_page %>"); - }); - })); - - }); -</script> - -<% @title = _("Make an {{law_used_short}} request to '{{public_body_name}}'",:law_used_short=>h(@info_request.law_used_short),:public_body_name=>h(@info_request.public_body.name)) %> - - <h1><%= _('2. Ask for Information') %></h1> - - <% if @existing_request %> - <div class="errorExplanation" id="errorExplanation"><ul> - <li> - <%= _('{{existing_request_user}} already - created the same request on {{date}}. You can either view the <a href="{{existing_request}}">existing request</a>, - or edit the details below to make a new but similar request.',:existing_request_user=>user_or_you_capital_link(@existing_request.user), :date=>simple_date(@existing_request.created_at), :existing_request=>request_url(@existing_request)) %> - </li> - </ul></div> - <% end %> - - <%= foi_error_messages_for :info_request, :outgoing_message %> - - <% form_for(:info_request, @info_request, :html => { :id => 'write_form' } ) do |f| %> - - <div id="request_header"> - <div id="request_header_body"> - <label class="form_label" for="info_request_public_body_id"><%= _('To:') %></label> - <span id="to_public_body"><%=h(@info_request.public_body.name)%></span> - <div class="form_item_note"> - <% if @info_request.public_body.info_requests.size > 0 %> - <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_url(@info_request.public_body)) %> - <% else %> - <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_url) %> - <% end %> - </div> - - <% if @info_request.public_body.has_notes? %> - <div id="request_header_text"> - <h3><%= _('Special note for this authority!') %></h3> - <p><%= @info_request.public_body.notes_as_html %></p> - </div> - <% end %> - - <% if @info_request.public_body.eir_only? %> - <h3><%= _('Please ask for environmental information only') %></h3> - - <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>. - <%= _('However, you have the right to request environmental - information under a different law') %> (<a href="/help/requesting#eir">explanation</a>). - <%= _('This covers a very wide spectrum of information about the state of - the <strong>natural and built environment</strong>, such as:') %> - - <ul> - <li><%= _('Air, water, soil, land, flora and fauna (including how these effect - human beings)') %></li> - <li><%= _('Information on emissions and discharges (e.g. noise, energy, - radiation, waste materials)') %></li> - <li><%= _('Human health and safety') %></li> - <li><%= _('Cultural sites and built structures (as they may be affected by the - environmental factors listed above)') %></li> - <li><%= _('Plans and administrative measures that affect these matters') %></li> - </ul> - - <p><%= _('Please only request information that comes under those categories, <strong>do not waste your - time</strong> or the time of the public authority by requesting unrelated information.') %></p> - <% end %> - </div> - - <div id="request_header_subject"> - <p> - <label class="form_label" for="typeahead_search"><%= _('Summary:') %></label> - <%= f.text_field :title, :size => 50, :id =>"typeahead_search" %> - </p> - <div class="form_item_note"> - (<%= _("a one line summary of the information you are requesting, \n\t\t\te.g.") %> - <%= render :partial => "summary_suggestion" %>) - </div> - </div> - - <div id="typeahead_response"> - </div> - </div> - - <div id="request_advice"> - <ul> - <li><%= _('Write your request in <strong>simple, precise language</strong>.') %></li> - <li><%= _('Ask for <strong>specific</strong> documents or information, this site is not suitable for general enquiries.') %></li> - <li><%= raw(_('Keep it <strong>focused</strong>, you\'ll be more likely to get what you want (<a href="%s">why?</a>).') % [help_requesting_path + '#focused']) %></li> - </ul> - </div> - - <div id="request_form"> - <% fields_for :outgoing_message do |o| %> - <p> - <label class="form_label" for="outgoing_message_body"><%= _('Your request:') %></label> - <%= o.text_area :body, :rows => 20, :cols => 60 %> - </p> - <% end %> - - <% if !@user %> - <p class="form_note"> - <%= raw(_('Everything that you enter on this page, including <strong>your name</strong>, - will be <strong>displayed publicly</strong> on - this website forever (<a href="%s">why?</a>).') % [help_privacy_path+"#public_request"]) %> - <%= raw(_('If you are thinking of using a pseudonym, - please <a href="%s">read this first</a>.') % [help_privacy_path+"#real_name"]) %> - </p> - <% else %> - <p class="form_note"> - <%= raw(_('Everything that you enter on this page - will be <strong>displayed publicly</strong> on - this website forever (<a href="%s">why?</a>).') % [help_privacy_path+"#public_request"]) %> - </p> - <% end %> - - <p class="form_note"> - <%= raw(_("<strong> Can I request information about myself?</strong>\n" + - "\t\t\t<a href=\"%s\">No! (Click here for details)</a>") % [help_requesting_path+"#data_protection"]) %> - </p> - - <div class="form_button"> - <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> - <%= hidden_field_tag(:submitted_new_request, 1 ) %> - <%= hidden_field_tag(:preview, 1 ) %> - <%= submit_tag _("Preview your public request") %> - </div> - - <% if !@info_request.tag_string.empty? %> - <p class="form_note"> - <!-- <label class="form_label" for="info_request_tag_string">Tags:</label> - <%= f.text_field :tag_string, :size => 50 %> --> - - <%= f.hidden_field(:tag_string) %> - <strong>Tags:</strong> <%=h @info_request.tag_string %> - </p> - <% end %> - - </div> -<% end %> - - diff --git a/app/views/request/new_bad_contact.rhtml b/app/views/request/new_bad_contact.html.erb index 56f3f4168..56f3f4168 100644 --- a/app/views/request/new_bad_contact.rhtml +++ b/app/views/request/new_bad_contact.html.erb diff --git a/app/views/request/new_please_describe.rhtml b/app/views/request/new_please_describe.html.erb index ff27405b8..8da4eb555 100644 --- a/app/views/request/new_please_describe.rhtml +++ b/app/views/request/new_please_describe.html.erb @@ -1,4 +1,4 @@ -<% @title = "First, did your other requests succeed?" %> +<% @title = _("First, did your other requests succeed?") %> <h1><%=@title%></h1> @@ -13,7 +13,7 @@ if they are successful yet or not.') %> </ul> <p> - <%= raw(_('When you\'re done, <strong>come back here</strong>, <a href="%s">reload this page</a> and file your new request.') % [request.request_uri]) %> + <%= _('When you\'re done, <strong>come back here</strong>, <a href="{{url}}">reload this page</a> and file your new request.', :url => request.fullpath.html_safe) %> </p> <p> diff --git a/app/views/request/preview.html.erb b/app/views/request/preview.html.erb new file mode 100644 index 000000000..0265d0328 --- /dev/null +++ b/app/views/request/preview.html.erb @@ -0,0 +1,62 @@ +<% if @batch %> + <% @title = _("Preview new {{law_used_short}} request", :law_used_short => h(@info_request.law_used_short)) %> +<% else %> + <% @title = _("Preview new {{law_used_short}} request to '{{public_body_name}}", :law_used_short => h(@info_request.law_used_short), :public_body_name => h(@info_request.public_body.name)) %> +<% end %> +<%= form_for(@info_request, :url => (@batch ? new_batch_path : new_request_path), :html => { :id => 'preview_form' } ) do |f| %> + + <h1><%= _('3. Now check your request') %></h1> + <ul> + <li><%= _('Check you haven\'t included any <strong>personal information</strong>.') %></li> + <li><%= raw(_('Your name, request and any responses will appear in <strong>search engines</strong> + (<a href="{{url}}">details</a>).', :url => (help_privacy_path+"#public_request").html_safe)) %> + </li> + </ul> + + <%= fields_for :outgoing_message do |o| %> + + <div class="correspondence" id="outgoing-0"> + <p class="preview_subject"> + <strong><%= _('To:') %></strong> + <% if @batch %> + <%= _("Your selected authorities")%> + <% else %> + <%=h(@info_request.public_body.name)%> + <% end %> + <br><strong><%= _('Subject:') %></strong> <%=h @info_request.email_subject_request %> + </p> + + <div class="correspondence_text"> + <p><%= @outgoing_message.get_body_for_html_display %></p> + <%= o.hidden_field(:body) %> + </div> + + <p class="event_actions"> + </p> + </div> + <% end %> + + <p><%= _('<strong>Privacy note:</strong> If you want to request private information about + yourself then <a href="{{url}}">click here</a>.', :url => (help_requesting_path+"#data_protection").html_safe) %> + + <p> + <%= f.hidden_field(:title) %> + <% if @batch %> + <% params[:public_body_ids].each do |public_body_id| %> + <%= hidden_field_tag("public_body_ids[]", public_body_id)%> + <% end %> + <% else %> + <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% end %> + <%= f.hidden_field(:tag_string) %> + <%= hidden_field_tag(:submitted_new_request, 1) %> + <%= hidden_field_tag(:preview, 0 ) %> + <%= submit_tag _("Edit this request"), :name => 'reedit', :id => 'reedit_button' %> + <%= submit_tag _("Send request"), :name => 'submit', :id => 'submit_button' %> + </p> + + <% if !@info_request.tag_string.empty? %> + <p><strong><%= _('Tags:') %></strong> <%=h @info_request.tag_string %></p> + <% end %> + +<% end %> diff --git a/app/views/request/preview.rhtml b/app/views/request/preview.rhtml deleted file mode 100644 index 8d1fd753e..000000000 --- a/app/views/request/preview.rhtml +++ /dev/null @@ -1,48 +0,0 @@ -<% @title = "Preview new " + h(@info_request.law_used_short) + " request to '" + h(@info_request.public_body.name) + "'" %> - -<% form_for(:info_request, @info_request, :html => { :id => 'preview_form' } ) do |f| %> - - <h1><%= _('3. Now check your request') %></h1> - <ul> - <li><%= _('Check you haven\'t included any <strong>personal information</strong>.') %></li> - <li><%= raw(_('Your name, request and any responses will appear in <strong>search engines</strong> - (<a href="%s">details</a>).') % [help_privacy_path+"#public_request"]) %> - </li> - </ul> - - <% fields_for :outgoing_message do |o| %> - - <div class="correspondence" id="outgoing-0"> - <p class="preview_subject"> - <strong><%= _('To:') %></strong> <%=h @info_request.public_body.name %> - <br><strong><%= _('Subject:') %></strong> <%=h @info_request.email_subject_request %> - </p> - - <div class="correspondence_text"> - <p><%= @outgoing_message.get_body_for_html_display %></p> - <%= o.hidden_field(:body) %> - </div> - - <p class="event_actions"> - </p> - </div> - <% end %> - - <p><%= raw(_('<strong>Privacy note:</strong> If you want to request private information about - yourself then <a href="%s">click here</a>.') % [help_requesting_path+"#data_protection"]) %> - - <p> - <%= f.hidden_field(:title) %> - <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> - <%= f.hidden_field(:tag_string) %> - <%= hidden_field_tag(:submitted_new_request, 1) %> - <%= hidden_field_tag(:preview, 0 ) %> - <%= submit_tag _("Edit this request"), :name => 'reedit', :id => 'reedit_button' %> - <%= submit_tag _("Send request"), :name => 'submit', :id => 'submit_button' %> - </p> - - <% if !@info_request.tag_string.empty? %> - <p><strong><%= _('Tags:') %></strong> <%=h @info_request.tag_string %></p> - <% end %> - -<% end %>
\ No newline at end of file diff --git a/app/views/request/select_authorities.html.erb b/app/views/request/select_authorities.html.erb new file mode 100644 index 000000000..e16bcc191 --- /dev/null +++ b/app/views/request/select_authorities.html.erb @@ -0,0 +1,77 @@ +<% @title = _("Select the authorities to write to") %> +<h1><%= _('1. Select authorities') %></h1> + +<p> + <%= _("Search for the authorities you'd like information from:") %> +</p> + +<div> + <%= form_tag(select_authorities_path, {:method => 'get', :id => 'body_search_form', :remote => true, "data-type" => 'json'}) do %> + <%= text_field_tag 'public_body_query', params[:public_body_query], { :size => 30, :title => "type your search term here" } %> + <% if !@public_bodies.blank? %> + <%- @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <%- end %> + <% end %> + <% end %> +</div> + +<div id="body_selection"> + <div id="body_lists"> + <div id="body_candidates" class="body_list"> + <%= form_tag(select_authorities_path, {:id => "body_select_form"}) do %> + <%= submit_tag _(' >> '), :id => 'body_select_all_button', :class => 'select_all_button' %> + <%= submit_tag _(' > '), :id => 'body_select_button' %> + <%= hidden_field_tag "public_body_query", params[:public_body_query], { :id => 'public_body_select_query' } %> + <% if !@public_bodies.blank? %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <% end %> + <% end %> + <select multiple name="public_body_ids[]" id="select_body_candidates" class="body_select" size="15"> + <% if @search_bodies %> + <% @search_bodies.results.each do |result| %> + <% unless (@public_bodies && @public_bodies.include?(result[:model])) %> + <option value="<%= result[:model].id %>"><%= result[:model].name %></option> + <% end %> + <% end %> + <% end %> + </select> + <% end %> + </div> + + <div id="body_selections" class="body_list"> + <%= form_tag(select_authorities_path, {:id => "body_deselect_form"}) do %> + + <%= submit_tag _(' < '), :id => 'body_deselect_button' %> + <%= submit_tag _(' << '), :id => 'body_deselect_all_button', :class => 'select_all_button' %> + <%= hidden_field_tag "public_body_query", params[:public_body_query], { :id => 'public_body_deselect_query' } %> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <% end %> + <% end %> + <select multiple name="remove_public_body_ids[]" id="select_body_selections" class="body_select" size="15"> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <option value="<%= public_body.id %>"><%= public_body.name %></option> + <% end %> + <% end %> + </select> + <% end %> + + <div id="body_submission"> + <%= form_tag(new_batch_path, {:id => "body_submit_form"}) do %> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id , {:id => nil} %> + <% end %> + <% end %> + <%= submit_tag _('Make a request to these authorities'), :id => 'body_submit_button' %> + <% end %> + </div> + + </div> + </div> +</div> +<%= javascript_include_tag 'jquery_ujs.js', 'select-authorities.js' %> diff --git a/app/views/request/select_authority.rhtml b/app/views/request/select_authority.html.erb index 652c24da9..83abdb184 100644 --- a/app/views/request/select_authority.rhtml +++ b/app/views/request/select_authority.html.erb @@ -1,23 +1,22 @@ -<script type="text/javascript" src="/javascripts/ba-throttle-debounce.js"></script> <script type="text/javascript"> $(document).ready(function(){ $("#authority_preview").hide(); - + // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin: // http://benalman.com/projects/jquery-throttle-debounce-plugin/ - $("#query").keypress($.debounce( 300, function() { + $("#query").keypress($.debounce( 300, function() { // Do a type ahead search and display results $("#typeahead_response").load("<%=search_ahead_bodies_url%>?query="+encodeURI(this.value), function() { $("#authority_preview").hide(); // Hide the preview, since results have changed }); })); - // We're using the existing body list: we intercept the clicks on the titles to + // We're using the existing body list: we intercept the clicks on the titles to // display a preview on the right hand side of the screen $("#typeahead_response .head a").live('click', function() { $("#authority_preview").load(this.href+" #public_body_show", function() { $("#authority_preview").show(); - $(window).scrollTop($("#banner").height()); + $(window).scrollTop($("#banner").height()); $("#authority_preview #header_right").hide(); }); return false; @@ -30,41 +29,31 @@ <h1 style="clear: left"><%= _('1. Select an authority') %></h1> <div id="authority_selection"> - <% form_tag({:controller => "request", :action => "select_authority"}, {:id => "search_form", :method => "get"}) do %> + <%= form_tag({:controller => "request", :action => "select_authority"}, {:id => "search_form", :method => "get"}) do %> <div> <p> - <%= raw(_('First, type in the <strong>name of the UK public authority</strong> you\'d + <%= _('First, type in the <strong>name of the UK public authority</strong> you\'d like information from. <strong>By law, they have to respond</strong> - (<a href="%s#%s">why?</a>).') % [help_about_url, "whybother_them"]) %> + (<a href="{{url}}">why?</a>).', :url => (help_about_path + "#whybother_them").html_safe) %> </p> - <%= text_field_tag 'query', params[:query], { :size => 30 } %> + <%= text_field_tag 'query', params[:query], { :size => 30, :title => "type your search term here" } %> <%= hidden_field_tag 'bodies', 1 %> <%= submit_tag _('Search') %> </div> + <% if AlaveteliConfiguration.allow_batch_requests && @user && @user.can_make_batch_requests? %> + <div id="batch_request_link"> + <p> + <%= _('Or make a <a href="{{url}}">batch request</a> to <strong>multiple authorities</strong> at once.', :url => select_authorities_path) %> + </p> + </div> + <% end %> <% end %> - <div id="typeahead_response"> - <% if !@xapian_requests.nil? %> - <% if @xapian_requests.results.size > 0 %> - <h3><%= _('Top search results:') %></h3> - <p> - <%= _('Select one to see more information about the authority.')%> - </p> - <% else %> - <h3><%= _('No results found.') %></h3> - <% end %> - <div id="authority_search_ahead_results"> - <% for result in @xapian_requests.results %> - <%= render :partial => 'public_body/body_listing_single', :locals => { :public_body => result[:model] } %> - <% end %> - </div> - - <% end %> - - + <div id="typeahead_response"> + <%= render :partial => 'public_body/search_ahead' %> </div> </div> - + <div id="authority_preview"> </div> - + diff --git a/app/views/request/show.rhtml b/app/views/request/show.html.erb index 0cae3a9aa..153b0b861 100644 --- a/app/views/request/show.rhtml +++ b/app/views/request/show.html.erb @@ -10,8 +10,8 @@ <% end %> <% if @info_request.prominence == 'requester_only' %> <p id="hidden_request"> - <%= raw(_('This request is hidden, so that only you the requester can see it. Please - <a href="%s">contact us</a> if you are not sure why.') % [help_requesting_path]) %> + <%= _('This request is hidden, so that only you the requester can see it. Please + <a href="{{url}}">contact us</a> if you are not sure why.', :url => help_requesting_path.html_safe) %> </p> <% end %> @@ -25,7 +25,7 @@ <div id="left_column"> <h1><%=h(@info_request.title)%></h1> - <% if !@info_request.is_external? && @info_request.user.profile_photo %> + <% if !@info_request.is_external? && @info_request.user.profile_photo && !@render_to_file %> <p class="user_photo_on_request"> <img src="<%= get_profile_photo_url(:url_name => @info_request.user.url_name) %>" alt=""> </p> @@ -34,21 +34,23 @@ <p class="subtitle"> <% if !@user.nil? && @user.admin_page_links? %> <%= _('{{user}} ({{user_admin_link}}) made this {{law_used_full}} request (<a href="{{request_admin_url}}">admin</a>) to {{public_body_link}} (<a href="{{public_body_admin_url}}">admin</a>)', - :user => @info_request.is_external? ? (@info_request.user_name || _('An anonymous user')) : user_link(@info_request.user), + :user => request_user_link(@info_request, _('An anonymous user')), :law_used_full => h(@info_request.law_used_full), :user_admin_link => user_admin_link_for_request(@info_request, _('external'), _('admin')), - :request_admin_url => request_admin_url(@info_request), + :request_admin_url => admin_request_show_url(@info_request), :public_body_link => public_body_link(@info_request.public_body), - :public_body_admin_url => public_body_admin_url(@info_request.public_body)) %> + :public_body_admin_url => admin_body_show_url(@info_request.public_body)) %> <% else %> - <%= _('{{user}} made this {{law_used_full}} request',:user=>@info_request.is_external? ? (@info_request.user_name || _('An anonymous user')) : user_link(@info_request.user), :law_used_full=>h(@info_request.law_used_full)) %> + <%= _('{{user}} made this {{law_used_full}} request', + :user=>request_user_link(@info_request, _('An anonymous user')), + :law_used_full=>h(@info_request.law_used_full)) %> <%= _('to {{public_body}}',:public_body=>public_body_link(@info_request.public_body)) %> <% end %> </p> <p id="request_status" class="request_icon_line icon_<%= @info_request.calculate_status %>"> <% if @info_request.awaiting_description %> - <% if @is_owning_user && !@info_request.is_external? %> + <% if @is_owning_user && !@info_request.is_external? && !@render_to_file %> <%= _('Please <strong>answer the question above</strong> so we know whether the ')%> <%= MySociety::Format.fancy_pluralize(@new_responses_count, 'recent response contains', 'recent responses contain') %> <%= _('useful information.') %> <% else %> @@ -80,14 +82,14 @@ <%= _('in term time') %> <% end %> <%= _('by') %> <strong><%= simple_date(@info_request.date_response_required_by) %></strong> - (<%= raw(_('<a href="%s">details</a>') % [help_requesting_path + '#quickly_response']) %>) + (<%= link_to _('details'), help_requesting_path + '#quickly_response' %>) <% elsif @status == 'waiting_response_very_overdue' %> <%= _('Response to this request is <strong>long overdue</strong>.') %> <%= _('By law, under all circumstances, {{public_body_link}} should have responded by now',:public_body_link => public_body_link(@info_request.public_body)) %> - (<%= raw(_('<a href="%s">details</a>') % [help_requesting_path + '#quickly_response']) %>). + (<%= link_to _('details'), help_requesting_path + '#quickly_response' %>). <% if !@info_request.is_external? %> <%= _('You can <strong>complain</strong> by') %> - <%= link_to _("requesting an internal review"), show_response_no_followup_url(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup" %>. + <%= link_to _("requesting an internal review"), show_response_no_followup_path(:id => @info_request.id, :incoming_message_id => nil) + "?internal_review=1#followup" %>. <% end %> <% elsif @status == 'not_held' %> <%= public_body_link(@info_request.public_body) %> <%= _('<strong>did not have</strong> the information requested.') %> @@ -101,12 +103,12 @@ <% if @is_owning_user && !@info_request.is_external? %> <%=h @info_request.public_body.name %> <%= _('is <strong>waiting for your clarification</strong>.') %> <%= _('Please') %> - <%= link_to _("send a follow up message"), respond_to_last_url(@info_request) + '#followup' %>. + <%= link_to _("send a follow up message"), respond_to_last_path(@info_request) + '#followup' %>. <% else %> <%= _('The request is <strong>waiting for clarification</strong>.') %> <% if !@info_request.is_external? %> <%= _('If you are {{user_link}}, please',:user_link=>user_link_for_request(@info_request)) %> - <%= link_to _("sign in"), signin_url(:r => request.request_uri) %> <%= _('to send a follow up message.') %> + <%= link_to _("sign in"), signin_path(:r => request.fullpath) %> <%= _('to send a follow up message.') %> <% end %> <% end %> <% elsif @status == 'gone_postal' %> @@ -146,4 +148,4 @@ <%= render :partial => 'after_actions' %> </div> -<%= render :partial => 'sidebar' %> +<%- if @sidebar %><%= render :partial => 'sidebar' %><% end %> diff --git a/app/views/request/show.text.erb b/app/views/request/show.text.erb new file mode 100644 index 000000000..8079d10d5 --- /dev/null +++ b/app/views/request/show.text.erb @@ -0,0 +1,17 @@ +<%= _('This is a plain-text version of the Freedom of Information request "{{request_title}}". The latest, full version is available online at {{full_url}}', :request_title => @info_request.title, :full_url => "http://#{AlaveteliConfiguration::domain}#{show_request_path(:url_title=>@info_request.url_title)}") %>. + +<% @info_request_events.each do |info_request_event| %> + <% if info_request_event.visible %> + <% case info_request_event.event_type %> + <% when 'response' %> + <%= render :partial => 'request/incoming_correspondence', :formats => 'text', :locals => { :incoming_message => info_request_event.incoming_message } %> + <% when 'sent', 'followup_sent' %> + <%= render :partial => 'request/outgoing_correspondence', :formats => 'text', :locals => { :outgoing_message => info_request_event.outgoing_message, :info_request_event => info_request_event }%> + <% when 'resent', 'followup_resent' %> + <%= render :partial => 'request/resent_outgoing_correspondence', :formats => 'text', :locals => { outgoing_message => info_request_event.outgoing_message, :info_request_event => info_request_event }%> + <% when 'comment' %> + <%= render :partial => 'comment/single_comment', :formats => 'text', :locals => { :comment => info_request_event.comment } %> + <% end %> +------------------------------- + <% end %> +<% end %> diff --git a/app/views/request/show_response.rhtml b/app/views/request/show_response.html.erb index ac1f04227..ace86cf4c 100644 --- a/app/views/request/show_response.rhtml +++ b/app/views/request/show_response.html.erb @@ -26,8 +26,8 @@ <%= _('The authority only has a <strong>paper copy</strong> of the information.') %> </dt> <dd> - <%= raw(_('At the bottom of this page, write a reply to them trying to persuade them to scan it in - (<a href="%s">more details</a>).') % [help_privacy_path + '#postal_answer']) %> + <%= _('At the bottom of this page, write a reply to them trying to persuade them to scan it in + (<a href="{{url}}">more details</a>).', :url => (help_privacy_path + '#postal_answer').html_safe) %> </dd> <dt> @@ -36,7 +36,7 @@ <dd> <%= _('To do that please send a private email to ') %><%=h(@postal_email_name)%> <<%=link_to h(@postal_email), "mailto:" + @postal_email%>> - <%= _('containing your postal address, and asking them to reply to this request. + <%= _('containing your postal address, and asking them to reply to this request. Or you could phone them.') %> <%= _('When you receive the paper response, please help @@ -63,16 +63,16 @@ <% end %> <% else %> <% if @incoming_message.recently_arrived %> - <h2><%= _('New response to {{law_used_short}} request',:law_used_short => h(@info_request.law_used_short))%> '<%= request_link @info_request %>'</h2> + <h2><%= _('New response to {{law_used_short}} request',:law_used_short => h(@info_request.law_used_short))%> '<%= request_link @info_request %>'</h2> <% else %> <h2>Response to <%=h(@info_request.law_used_short)%> request '<%= request_link @info_request %>'</h2> <% end %> <% end %> <% if @incoming_message.nil? %> - <%= render :partial => 'correspondence', :locals => { :info_request_event => @info_request.get_last_outgoing_event, :incoming_message => nil } %> + <%= render :partial => 'correspondence', :locals => { :info_request_event => @info_request.get_last_outgoing_event } %> <% else %> - <%= render :partial => 'correspondence', :locals => { :info_request_event => nil, :incoming_message => @incoming_message } %> + <%= render :partial => 'correspondence', :locals => { :info_request_event => @incoming_message.response_event } %> <% end %> <% end %> diff --git a/app/views/request/similar.rhtml b/app/views/request/similar.html.erb index 0d53f6919..5bdefc494 100644 --- a/app/views/request/similar.rhtml +++ b/app/views/request/similar.html.erb @@ -1,7 +1,16 @@ -<% @title = "Requests similar to '" + h(@info_request.title) + "'" + @page_desc %> -<h1><%="Requests similar to '" + request_link(@info_request) + "'" + @page_desc %></h1> +<%- if @page > 1 %> + <%- @title = _("Requests similar to '{{request_title}}' (page {{page}})", {:page => @page, :request_title => @info_request.title}) %> +<%- else %> +<%- @title = _("Requests similar to '{{request_title}}'", {:request_title => @info_request.title}) %> +<%- end %> -<!-- Important terms: <%= @xapian_object.important_terms.join(" ") %> --> +<h1> + <%- if @page > 1 %> + <%= _("Requests similar to '{{request_title}}' (page {{page}})", {:page => @page, :request_title => request_link(@info_request)}) %> + <%- else %> + <%= _("Requests similar to '{{request_title}}'", {:request_title => request_link(@info_request)}) %> + <%- end %> +</h1> <% if @xapian_object.results.empty? %> <p><%= _('No similar requests found.')%></p> diff --git a/app/views/request/simple_correspondence.rhtml b/app/views/request/simple_correspondence.rhtml deleted file mode 100644 index 0da9ef172..000000000 --- a/app/views/request/simple_correspondence.rhtml +++ /dev/null @@ -1,45 +0,0 @@ -<%= _('This is a plain-text version of the Freedom of Information request "{{request_title}}". The latest, full version is available online at {{full_url}}', :request_title => @info_request.title, :full_url => "http://#{Configuration::domain}#{show_request_path(:url_title=>@info_request.url_title)}") %>. - -<% for info_request_event in @info_request_events %> -<% - incoming_message = nil - if info_request_event.visible - if !info_request_event.nil? && info_request_event.event_type == 'response' - incoming_message = info_request_event.incoming_message - end - - - if not incoming_message.nil? - if !incoming_message.safe_mail_from.nil? && incoming_message.safe_mail_from.strip != @info_request.public_body.name.strip %> -<%= _('From:') %> <%= incoming_message.safe_mail_from %><% end - if incoming_message.safe_mail_from.nil? || (incoming_message.mail_from_domain == @info_request.public_body.request_email_domain) %>, <%= @info_request.public_body.name %><% end %> -<%= _('To:') %> <% if @info_request.user_name %><%= @info_request.user_name %><% else %><%= "[#{_('An anonymous user')}]"%><% end %> -<%= _('Date:') %> <%= simple_date(incoming_message.sent_at) %> - -<%= incoming_message.get_body_for_quoting %> -<% incoming_message.get_attachments_for_display.each do |a| %> - <%= _('Attachment:') %> <%= a.display_filename %> (<%= a.display_size %>) - <% end %> -<% -elsif [ 'sent', 'followup_sent' ].include?(info_request_event.event_type) - outgoing_message = info_request_event.outgoing_message - %> -<%= _('From:') %> <% if @info_request.user_name %><%= @info_request.user_name %><% else %><%= "[#{_('An anonymous user')}]"%><% end %> -<%= _('To:') %> <%= @info_request.public_body.name %> -<%= _('Date:') %> <%= simple_date(info_request_event.created_at) %> -<% - text = outgoing_message.body.strip - outgoing_message.remove_privacy_sensitive_things!(text) %> - -<%= text %> -<% elsif [ 'resent', 'followup_resent' ].include?(info_request_event.event_type) %> -<%= _('Date:') %> <%= simple_date(info_request_event.created_at) %> -Sent <% if info_request_event.outgoing_message.message_type == 'initial_request' %> request <% elsif info_request_event.outgoing_message.message_type == 'followup' %> a follow up <% else %> <% raise "unknown message_type" %><% end %> to <%= public_body_link(@info_request.public_body) %> again<% if not info_request_event.same_email_as_previous_send? %>, using a new contact address<% end %>. - -<% elsif info_request_event.event_type == 'comment' - comment = info_request_event.comment -%> -<%= _("{{username}} left an annotation:", :username =>comment.user.name) %> (<%= simple_date(comment.created_at || Time.now) %>) -<%= comment.body.strip %> -<% end %> --------------------------------<% end %><% end %> diff --git a/app/views/request/upload_response.rhtml b/app/views/request/upload_response.html.erb index 675951595..f5fd6f000 100644 --- a/app/views/request/upload_response.rhtml +++ b/app/views/request/upload_response.html.erb @@ -12,7 +12,7 @@ <h1><%= _('Respond to the FOI request')%> '<%=request_link(@info_request)%>'<% _(' made by ')%><%=user_link(@info_request.user) %></h1> <p> - <%= raw(_('Your response will <strong>appear on the Internet</strong>, <a href="%s">read why</a> and answers to other questions.') % [help_officers_path]) %> + <%= raw(_('Your response will <strong>appear on the Internet</strong>, <a href="{{url}}">read why</a> and answers to other questions.', :url => help_officers_path.html_safe)) %> </p> <h2><%= _('Respond by email')%></h2> @@ -28,9 +28,9 @@ <h2><%= _('Respond using the web')%></h2> <p><%= raw(_('Enter your response below. You may attach one file (use email, or - <a href="%s">contact us</a> if you need more).')% [help_contact_path]) %></p> + <a href="{{url}}">contact us</a> if you need more).', :url => help_contact_path.html_safe)) %></p> - <% form_tag '', :id => 'upload_response_form', :multipart => true do %> + <%= form_tag '', :id => 'upload_response_form', :multipart => true do %> <p> <label class="form_label" for="body"><% _('Response:')%></label> <%= text_area_tag :body, "", :rows => 10, :cols => 55 %> diff --git a/app/views/request_game/play.rhtml b/app/views/request_game/play.html.erb index eedf19ca2..d5aa0d00e 100644 --- a/app/views/request_game/play.rhtml +++ b/app/views/request_game/play.html.erb @@ -11,7 +11,7 @@ <tr> <td> <%= c += 1 %>. <td> <td> <%= user_link(classifications.user) %> </td> - <td> <%=pluralize(classifications.cnt, 'request').gsub(" ", " ")%> </td> + <td> <%=pluralize(classifications.cnt, 'request').gsub(" ", " ").html_safe %> </td> </tr> <% end %> </table> @@ -22,7 +22,7 @@ <tr> <td> <%= c += 1 %>. <td> <td> <%= user_link(classifications.user) %> </td> - <td> <%= pluralize(classifications.cnt, 'request').gsub(" ", " ")%> </td> + <td> <%= pluralize(classifications.cnt, 'request').gsub(" ", " ").html_safe %> </td> </tr> <% end %> </table> @@ -36,8 +36,8 @@ information has been provided. Everyone'll be exceedingly grateful.")%></p> <%= render :partial => 'request/request_listing_single', :locals => { :info_request => info_request } %> <% end %> <p id="game_buttons"> -<%= button_to _('I don\'t like these ones — give me some more!'), play_url %> -<%= button_to _('I don\'t want to do any more tidying now!'), stop_url %> +<%= button_to _('I don\'t like these ones — give me some more!'), categorise_play_url %> +<%= button_to _('I don\'t want to do any more tidying now!'), categorise_stop_url %> </p> <p><%= _('Thanks for helping - your work will make it easier for everyone to find successful responses, and maybe even let us make league tables...')%></p> diff --git a/app/views/request_mailer/comment_on_alert.rhtml b/app/views/request_mailer/comment_on_alert.text.erb index 691e6f9bb..691e6f9bb 100644 --- a/app/views/request_mailer/comment_on_alert.rhtml +++ b/app/views/request_mailer/comment_on_alert.text.erb diff --git a/app/views/request_mailer/comment_on_alert_plural.rhtml b/app/views/request_mailer/comment_on_alert_plural.text.erb index a495b8e08..a495b8e08 100644 --- a/app/views/request_mailer/comment_on_alert_plural.rhtml +++ b/app/views/request_mailer/comment_on_alert_plural.text.erb diff --git a/app/views/request_mailer/external_response.rhtml b/app/views/request_mailer/external_response.rhtml deleted file mode 100644 index e9858f03f..000000000 --- a/app/views/request_mailer/external_response.rhtml +++ /dev/null @@ -1 +0,0 @@ -<%=@body%> diff --git a/app/views/request_mailer/external_response.text.erb b/app/views/request_mailer/external_response.text.erb new file mode 100644 index 000000000..fab256adf --- /dev/null +++ b/app/views/request_mailer/external_response.text.erb @@ -0,0 +1 @@ +<%= @message_body %> diff --git a/app/views/request_mailer/fake_response.rhtml b/app/views/request_mailer/fake_response.rhtml deleted file mode 100644 index e9858f03f..000000000 --- a/app/views/request_mailer/fake_response.rhtml +++ /dev/null @@ -1 +0,0 @@ -<%=@body%> diff --git a/app/views/request_mailer/fake_response.text.erb b/app/views/request_mailer/fake_response.text.erb new file mode 100644 index 000000000..fab256adf --- /dev/null +++ b/app/views/request_mailer/fake_response.text.erb @@ -0,0 +1 @@ +<%= @message_body %> diff --git a/app/views/request_mailer/new_response.rhtml b/app/views/request_mailer/new_response.text.erb index 083f873b4..672212f20 100644 --- a/app/views/request_mailer/new_response.rhtml +++ b/app/views/request_mailer/new_response.text.erb @@ -1,6 +1,6 @@ <%= _('You have a new response to the {{law_used_full}} request ',:law_used_full=>@info_request.law_used_full)%> -'<%= @info_request.title %>' <%=_('that you made to')%> -<%= @info_request.public_body.name %>. +'<%= raw @info_request.title %>' <%=_('that you made to')%> +<%= raw @info_request.public_body.name %>. <%= _('To view the response, click on the link below.')%> diff --git a/app/views/request_mailer/new_response_reminder_alert.rhtml b/app/views/request_mailer/new_response_reminder_alert.text.erb index 86fc71de7..c196dafe6 100644 --- a/app/views/request_mailer/new_response_reminder_alert.rhtml +++ b/app/views/request_mailer/new_response_reminder_alert.text.erb @@ -3,7 +3,7 @@ <%=@url%> <%= _('Your request was called {{info_request}}. Letting everyone know whether you got the information will help us keep tabs on',:info_request=>@info_request.title)%> -<%= @info_request.public_body.name %>. +<%= raw @info_request.public_body.name %>. -- <%= _('the {{site_name}} team', :site_name=>site_name) %> diff --git a/app/views/request_mailer/not_clarified_alert.rhtml b/app/views/request_mailer/not_clarified_alert.text.erb index 2408452b3..2408452b3 100644 --- a/app/views/request_mailer/not_clarified_alert.rhtml +++ b/app/views/request_mailer/not_clarified_alert.text.erb diff --git a/app/views/request_mailer/old_unclassified_updated.rhtml b/app/views/request_mailer/old_unclassified_updated.text.erb index 5b8534832..5b8534832 100644 --- a/app/views/request_mailer/old_unclassified_updated.rhtml +++ b/app/views/request_mailer/old_unclassified_updated.text.erb diff --git a/app/views/request_mailer/overdue_alert.rhtml b/app/views/request_mailer/overdue_alert.text.erb index b8a9ba525..249bf6bb8 100644 --- a/app/views/request_mailer/overdue_alert.rhtml +++ b/app/views/request_mailer/overdue_alert.text.erb @@ -1,4 +1,4 @@ -<%= @info_request.public_body.name %> <%= _('have delayed.')%> +<%= raw @info_request.public_body.name %> <%= _('have delayed.')%> <%= _('They have not replied to your {{law_used_short}} request {{title}} promptly, as normally required by law',:law_used_short=>@info_request.law_used_short,:title=>@info_request.title)%><% if @info_request.public_body.is_school? %> <%=_('during term time')%> <% end %>. diff --git a/app/views/request_mailer/requires_admin.rhtml b/app/views/request_mailer/requires_admin.text.erb index 06a798792..b2e58295e 100644 --- a/app/views/request_mailer/requires_admin.rhtml +++ b/app/views/request_mailer/requires_admin.text.erb @@ -1,9 +1,11 @@ +<%= raw @message %> + --------------------------------------------------------------------- -<%=@reported_by.name%> <%= _('has reported an')%> <%=@info_request.law_used_short%> +<%= raw @reported_by.name %> <%= _('has reported an')%> <%= raw @info_request.law_used_short %> <%= _('response as needing administrator attention. Take a look, and reply to this email to let them know what you are going to do about it.')%> -Request '<%=@info_request.title%>': +Request '<%= raw @info_request.title %>': <%= @url %> <%= _('Administration URL:') %> diff --git a/app/views/request_mailer/stopped_responses.rhtml b/app/views/request_mailer/stopped_responses.text.erb index 9cd156860..9cd156860 100644 --- a/app/views/request_mailer/stopped_responses.rhtml +++ b/app/views/request_mailer/stopped_responses.text.erb diff --git a/app/views/request_mailer/very_overdue_alert.rhtml b/app/views/request_mailer/very_overdue_alert.text.erb index 6abd198a0..80597473c 100644 --- a/app/views/request_mailer/very_overdue_alert.rhtml +++ b/app/views/request_mailer/very_overdue_alert.text.erb @@ -1,4 +1,4 @@ -<%= @info_request.public_body.name %> <%= _('are long overdue.')%> +<%= raw @info_request.public_body.name %> <%= _('are long overdue.')%> <%= _('They have not replied to your {{law_used_short}} request {{title}}, as required by law',:law_used_short=>@info_request.law_used_short,:title=>@info_request.title)%><% if @info_request.public_body.is_school? %> <%= _('even during holidays')%><% end %>. diff --git a/app/views/track/_tracking_links.rhtml b/app/views/track/_tracking_links.html.erb index 06e87ac74..5419ec605 100644 --- a/app/views/track/_tracking_links.rhtml +++ b/app/views/track/_tracking_links.html.erb @@ -1,6 +1,6 @@ <% if @user - existing_track = TrackThing.find_by_existing_track(@user, track_thing) + existing_track = TrackThing.find_existing(@user, track_thing) end %> @@ -9,20 +9,20 @@ <% elsif existing_track %> <p><%= track_thing.params[:verb_on_page_already] %></p> <div class="feed_link feed_link_<%=location%>"> - <%= link_to _("Unsubscribe"), {:controller => 'track', :action => 'update', :track_id => existing_track.id, :track_medium => "delete", :r => request.request_uri}, :class => "link_button_green" %> + <%= link_to _("Unsubscribe"), {:controller => 'track', :action => 'update', :track_id => existing_track.id, :track_medium => "delete", :r => request.fullpath}, :class => "link_button_green" %> </div> <% elsif track_thing %> <div class="feed_link feed_link_<%=location%>"> <% if defined? follower_count && follower_count > 0 %> - <%= link_to _("I like this request"), do_track_url(track_thing), :class => "link_button_green" %> + <%= link_to _("I like this request"), do_track_path(track_thing), :class => "link_button_green" %> <% else %> - <%= link_to _("Follow"), do_track_url(track_thing), :class => "link_button_green" %> + <%= link_to _("Follow"), do_track_path(track_thing), :class => "link_button_green" %> <% end %> </div> <div class="feed_link feed_link_<%=location%>"> - <%= link_to '<img src="/images/feed-16.png" alt="">'.html_safe, do_track_url(track_thing, 'feed') %> - <%= link_to (location == 'sidebar' ? _('RSS feed of updates') : _('RSS feed')), do_track_url(track_thing, 'feed') %> + <%= link_to image_tag('feed-16.png', :alt => "RSS icon"), do_track_path(track_thing, 'feed') %> + <%= link_to (location == 'sidebar' ? _('RSS feed of updates') : _('RSS feed')), do_track_path(track_thing, 'feed') %> </div> <% end %> diff --git a/app/views/track/atom_feed.atom.erb b/app/views/track/atom_feed.atom.erb index 23c932308..be9c39e72 100644 --- a/app/views/track/atom_feed.atom.erb +++ b/app/views/track/atom_feed.atom.erb @@ -9,7 +9,7 @@ # Get the HTML content from the same partial template as website search does content = '' if result[:model].class.to_s == 'InfoRequestEvent' - content += render :partial => 'request/request_listing_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } + content += render :partial => 'request/request_listing_via_event', :formats => ['html'], :locals => { :event => result[:model], :info_request => result[:model].info_request } else content = "<p><strong>Unknown search result type " + result[:model].class.to_s + "</strong></p>" end diff --git a/app/views/track_mailer/event_digest.rhtml b/app/views/track_mailer/event_digest.text.erb index dc8132b99..8dbc7fe06 100644 --- a/app/views/track_mailer/event_digest.rhtml +++ b/app/views/track_mailer/event_digest.text.erb @@ -17,17 +17,17 @@ # e.g. Julian Burgess sent a request to Royal Mail Group (15 May 2008) if event.event_type == 'response' - url = main_url(incoming_message_url(event.incoming_message)) + url = incoming_message_url(event.incoming_message) main_text += _("{{public_body}} sent a response to {{user_name}}", :public_body => event.info_request.public_body.name, :user_name => event.info_request.user_name) elsif event.event_type == 'followup_sent' - url = main_url(outgoing_message_url(event.outgoing_message)) + url = outgoing_message_url(event.outgoing_message) main_text += _("{{user_name}} sent a follow up message to {{public_body}}", :user_name => event.info_request.user_name, :public_body => event.info_request.public_body.name) elsif event.event_type == 'sent' # this is unlikely to happen in real life, but happens in the test code - url = main_url(outgoing_message_url(event.outgoing_message)) + url = outgoing_message_url(event.outgoing_message) main_text += _("{{user_name}} sent a request to {{public_body}}", :user_name => event.info_request.user_name, :public_body => event.info_request.public_body.name) elsif event.event_type == 'comment' - url = main_url(comment_url(event.comment)) + url = comment_url(event.comment) main_text += _("{{user_name}} added an annotation", :user_name => event.comment.user.name) else raise "unknown type in event_digest " + event.event_type diff --git a/app/views/user/_change_receive_email.rhtml b/app/views/user/_change_receive_email.html.erb index 83e5d8601..12824f711 100644 --- a/app/views/user/_change_receive_email.rhtml +++ b/app/views/user/_change_receive_email.html.erb @@ -1,4 +1,4 @@ -<% form_tag(:controller=>"user", :action=>"set_receive_email_alerts") do %> +<%= form_tag(:controller=>"user", :action=>"set_receive_email_alerts") do %> <div> <% if @user.receive_email_alerts %> <%= _('You are currently receiving notification of new activity on your wall by email.', :wall_url => show_user_wall_path) %><br><br> diff --git a/app/views/user/_show_user_info.rhtml b/app/views/user/_show_user_info.html.erb index 3c229e9ce..9182f0733 100644 --- a/app/views/user/_show_user_info.rhtml +++ b/app/views/user/_show_user_info.html.erb @@ -1,10 +1,10 @@ <% if !@display_user.get_about_me_for_html_display.empty? || @is_you %> <div class="user_about_me"> - <img class="comment_quote" src="/images/quote-marks.png" alt=""> + <%= image_tag "quote-marks.png", :class => "comment_quote" %> <%= @display_user.get_about_me_for_html_display %> <% if @is_you %> - (<%= link_to _("edit text about you"), set_profile_about_me_url() %>) + (<%= link_to _("edit text about you"), set_profile_about_me_path %>) <% end %> </div> <% end %> @@ -12,9 +12,9 @@ <% if @is_you %> <p id="user_change_password_email"> <% if @display_user.profile_photo %> - <%= link_to _('Change profile photo'), set_profile_photo_url() %> | + <%= link_to _('Change profile photo'), set_profile_photo_path %> | <% end %> - <%= link_to _('Change your password'), signchangepassword_url() %> | - <%= link_to _('Change your email'), signchangeemail_url() %> + <%= link_to _('Change your password'), signchangepassword_path %> | + <%= link_to _('Change your email'), signchangeemail_path %> </p> <% end %> diff --git a/app/views/user/_signin.rhtml b/app/views/user/_signin.html.erb index c4d917991..afc55d249 100644 --- a/app/views/user/_signin.rhtml +++ b/app/views/user/_signin.html.erb @@ -1,6 +1,6 @@ <div id="signin"> -<% form_tag({:action => "signin"}, {:id => "signin_form"}) do %> +<%= form_tag({:action => "signin"}, {:id => "signin_form"}) do %> <%= foi_error_messages_for :user_signin %> <!--<% if not sign_in_as_existing_user %> @@ -18,7 +18,7 @@ </p> <p class="form_note"> - <%= link_to _('Forgotten your password?'), signchangepassword_url + "?pretoken=" + h(params[:token]), :tabindex => 30 %> + <%= link_to _('Forgotten your password?'), signchangepassword_path + "?pretoken=" + h(params[:token]), :tabindex => 30 %> </p> <p class="form_checkbox"> diff --git a/app/views/user/_signup.rhtml b/app/views/user/_signup.html.erb index 913423ffa..ec6541881 100644 --- a/app/views/user/_signup.rhtml +++ b/app/views/user/_signup.html.erb @@ -1,6 +1,6 @@ <div id="signup"> -<% form_tag({:action => "signup"}, {:id => "signup_form"}) do %> +<%= form_tag({:action => "signup"}, {:id => "signup_form"}) do %> <%= foi_error_messages_for :user_signup %> <!--<h2><%= _('If you\'re new to {{site_name}}', :site_name=>site_name)%></h2>--> @@ -10,30 +10,30 @@ <%= text_field 'user_signup', 'email', { :size => 20, :tabindex => 60 } %> </p> <div class="form_item_note"> - <%= raw(_('We will not reveal your email address to anybody unless you or - the law tell us to (<a href="%s">details</a>). ') %[help_privacy_path]) %> + <%= _('We will not reveal your email address to anybody unless you or + the law tell us to (<a href="{{url}}">details</a>). ', :url => help_privacy_path) %> </div> <p> <label class="form_label" for="user_signup_name"> <%= _('Your name:')%></label> - <%= text_field 'user_signup', 'name', { :size => 20, :tabindex => 70 } %> + <%= text_field 'user_signup', 'name', { :size => 20, :tabindex => 70, :autocomplete => "off" } %> </p> <div class="form_item_note"> - <%= raw(_('Your <strong>name will appear publicly</strong> - (<a href="%s">why?</a>) + <%= _('Your <strong>name will appear publicly</strong> + (<a href="{{why_url}}">why?</a>) on this website and in search engines. If you - are thinking of using a pseudonym, please - <a href="%s">read this first</a>.') % [help_privacy_path+"#public_request", help_privacy_path+"#real_name"]) %> + are thinking of using a pseudonym, please + <a href="{{help_url}}">read this first</a>.', :why_url => (help_privacy_path+"#public_request").html_safe, :help_url => (help_privacy_path+"#real_name").html_safe) %> </div> <p> <label class="form_label" for="user_signup_password"> <%= _('Password:')%></label> - <%= password_field 'user_signup', 'password', { :size => 15, :tabindex => 80 } %> + <%= password_field 'user_signup', 'password', { :size => 15, :tabindex => 80, :autocomplete => "off" } %> </p> <p> <label class="form_label" for="user_signup_password_confirmation"> <%= _('Password: (again)')%></label> - <%= password_field 'user_signup', 'password_confirmation', { :size => 15, :tabindex => 90 } %> + <%= password_field 'user_signup', 'password_confirmation', { :size => 15, :tabindex => 90, :autocomplete => "off" } %> </p> <% if @request_from_foreign_country %> diff --git a/app/views/user/_user_listing_single.rhtml b/app/views/user/_user_listing_single.html.erb index 53df3a7e8..ed1b95718 100644 --- a/app/views/user/_user_listing_single.rhtml +++ b/app/views/user/_user_listing_single.html.erb @@ -5,14 +5,14 @@ end %> <div class="user_listing"> <% if display_user.profile_photo %> <div class="user_photo_on_search"> - <a href="<%=user_url(display_user)%>"> + <a href="<%=user_path(display_user)%>"> <img src="<%= get_profile_photo_url(:url_name => display_user.url_name) %>" alt=""> </a> </div> <% end %> <span class="head <% if display_user.profile_photo %>no_icon<% end %>"> - <%= link_to highlight_words(display_user.name, @highlight_words), user_url(display_user) %> + <%= link_to highlight_words(display_user.name, @highlight_words), user_path(display_user) %> </span> <span class="bottomline"> diff --git a/app/views/user/bad_token.rhtml b/app/views/user/bad_token.html.erb index 538bc5606..538bc5606 100644 --- a/app/views/user/bad_token.rhtml +++ b/app/views/user/bad_token.html.erb diff --git a/app/views/user/banned.rhtml b/app/views/user/banned.html.erb index 475c10977..475c10977 100644 --- a/app/views/user/banned.rhtml +++ b/app/views/user/banned.html.erb diff --git a/app/views/user/confirm.rhtml b/app/views/user/confirm.html.erb index bc70a1f36..bc70a1f36 100644 --- a/app/views/user/confirm.rhtml +++ b/app/views/user/confirm.html.erb diff --git a/app/views/user/contact.rhtml b/app/views/user/contact.html.erb index 333b72334..6d23dd1ed 100644 --- a/app/views/user/contact.rhtml +++ b/app/views/user/contact.html.erb @@ -6,7 +6,7 @@ <%= foi_error_messages_for :contact %> -<% form_for :contact do |f| %> +<%= form_for :contact do |f| %> <div class="form_note"> <h1><%= _("Contact {{recipient}}", :recipient => h(@recipient_user.name)) %></h1> diff --git a/app/views/user/no_cookies.rhtml b/app/views/user/no_cookies.html.erb index c291367f2..0a4a39b1b 100644 --- a/app/views/user/no_cookies.rhtml +++ b/app/views/user/no_cookies.html.erb @@ -12,11 +12,11 @@ browser. Then press refresh to have another go.')%></p> <p><%= _('If your browser is set to accept cookies and you are seeing this message, then there is probably a fault with our server.')%> -<%= raw(_('Please <a href="%s">get in touch</a> with us so we can fix it.') % [help_contact_path]) %> +<%= _('Please <a href="{{url}}">get in touch</a> with us so we can fix it.', :url => help_contact_path.html_safe) %> <%= _('Let us know what you were doing when this message appeared and your browser and operating system type and version.')%></p> -<p><%= raw(_('If you are still having trouble, please <a href="%s">contact us</a>.') % [help_contact_path]) %> +<p><%= _('If you are still having trouble, please <a href="{{url}}">contact us</a>.', :url => help_contact_path.html_safe) %> </p> diff --git a/app/views/user/rate_limited.rhtml b/app/views/user/rate_limited.html.erb index d52deebab..54a4e0461 100644 --- a/app/views/user/rate_limited.rhtml +++ b/app/views/user/rate_limited.html.erb @@ -2,7 +2,7 @@ <h1><%=@title%></h1> -<p><%= _("You have hit the rate limit on new requests. Users are ordinarily limited to {{max_requests_per_user_per_day}} requests in any rolling 24-hour period. You will be able to make another request in {{can_make_another_request}}.", :max_requests_per_user_per_day => Configuration::max_requests_per_user_per_day, :can_make_another_request => distance_of_time_in_words(Time.now, @next_request_permitted_at))%></p> +<p><%= _("You have hit the rate limit on new requests. Users are ordinarily limited to {{max_requests_per_user_per_day}} requests in any rolling 24-hour period. You will be able to make another request in {{can_make_another_request}}.", :max_requests_per_user_per_day => AlaveteliConfiguration::max_requests_per_user_per_day, :can_make_another_request => distance_of_time_in_words(Time.now, @next_request_permitted_at))%></p> <p><%= _("There is a limit on the number of requests you can make in a day, because we don’t want public authorities to be bombarded with large numbers of inappropriate requests. If you feel you have a good reason to ask for the limit to be lifted in your case, please <a href='{{help_contact_path}}'>get in touch</a>.", :help_contact_path => help_contact_path) %></p> diff --git a/app/views/user/river.rhtml b/app/views/user/river.html.erb index 9618e0aa8..9618e0aa8 100644 --- a/app/views/user/river.rhtml +++ b/app/views/user/river.html.erb diff --git a/app/views/user/set_crop_profile_photo.rhtml b/app/views/user/set_crop_profile_photo.html.erb index eed0304d2..0a22d36dc 100644 --- a/app/views/user/set_crop_profile_photo.rhtml +++ b/app/views/user/set_crop_profile_photo.html.erb @@ -9,7 +9,7 @@ <div id="set_crop_profile_photo"> -<% form_tag 'set_photo', :id => 'set_crop_profile_photo_form', :multipart => true do %> +<%= form_tag 'set_photo', :id => 'set_crop_profile_photo_form', :multipart => true do %> <table> <tr> @@ -37,7 +37,7 @@ <p> <%= hidden_field_tag 'submitted_crop_profile_photo', 1 %> - <%= submit_tag _("Done") + " >>" %> + <%= submit_tag _("Done >>") %> </p> <% end %> diff --git a/app/views/user/set_draft_profile_photo.rhtml b/app/views/user/set_draft_profile_photo.html.erb index b3faba7fc..b4bdd80f3 100644 --- a/app/views/user/set_draft_profile_photo.rhtml +++ b/app/views/user/set_draft_profile_photo.html.erb @@ -8,7 +8,7 @@ <div id="set_draft_profile_photo"> -<% form_tag 'set_photo', :id => 'set_draft_profile_photo_form', :multipart => true do %> +<%= form_tag 'set_photo', :id => 'set_draft_profile_photo_form', :multipart => true do %> <p> <label class="form_label" for="file_1"><%= _('Photo of you:')%></label> <%= file_field_tag :file, :size => 35, :id => 'file_1' %> @@ -45,14 +45,14 @@ <h2><%= _('OR remove the existing photo')%></h2> - <% form_tag 'clear_photo', :id => 'clear_profile_photo_form', :multipart => true do %> + <%= form_tag 'clear_photo', :id => 'clear_profile_photo_form', :multipart => true do %> <%= submit_tag _("Clear photo") %> <% end %> <% end %> <p> - <%= link_to _("Cancel, return to your profile page"), user_url(@user) %> + <%= link_to _("Cancel, return to your profile page"), user_path(@user) %> </p> </div> diff --git a/app/views/user/set_profile_about_me.rhtml b/app/views/user/set_profile_about_me.html.erb index 4fe1047da..fb7de7e97 100644 --- a/app/views/user/set_profile_about_me.rhtml +++ b/app/views/user/set_profile_about_me.html.erb @@ -4,7 +4,7 @@ <%= foi_error_messages_for :about_me %> -<% form_for :about_me do |f| %> +<%= form_for :about_me do |f| %> <div class="form_note"> <h1><%= _('Edit text about you')%></h1> <p> @@ -26,7 +26,7 @@ <%= _(' Include relevant links, such as to a campaign page, your blog or a twitter account. They will be made clickable. e.g.')%> - <a href="https://twitter.com/<%= Configuration::twitter_username %>">https://twitter.com/<%= Configuration::twitter_username %></a> + <a href="https://twitter.com/<%= AlaveteliConfiguration::twitter_username %>">https://twitter.com/<%= AlaveteliConfiguration::twitter_username %></a> </p> </div> diff --git a/app/views/user/show.rhtml b/app/views/user/show.html.erb index 31ea2a70b..76ecdeda0 100644 --- a/app/views/user/show.rhtml +++ b/app/views/user/show.html.erb @@ -55,7 +55,7 @@ <% else %> <% if @is_you %> <span id="set_photo"> - <%= link_to _('Set your profile photo'), set_profile_photo_url() %> + <%= link_to _('Set your profile photo'), set_profile_photo_path %> </span> <% end %> <% end %> @@ -66,12 +66,12 @@ <p class="subtitle"> <%= _('Joined {{site_name}} in', :site_name=>site_name) %> <%= year_from_date(@display_user.created_at) %> <% if !@user.nil? && @user.admin_page_links? %> - (<%= link_to "admin", user_admin_url(@display_user) %>) + (<%= link_to "admin", admin_user_show_path(@display_user) %>) <% end %> </p> <p> - <%= link_to _('Send message to ') + h(@display_user.name), contact_user_url(:id => @display_user.id) %> + <%= link_to _('Send message to ') + h(@display_user.name), contact_user_path(:id => @display_user.id) %> <% if @is_you %> (<%= _('just to see how it works')%>) <% end %> @@ -97,7 +97,7 @@ <% if not @is_you %> <p id="user_not_logged_in"> - <%= raw(_('<a href="%s">Sign in</a> to change password, subscriptions and more ({{user_name}} only)',:user_name=>h(@display_user.name)) % [signin_url(:r => request.request_uri)]) %> + <%= _('<a href="{{url}}">Sign in</a> to change password, subscriptions and more ({{user_name}} only)',:user_name=>h(@display_user.name), :url => signin_url(:r => request.fullpath).html_safe) %> </p> <% end %> </div> @@ -105,11 +105,22 @@ <div style="clear:both"></div> <% end %> +<% if @show_batches %> + + <% if @is_you && !@display_user.info_request_batches.empty? %> + <h2 class="batch_results" id="batch_requests"> + <%= n_('Your {{count}} batch requests', 'Your {{count}} batch requests', @display_user.info_request_batches.size, :count => @display_user.info_request_batches.size) %> + </h2> + <%= render :partial => 'info_request_batch/info_request_batch', :collection => @display_user.info_request_batches %> + <% end %> + +<% end %> + <% if @show_requests %> <div id="user_profile_search"> - <% form_tag(show_user_url, :method => "get", :id=>"search_form") do %> + <%= form_tag(show_user_url, :method => "get", :id=>"search_form") do %> <div> - <%= text_field_tag(:user_query, params[:user_query]) %> + <%= text_field_tag(:user_query, params[:user_query], {:title => "type your search term here" }) %> <% if @is_you %> <%= submit_tag(_("Search your contributions")) %> <% else %> @@ -128,7 +139,7 @@ <% end %> <% else %> <h2 class="foi_results" id="foi_requests"> - <%= @is_you ? n_('Your %d Freedom of Information request', 'Your %d Freedom of Information requests', @xapian_requests.matches_estimated) % @xapian_requests.matches_estimated.to_s : n_('This person\'s %d Freedom of Information request', 'This person\'s %d Freedom of Information requests', @xapian_requests.matches_estimated) % @xapian_requests.matches_estimated %> + <%= @is_you ? n_('Your {{count}} Freedom of Information request', 'Your {{count}} Freedom of Information requests', @xapian_requests.matches_estimated, :count => @xapian_requests.matches_estimated) : n_("This person's {{count}} Freedom of Information request", "This person's {{count}} Freedom of Information requests", @xapian_requests.matches_estimated, :count => @xapian_requests.matches_estimated) %> <!-- matches_estimated <%=@xapian_requests.matches_estimated%> --> <%= @match_phrase %> <%= @page_desc %> @@ -158,7 +169,7 @@ <% end %> <% else %> <h2 id="annotations"> - <%= @is_you ? n_('Your %d annotation', 'Your %d annotations', @display_user.visible_comments.size) % @display_user.visible_comments.size : n_('This person\'s %d annotation', 'This person\'s %d annotations', @display_user.visible_comments.size) % @display_user.visible_comments.size %> + <%= @is_you ? n_('Your {{count}} annotation', 'Your {{count}} annotations', @display_user.visible_comments.size, :count => @display_user.visible_comments.size) : n_("This person's {{count}} annotation", "This person's {{count}} annotations", @display_user.visible_comments.size, :count => @display_user.visible_comments.size) %> <!-- matches_estimated <%=@xapian_comments.matches_estimated%> --> <%= @page_desc %> </h2> @@ -181,12 +192,12 @@ <p><%= _("You're not following anything.")%></p> <% else %> <% if @track_things_grouped.size == 1 %> - <% form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %> + <%= form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %> <h3> <%=TrackThing.track_type_description(@track_things[0].track_type)%> <%= hidden_field_tag 'track_type', @track_things[0].track_type %> <%= hidden_field_tag 'user', @display_user.id %> - <%= hidden_field_tag 'r', request.request_uri %> + <%= hidden_field_tag 'r', request.fullpath %> <% if @track_things.size > 1 %> <%= submit_tag _('unsubscribe all') %> <% end %> @@ -195,12 +206,12 @@ <% end %> <% for track_type, track_things in @track_things_grouped %> <% if @track_things_grouped.size > 1 %> - <% form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %> + <%= form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %> <h3> <%=TrackThing.track_type_description(track_type)%> <%= hidden_field_tag 'track_type', track_type %> <%= hidden_field_tag 'user', @display_user.id %> - <%= hidden_field_tag 'r', request.request_uri %> + <%= hidden_field_tag 'r', request.fullpath %> <% if track_things.size > 1 %> <%= submit_tag _('unsubscribe all')%> <% end %> @@ -211,11 +222,11 @@ <ul> <% for track_thing in track_things %> <li> - <% form_tag({:controller => 'track', :action => 'update', :track_id => track_thing.id}, :class => "feed_form") do %> + <%= form_tag({:controller => 'track', :action => 'update', :track_id => track_thing.id}, :class => "feed_form") do %> <div> <%= track_thing.params[:list_description] %> <%= hidden_field_tag 'track_medium', "delete", { :id => 'track_medium_' + track_thing.id.to_s } %> - <%= hidden_field_tag 'r', request.request_uri, { :id => 'r_' + track_thing.id.to_s } %> + <%= hidden_field_tag 'r', request.fullpath, { :id => 'r_' + track_thing.id.to_s } %> <%= submit_tag _('unsubscribe') %> </div> <% end %> diff --git a/app/views/user/sign.rhtml b/app/views/user/sign.html.erb index 6a1979155..e8c5d5a58 100644 --- a/app/views/user/sign.rhtml +++ b/app/views/user/sign.html.erb @@ -12,7 +12,9 @@ <% end %> </p> <% if @post_redirect.post_params["controller"] == "admin_general" %> - <p id="superuser_message">Don't have a superuser account yet? <%= link_to "Sign in as the emergency user", @post_redirect.uri + "?emergency=1" %></p> + <% unless AlaveteliConfiguration::disable_emergency_user %> + <p id="superuser_message"><%= _("Don't have a superuser account yet?") %> <%= link_to _("Sign in as the emergency user"), @post_redirect.uri + "?emergency=1" %></p> + <% end %> <% end %> <%= render :partial => 'signin', :locals => { :sign_in_as_existing_user => true } %> diff --git a/app/views/user/signchangeemail.rhtml b/app/views/user/signchangeemail.html.erb index 0f8b76bc5..7308179f4 100644 --- a/app/views/user/signchangeemail.rhtml +++ b/app/views/user/signchangeemail.html.erb @@ -4,7 +4,7 @@ <div id="change_email"> -<% form_tag({:action => "signchangeemail"}, {:id => "signchangeemail_form"}) do %> +<%= form_tag({:action => "signchangeemail"}, {:id => "signchangeemail_form"}) do %> <%= foi_error_messages_for :signchangeemail %> <div class="form_note"> diff --git a/app/views/user/signchangeemail_confirm.rhtml b/app/views/user/signchangeemail_confirm.html.erb index bfedbac2d..bfedbac2d 100644 --- a/app/views/user/signchangeemail_confirm.rhtml +++ b/app/views/user/signchangeemail_confirm.html.erb diff --git a/app/views/user/signchangepassword.rhtml b/app/views/user/signchangepassword.html.erb index edb980b9f..51bcb466d 100644 --- a/app/views/user/signchangepassword.rhtml +++ b/app/views/user/signchangepassword.html.erb @@ -4,7 +4,7 @@ <div id="change_password"> -<% form_tag({:action => "signchangepassword"}, {:id => "signchangepassword_form"}) do %> +<%= form_tag({:action => "signchangepassword"}, {:id => "signchangepassword_form"}) do %> <%= foi_error_messages_for :user %> <div class="form_note"> diff --git a/app/views/user/signchangepassword_confirm.rhtml b/app/views/user/signchangepassword_confirm.html.erb index 63b6515cd..63b6515cd 100644 --- a/app/views/user/signchangepassword_confirm.rhtml +++ b/app/views/user/signchangepassword_confirm.html.erb diff --git a/app/views/user/signchangepassword_send_confirm.rhtml b/app/views/user/signchangepassword_send_confirm.html.erb index 84ee28f07..850237c34 100644 --- a/app/views/user/signchangepassword_send_confirm.rhtml +++ b/app/views/user/signchangepassword_send_confirm.html.erb @@ -2,7 +2,7 @@ <div id="change_password"> -<% form_tag({:action => "signchangepassword"}, {:id => "signchangepassword_form"}) do %> +<%= form_tag({:action => "signchangepassword"}, {:id => "signchangepassword_form"}) do %> <%= foi_error_messages_for :signchangepassword %> <div class="form_note"> diff --git a/app/views/user/signin_successful.rhtml b/app/views/user/signin_successful.html.erb index 675701d74..675701d74 100644 --- a/app/views/user/signin_successful.rhtml +++ b/app/views/user/signin_successful.html.erb diff --git a/app/views/user/wall.html.erb b/app/views/user/wall.html.erb new file mode 100644 index 000000000..6699c55fa --- /dev/null +++ b/app/views/user/wall.html.erb @@ -0,0 +1,19 @@ +<% @title = h(@display_user.name) + _(" - wall") %> +<div class="medium_column"> + <% if @is_you %> + <h2><%= _("My wall") %></h2> + <p><%= _('You can change the requests and users you are following on <a href="{{profile_url}}">your profile page</a>.', :profile_url => show_user_profile_path) %></p> + <%= render :partial => 'change_receive_email' %> + <% else %> + <h2><%= _("This is <a href=\"{{profile_url}}\">{{user_name}}'s</a> wall", :profile_url => show_user_profile_path, :user_name => h(@display_user.name)) %></h2> + <% end %> +</div> +<div id="user_profile_search"> + <% if !@feed_results.nil? and !@feed_results.empty? %> + <% for result in @feed_results %> + <%= render :partial => 'request/wall_listing', :locals => { :event => result, :info_request => result.info_request } %> + <% end %> + <% else %> + <p><%= _("There is nothing to display yet.") %></p> + <% end %> +</div> diff --git a/app/views/user/wall.rhtml b/app/views/user/wall.rhtml deleted file mode 100644 index 190cc0a6d..000000000 --- a/app/views/user/wall.rhtml +++ /dev/null @@ -1,16 +0,0 @@ -<% @title = h(@display_user.name) + _(" - wall") %> -<% if @is_you %> -<div class="medium_column"> - <p><%= _('You can change the requests and users you are following on <a href="{{profile_url}}">your profile page</a>.', :profile_url => show_user_profile_path) %> - <%= render :partial => 'change_receive_email' %> -</div> -<% end %> -<div id="user_profile_search"> - <% if !@feed_results.nil? %> - <% for result in @feed_results %> - <%= render :partial => 'request/wall_listing', :locals => { :event => result, :info_request => result.info_request } %> - <% end %> - <% end %> - - -</div> diff --git a/app/views/user/wrong_user.rhtml b/app/views/user/wrong_user.html.erb index 30256a639..30256a639 100644 --- a/app/views/user/wrong_user.rhtml +++ b/app/views/user/wrong_user.html.erb diff --git a/app/views/user/wrong_user_unknown_email.html.erb b/app/views/user/wrong_user_unknown_email.html.erb new file mode 100644 index 000000000..c1967fc1f --- /dev/null +++ b/app/views/user/wrong_user_unknown_email.html.erb @@ -0,0 +1,8 @@ + +<p id="sign_in_reason"> +<%= @reason_params[:web] %>. <%= _('Unfortunately we don\'t know the FOI +email address for that authority, so we can\'t validate this. +Please <a href="{{url}}">contact us</a> to sort it out.', :url => help_contact_path.html_safe) %> +</p> + + diff --git a/app/views/user/wrong_user_unknown_email.rhtml b/app/views/user/wrong_user_unknown_email.rhtml deleted file mode 100644 index c59c56941..000000000 --- a/app/views/user/wrong_user_unknown_email.rhtml +++ /dev/null @@ -1,8 +0,0 @@ - -<p id="sign_in_reason"> -<%= @reason_params[:web] %>. <%= raw(_('Unfortunately we don\'t know the FOI -email address for that authority, so we can\'t validate this. -Please <a href="%s">contact us</a> to sort it out.') % [help_contact_path]) %> -</p> - - diff --git a/app/views/user_mailer/already_registered.rhtml b/app/views/user_mailer/already_registered.text.erb index 59ffcbf94..32c2c7e63 100644 --- a/app/views/user_mailer/already_registered.rhtml +++ b/app/views/user_mailer/already_registered.text.erb @@ -1,10 +1,10 @@ -<%= @name %>, +<%= raw @name %>, <%= _('You just tried to sign up to {{site_name}}, when you already have an account. Your name and password have been left as they previously were. -Please click on the link below.', :site_name=>site_name)%> <%=@reasons[:email]%> +Please click on the link below.', :site_name=>site_name)%> <%=raw @reasons[:email] %> <%=@url%> diff --git a/app/views/user_mailer/changeemail_already_used.rhtml b/app/views/user_mailer/changeemail_already_used.text.erb index 1d74dda35..1d74dda35 100644 --- a/app/views/user_mailer/changeemail_already_used.rhtml +++ b/app/views/user_mailer/changeemail_already_used.text.erb diff --git a/app/views/user_mailer/changeemail_confirm.rhtml b/app/views/user_mailer/changeemail_confirm.text.erb index ffb9737f7..c73e9486b 100644 --- a/app/views/user_mailer/changeemail_confirm.rhtml +++ b/app/views/user_mailer/changeemail_confirm.text.erb @@ -1,4 +1,4 @@ -<%= @name %>, +<%= raw @name %>, <%= _('Please click on the link below to confirm that you want to change the email address that you use for {{site_name}} diff --git a/app/views/user_mailer/confirm_login.rhtml b/app/views/user_mailer/confirm_login.text.erb index 6f4feff00..fa86dc2b1 100644 --- a/app/views/user_mailer/confirm_login.rhtml +++ b/app/views/user_mailer/confirm_login.text.erb @@ -1,7 +1,7 @@ -<%= @name %>, +<%= raw @name %>, <%= _('Please click on the link below to confirm your email address.')%> -<%=@reasons[:email]%> +<%= raw @reasons[:email] %> <%=@url%> |