brand labels, pcf labels, bookmark and view counts

This commit is contained in:
Cynthia Foxwell 2025-04-16 13:34:35 -06:00
parent 80cca7b070
commit b2d71407ba
No known key found for this signature in database
19 changed files with 271 additions and 145 deletions

View File

@ -1,53 +1,121 @@
@font-face {
font-family: 'fontello';
src: url('/fonts/fontello.eot?21002321');
src: url('/fonts/fontello.eot?21002321#iefix') format('embedded-opentype'),
url('/fonts/fontello.woff2?21002321') format('woff2'),
url('/fonts/fontello.woff?21002321') format('woff'),
url('/fonts/fontello.ttf?21002321') format('truetype'),
url('/fonts/fontello.svg?21002321#fontello') format('svg');
font-family: "fontello";
src: url("/fonts/fontello.eot?76162212");
src:
url("/fonts/fontello.eot?76162212#iefix") format("embedded-opentype"),
url("/fonts/fontello.woff2?76162212") format("woff2"),
url("/fonts/fontello.woff?76162212") format("woff"),
url("/fonts/fontello.ttf?76162212") format("truetype"),
url("/fonts/fontello.svg?76162212#fontello") format("svg");
font-weight: normal;
font-style: normal;
}
[class^="icon-"]:before, [class*=" icon-"]:before {
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?76162212#fontello') format('svg');
}
}
*/
[class^="icon-"]:before,
[class*=" icon-"]:before {
font-family: "fontello";
font-style: normal;
font-weight: normal;
speak: never;
display: inline-block;
text-decoration: inherit;
width: 1em;
margin-right: 0.2em;
text-align: center;
/* opacity: .8; */
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
margin-left: 0.2em;
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
.icon-heart:before { content: '\2665'; } /* '♥' */
.icon-quote:before { content: '\275e'; } /* '❞' */
.icon-comment:before { content: '\e802'; } /* '' */
.icon-ok:before { content: '\e803'; } /* '' */
.icon-play:before { content: '\e804'; } /* '' */
.icon-link:before { content: '\e805'; } /* '' */
.icon-calendar:before { content: '\e806'; } /* '' */
.icon-location:before { content: '\e807'; } /* '' */
.icon-picture:before { content: '\e809'; } /* '' */
.icon-lock:before { content: '\e80a'; } /* '' */
.icon-down:before { content: '\e80b'; } /* '' */
.icon-retweet:before { content: '\e80d'; } /* '' */
.icon-search:before { content: '\e80e'; } /* '' */
.icon-pin:before { content: '\e80f'; } /* '' */
.icon-cog:before { content: '\e812'; } /* '' */
.icon-rss-feed:before { content: '\e813'; } /* '' */
.icon-info:before { content: '\f128'; } /* '' */
.icon-bird:before { content: '\f309'; } /* '' */
.icon-heart:before {
content: "\2665";
} /* '♥' */
.icon-quote:before {
content: "\275e";
} /* '❞' */
.icon-ok:before {
content: "\e800";
} /* '' */
.icon-play:before {
content: "\e801";
} /* '' */
.icon-comment:before {
content: "\e802";
} /* '' */
.icon-link:before {
content: "\e803";
} /* '' */
.icon-calendar:before {
content: "\e804";
} /* '' */
.icon-picture:before {
content: "\e805";
} /* '' */
.icon-lock:before {
content: "\e806";
} /* '' */
.icon-down:before {
content: "\e807";
} /* '' */
.icon-retweet:before {
content: "\e808";
} /* '' */
.icon-search:before {
content: "\e809";
} /* '' */
.icon-pin:before {
content: "\e80a";
} /* '' */
.icon-cog:before {
content: "\e80b";
} /* '' */
.icon-info:before {
content: "\e80c";
} /* '' */
.icon-bookmark:before {
content: "\e80d";
} /* '' */
.icon-eye:before {
content: "\e80e";
} /* '' */
.icon-pcf:before {
content: "\e83a";
} /* '' */
.icon-location:before {
content: "\f031";
} /* '' */
.icon-bird:before {
content: "\f099";
} /* '' */
.icon-rss-feed:before {
content: "\f09e";
} /* '' */

Binary file not shown.

View File

@ -1,46 +1,52 @@
<?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>Copyright (C) 2020 by original authors @ fontello.com</metadata>
<metadata>Copyright (C) 2025 by original authors @ fontello.com</metadata>
<defs>
<font id="fontello" horiz-adv-x="1000" >
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
<missing-glyph horiz-adv-x="1000" />
<glyph glyph-name="heart" unicode="&#x2665;" d="M790 644q70-64 70-156t-70-158l-360-330-360 330q-70 66-70 158t70 156q62 58 151 58t153-58l56-52 58 52q62 58 150 58t152-58z" horiz-adv-x="860" />
<glyph glyph-name="heart" unicode="&#x2665;" d="M500-79q-14 0-25 10l-348 336q-5 5-15 15t-31 37-38 54-30 67-13 77q0 123 71 192t196 70q34 0 70-12t67-33 54-38 42-38q20 20 42 38t54 38 67 33 70 12q125 0 196-70t71-192q0-123-128-251l-347-335q-10-10-25-10z" horiz-adv-x="1000" />
<glyph glyph-name="quote" unicode="&#x275e;" d="M18 685l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z m558 0l335 0 0-334q0-140-98-238t-237-97l0 111q92 0 158 65t65 159l-223 0 0 334z" horiz-adv-x="928" />
<glyph glyph-name="ok" unicode="&#xe800;" d="M249 0q-34 0-56 28l-180 236q-16 24-12 52t26 46 51 14 47-28l118-154 296 474q16 24 43 30t53-8q24-16 30-43t-8-53l-350-560q-20-32-56-32z" horiz-adv-x="667" />
<glyph glyph-name="play" unicode="&#xe801;" d="M772 333l-741-412q-13-7-22-2t-9 20v822q0 14 9 20t22-2l741-412q13-7 13-17t-13-17z" horiz-adv-x="785.7" />
<glyph glyph-name="comment" unicode="&#xe802;" d="M1000 350q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12-10-1-17 5t-10 16v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 73 40 139t106 114 160 76 194 28q136 0 251-48t182-130 67-179z" horiz-adv-x="1000" />
<glyph glyph-name="ok" unicode="&#xe803;" d="M0 260l162 162 166-164 508 510 164-164-510-510-162-162-162 164z" horiz-adv-x="1000" />
<glyph glyph-name="link" unicode="&#xe803;" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" />
<glyph glyph-name="play" unicode="&#xe804;" d="M772 333l-741-412q-13-7-22-2t-9 20v822q0 14 9 20t22-2l741-412q13-7 13-17t-13-17z" horiz-adv-x="785.7" />
<glyph glyph-name="calendar" unicode="&#xe804;" d="M800 700q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l46 0 0-100 160 0 0 100 290 0 0-100 160 0 0 100 44 0z m0-700l0 400-700 0 0-400 700 0z m-540 800l0-170-70 0 0 170 70 0z m450 0l0-170-70 0 0 170 70 0z" horiz-adv-x="900" />
<glyph glyph-name="link" unicode="&#xe805;" d="M294 116q14 14 34 14t36-14q32-34 0-70l-42-40q-56-56-132-56-78 0-134 56t-56 132q0 78 56 134l148 148q70 68 144 77t128-43q16-16 16-36t-16-36q-36-32-70 0-50 48-132-34l-148-146q-26-26-26-64t26-62q26-26 63-26t63 26z m450 574q56-56 56-132 0-78-56-134l-158-158q-74-72-150-72-62 0-112 50-14 14-14 34t14 36q14 14 35 14t35-14q50-48 122 24l158 156q28 28 28 64 0 38-28 62-24 26-56 31t-60-21l-50-50q-16-14-36-14t-34 14q-34 34 0 70l50 50q54 54 127 51t129-61z" horiz-adv-x="800" />
<glyph glyph-name="picture" unicode="&#xe805;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="calendar" unicode="&#xe806;" d="M800 700q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-700 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l46 0 0-100 160 0 0 100 290 0 0-100 160 0 0 100 44 0z m0-700l0 400-700 0 0-400 700 0z m-540 800l0-170-70 0 0 170 70 0z m450 0l0-170-70 0 0 170 70 0z" horiz-adv-x="900" />
<glyph glyph-name="lock" unicode="&#xe806;" d="M0 350q0 207 147 354t353 146 354-146 146-354-146-354-354-146-353 146-147 354z m252-271l496 0 0 314-82 0 0 59q0 33-14 66-19 45-62 74t-94 30-92-29-62-75q-16-35-14-125l-76 0 0-314z m176 314l0 59q2 31 19 49t45 21l4 0q29-2 49-22t21-48l0-59-138 0z" horiz-adv-x="1000" />
<glyph glyph-name="location" unicode="&#xe807;" d="M250 750q104 0 177-73t73-177q0-106-62-243t-126-223l-62-84q-10 12-27 35t-60 89-76 130-60 147-27 149q0 104 73 177t177 73z m0-388q56 0 96 40t40 96-40 95-96 39-95-39-39-95 39-96 95-40z" horiz-adv-x="500" />
<glyph glyph-name="down" unicode="&#xe807;" d="M939 399l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
<glyph glyph-name="picture" unicode="&#xe809;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="retweet" unicode="&#xe808;" d="M714 11q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" />
<glyph glyph-name="lock" unicode="&#xe80a;" d="M0 350q0 207 147 354t353 146 354-146 146-354-146-354-354-146-353 146-147 354z m252-271l496 0 0 314-82 0 0 59q0 33-14 66-19 45-62 74t-94 30-92-29-62-75q-16-35-14-125l-76 0 0-314z m176 314l0 59q2 31 19 49t45 21l4 0q29-2 49-22t21-48l0-59-138 0z" horiz-adv-x="1000" />
<glyph glyph-name="search" unicode="&#xe809;" d="M643 386q0 103-73 176t-177 74-177-74-73-176 73-177 177-73 177 73 73 177z m286-465q0-29-22-50t-50-21q-30 0-50 21l-191 191q-100-69-223-69-80 0-153 31t-125 84-84 125-31 153 31 152 84 126 125 84 153 31 153-31 125-84 84-126 31-152q0-123-69-223l191-191q21-21 21-51z" horiz-adv-x="928.6" />
<glyph glyph-name="down" unicode="&#xe80b;" d="M939 399l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
<glyph glyph-name="pin" unicode="&#xe80a;" d="M573 37q0-23-15-38t-37-15q-21 0-37 16l-169 169-315-236 236 315-168 169q-24 23-12 56 14 32 48 32 157 0 270 57 90 45 151 171 9 24 36 32t50-13l208-209q21-23 14-50t-32-36q-127-63-172-152-56-110-56-268z" horiz-adv-x="834" />
<glyph glyph-name="retweet" unicode="&#xe80d;" d="M714 11q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" />
<glyph glyph-name="cog" unicode="&#xe80b;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
<glyph glyph-name="search" unicode="&#xe80e;" d="M772 78q30-34 6-62l-46-46q-36-32-68 0l-190 190q-74-42-156-42-128 0-223 95t-95 223 90 219 218 91 224-95 96-223q0-88-46-162z m-678 358q0-88 68-156t156-68 151 63 63 153q0 88-68 155t-156 67-151-63-63-151z" horiz-adv-x="789" />
<glyph glyph-name="info" unicode="&#xe80c;" d="M494 740q86-62 86-184 0-64-42-124-12-20-88-80l-46-30q-40-34-48-60-6-16-8-44 0-14-16-14l-128 0q-16 0-16 12 4 98 28 124 16 22 48 48t56 42l24 14q22 16 34 34 28 44 28 70 0 40-26 78-28 36-92 36-68 0-94-44-28-42-28-92l-166 0q6 162 114 232 70 42 166 42 130 0 214-60z m-216-636q44 0 73-30t27-74q-2-46-32-73t-74-25q-44 0-73 29t-27 75 32 73 74 25z" horiz-adv-x="580" />
<glyph glyph-name="pin" unicode="&#xe80f;" d="M268 368v250q0 8-5 13t-13 5-13-5-5-13v-250q0-8 5-13t13-5 13 5 5 13z m375-197q0-14-11-25t-25-10h-239l-29-270q-1-7-6-11t-11-5h-1q-15 0-17 15l-43 271h-225q-15 0-25 10t-11 25q0 69 44 124t99 55v286q-29 0-50 21t-22 50 22 50 50 22h357q29 0 50-22t21-50-21-50-50-21v-286q55 0 99-55t44-124z" horiz-adv-x="642.9" />
<glyph glyph-name="bookmark" unicode="&#xe80d;" d="M650 779q12 0 24-5 19-8 29-23t11-35v-719q0-19-11-35t-29-23q-10-4-24-4-27 0-47 18l-246 236-246-236q-20-19-46-19-13 0-25 5-18 7-29 23t-11 35v719q0 19 11 35t29 23q12 5 25 5h585z" horiz-adv-x="714.3" />
<glyph glyph-name="cog" unicode="&#xe812;" d="M911 295l-133-56q-8-22-12-31l55-133-79-79-135 53q-9-4-31-12l-55-134-112 0-56 133q-11 4-33 13l-132-55-78 79 53 134q-1 3-4 9t-6 12-4 11l-131 55 0 112 131 56 14 33-54 132 78 79 133-54q22 9 33 13l55 132 112 0 56-132q14-5 31-13l133 55 80-79-54-135q6-12 12-30l133-56 0-112z m-447-111q69 0 118 48t49 118-49 119-118 50-119-50-49-119 49-118 119-48z" horiz-adv-x="928" />
<glyph glyph-name="eye" unicode="&#xe80e;" d="M0 350q6 49 64 110 79 80 176 129 129 60 260 60 137-2 260-60 103-53 176-129 64-73 64-110-6-49-64-109-79-80-176-129-129-61-260-61-137 2-260 61-103 53-176 129-64 72-64 109z m264 0q0-94 69-159t167-65 167 65 69 159-69 159-167 66-167-66-69-159z m86 1q0 60 44 102t106 42 106-42 44-102-44-102-106-43-106 43-44 102z" horiz-adv-x="1000" />
<glyph glyph-name="rss-feed" unicode="&#xe813;" d="M184 93c0-51-43-91-93-91s-91 40-91 91c0 50 41 91 91 91s93-41 93-91z m261-85l-125 0c0 174-140 323-315 323l0 118c231 0 440-163 440-441z m259 0l-136 0c0 300-262 561-563 561l0 129c370 0 699-281 699-690z" horiz-adv-x="704" />
<glyph glyph-name="pcf" unicode="&#xe83a;" d="M50 800c-24 0-50-25-50-50l0-350c0-111 50-250 250-250 18 0 34 1 50 3l0 147-200 0s25 100 150 100c25 0 35-4 50-9l0 109c0 46 18 84 50 100 50 25 70 15 100 0 50-25 50-25 50-25l0 175c0 25-26 50-50 50-50 0-100-50-200-50-100 0-150 50-200 50z m88-175c34 0 62-28 62-62 0-35-28-63-62-63-35 0-63 28-63 63 0 34 28 62 63 62z m262-75c-24 0-50-25-50-50l0-350c0-111 50-250 250-250 200 0 250 139 250 250l0 350c0 25-26 50-50 50-50 0-100-50-200-50-100 0-150 50-200 50z m88-175c34 0 62-28 62-62 0-35-28-63-62-63-35 0-63 28-63 63 0 34 28 62 63 62z m225 0c34 0 62-28 62-62 0-35-28-63-62-63-35 0-63 28-63 63 0 34 28 62 63 62z m-263-275l300 0s-25-100-150-100-150 100-150 100z" horiz-adv-x="850" />
<glyph glyph-name="info" unicode="&#xf128;" d="M393 149v-134q0-9-7-15t-15-7h-134q-9 0-16 7t-7 15v134q0 9 7 16t16 6h134q9 0 15-6t7-16z m176 335q0-30-8-56t-20-43-31-33-32-25-34-19q-23-13-38-37t-15-37q0-10-7-18t-16-9h-134q-8 0-14 11t-6 20v26q0 46 37 87t79 60q33 16 47 32t14 42q0 24-26 41t-60 18q-36 0-60-16-20-14-60-64-7-9-17-9-7 0-14 4l-91 70q-8 6-9 14t3 16q89 148 259 148 45 0 90-17t81-46 59-72 23-88z" horiz-adv-x="571.4" />
<glyph glyph-name="location" unicode="&#xf031;" d="M0 473q0 70 27 134 25 61 74 111 47 47 110 73 63 27 134 27 72 0 135-27 62-26 110-73 48-49 74-111 27-64 27-134t-27-134q-18-40-36-65l-229-347q-22-35-53-35t-55 35l-228 347q-22 31-36 65-27 64-27 134z m217 0q0-53 37-91 38-38 91-38 54 0 91 38 38 37 38 91 0 53-38 91-37 38-91 38-53 0-91-38-37-38-37-91z" horiz-adv-x="691.4" />
<glyph glyph-name="bird" unicode="&#xf309;" d="M920 636q-36-54-94-98l0-24q0-130-60-250t-186-203-290-83q-160 0-290 84 14-2 46-2 132 0 234 80-62 2-110 38t-66 94q10-4 34-4 26 0 50 6-66 14-108 66t-42 120l0 2q36-20 84-24-84 58-84 158 0 48 26 94 154-188 390-196-6 18-6 42 0 78 55 133t135 55q82 0 136-58 60 12 120 44-20-66-82-104 56 8 108 30z" horiz-adv-x="920" />
<glyph glyph-name="bird" unicode="&#xf099;" d="M904 622q-37-54-90-93 0-8 0-23 0-73-21-145t-64-139-103-117-144-82-181-30q-151 0-276 81 19-2 43-2 126 0 224 77-59 1-105 36t-64 89q19-3 34-3 24 0 48 6-63 13-104 62t-41 115v2q38-21 82-23-37 25-59 64t-22 86q0 49 25 91 68-83 164-133t208-55q-5 21-5 41 0 75 53 127t127 53q79 0 132-57 61 12 115 44-21-64-80-100 52 6 104 28z" horiz-adv-x="928.6" />
<glyph glyph-name="rss-feed" unicode="&#xf09e;" d="M214 100q0-45-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m286-69q1-15-9-26-10-12-27-12h-75q-14 0-24 9t-11 23q-12 128-103 219t-219 103q-14 1-23 11t-9 24v75q0 16 12 26 9 10 24 10h3q89-7 170-45t145-101q63-63 101-145t45-171z m286-1q1-15-10-26-10-11-26-11h-80q-14 0-25 10t-10 23q-7 120-57 228t-129 188-188 129-227 57q-14 1-24 11t-10 24v80q0 16 11 26 10 10 25 10h1q147-8 280-67t238-164q104-104 164-238t67-280z" horiz-adv-x="785.7" />
</font>
</defs>
</svg>
</svg>

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -17,8 +17,8 @@ const
graphql = api / "graphql"
graphUser* = graphql / "32pL5BWe9WKeSK1MoPvFQQ/UserByScreenName"
graphUserById* = graphql / "5vdJ5sWkbSRDiiNZvwc2Yg/UserByRestId"
graphUserTweets* = graphql / "3JNH4e9dq1BifLxAa3UMWg/UserWithProfileTweetsQueryV2"
graphUserTweetsAndReplies* = graphql / "8IS8MaO-2EN6GZZZb8jF0g/UserWithProfileTweetsAndRepliesQueryV2"
graphUserTweets* = graphql / "M3Hpkrb8pjWkEuGdLeXMOA/UserTweets"
graphUserTweetsAndReplies* = graphql / "pz0IHaV_t7T4HJavqqqcIA/UserTweetsAndReplies"
graphUserMedia* = graphql / "dexO_2tohK86JDudXXG3Yw/UserMedia"
graphTweet* = graphql / "y90SwUGBZ3yz0yNUmCHgTw/TweetDetail"
graphTweetResult* = graphql / "sITyJdhRPpvpEjg4waUmTA/TweetResultByIdQuery"
@ -95,7 +95,7 @@ const
"unified_cards_ad_metadata_container_dynamic_card_content_query_enabled": false,
"verified_phone_label_enabled": false,
"vibe_api_enabled": false,
"view_counts_everywhere_api_enabled": false,
"view_counts_everywhere_api_enabled": true,
"premium_content_api_read_enabled": false,
"responsive_web_grok_analyze_button_fetch_trends_enabled": false,
"responsive_web_grok_analysis_button_from_backend": false,
@ -108,7 +108,8 @@ const
"responsive_web_twitter_article_notes_tab_enabled": false,
"subscriptions_feature_can_gift_premium": false,
"hidden_profile_subscriptions_enabled": true,
"subscriptions_verification_info_is_identity_verified_enabled": true
"subscriptions_verification_info_is_identity_verified_enabled": true,
"responsive_web_grok_show_grok_translated_post": false
}""".replace(" ", "").replace("\n", "")
tweetVariables* = """{
@ -131,9 +132,13 @@ const
}""".replace(" ", "").replace("\n", "")
userTweetsVariables* = """{
"rest_id": "$1",
"userId": "$1",
$2
"count": 20
"count": 20,
"includePromotedContent": false,
"withCommunity": true,
"withQuickPromoteEligibilityTweetFields": false,
"withVoice": true
}""".replace(" ", "").replace("\n", "")
listTweetsVariables* = """{

View File

@ -7,6 +7,7 @@ const
cards = "cards.twitter.com/cards"
tco = "https://t.co"
twitter = parseUri("https://twitter.com")
sameProto = "//"
let
twRegex = re"(?<=(?<!\S)https:\/\/|(?<=\s))(www\.|mobile\.)?(twitter|x)\.com"
@ -31,7 +32,10 @@ let
illegalXmlRegex = re"(*UTF8)[^\x09\x0A\x0D\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]"
proc getUrlPrefix*(cfg: Config): string =
"https://" & cfg.hostname
var proto = "http"
if cfg.useHttps: proto &= "s"
proto &= "://"
result = proto & cfg.hostname
proc shortLink*(text: string; length=28): string =
result = text.replace(wwwRegex, "")
@ -62,8 +66,9 @@ proc replaceUrls*(body: string; prefs: Prefs; absolute=""): string =
result = result.replace(tco, https & prefs.replaceTwitter & "/t.co")
result = result.replace(cards, prefs.replaceTwitter & "/cards")
result = result.replace(twRegex, prefs.replaceTwitter)
result = result.replace("https:", "")
result = result.replacef(twLinkRegex, a(
prefs.replaceTwitter & "$4", href = https & prefs.replaceTwitter & "$2"))
prefs.replaceTwitter & "$4", href = prefs.replaceTwitter & "$2"))
if prefs.replaceReddit.len > 0 and ("reddit.com" in result or "redd.it" in result):
result = result.replace(rdShortRegex, prefs.replaceReddit & "/comments/")

View File

@ -14,7 +14,7 @@ import routes/[
activityspoof]
const instancesUrl = "https://github.com/zedeus/nitter/wiki/Instances"
const issuesUrl = "https://github.com/zedeus/nitter/issues"
const issuesUrl = "https://gitlab.com/Cynosphere/nitter/issues"
#let accountsPath = getEnv("NITTER_ACCOUNTS_FILE", "./guest_accounts.json")
@ -80,7 +80,7 @@ routes:
error InternalError:
echo error.exc.name, ": ", error.exc.msg
const link = a("open a GitHub issue", href = issuesUrl)
const link = a("open an issue", href = issuesUrl)
resp Http500, showError(
&"An error occurred, please {link} with the URL you tried to visit.", cfg)

View File

@ -39,14 +39,28 @@ proc parseGraphUser*(js: JsonNode): User =
result = parseUser(user{"legacy"}, user{"rest_id"}.getStr)
let label = user{"affiliates_highlighted_label", "label"}
if not label.isNull and label{"userLabelType"}.getStr == "AutomatedLabel":
result.bot = true
let entities = label{"longDescription", "entities"}
if not entities.isNull:
for ent in entities:
if ent{"ref", "type"}.getStr != "TimelineRichTextMention": continue
result.botOwner = ent{"ref", "screen_name"}.getStr
break
if not label.isNull:
let labelType = label{"userLabelType"}.getStr
if labelType == "AutomatedLabel":
result.bot = true
let entities = label{"longDescription", "entities"}
if not entities.isNull:
for ent in entities:
if ent{"ref", "type"}.getStr != "TimelineRichTextMention": continue
result.botOwner = ent{"ref", "screen_name"}.getStr
break
# TODO: are there other types than the two
# TODO: find profile with "userLabelDisplayType" not equal to "Badge"
elif labelType == "BusinessLabel":
result.badge = Badge(
name: label{"description"}.getStr,
icon: label{"badge", "url"}.getStr,
url: label{"url", "url"}.getStr
)
let pcf = user{"parody_commentary_fan_label"}.getStr
if pcf.len > 0 and pcf != "None":
result.pcf = pcf
if result.verifiedType == VerifiedType.none and user{"is_blue_verified"}.getBool(false):
result.verifiedType = blue
@ -230,7 +244,8 @@ proc parseTweet(js: JsonNode; jsCard: JsonNode = newJNull()): Tweet =
replies: js{"reply_count"}.getInt,
retweets: js{"retweet_count"}.getInt,
likes: js{"favorite_count"}.getInt,
quotes: js{"quote_count"}.getInt
quotes: js{"quote_count"}.getInt,
bookmarks: js{"bookmark_count"}.getInt
)
)
@ -491,6 +506,11 @@ proc parseGraphTweet(js: JsonNode; isLegacy=false): Tweet =
note.expandBirdwatchEntities(communityNote{"subtitle"})
result.birdwatch = some(note)
if not js{"views", "count"}.isNull:
result.stats.views = parseInt(js{"views", "count"}.getStr)
else:
result.stats.views = -1
proc parseGraphThread(js: JsonNode): tuple[thread: Chain; self: bool] =
for t in js{"content", "items"}:
let entryId = t{"entryId"}.getStr
@ -562,28 +582,12 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile =
let instructions =
if root == "list": ? js{"data", "list", "timeline_response", "timeline", "instructions"}
elif root == "user": ? js{"data", "user_result", "result", "timeline_response", "timeline", "instructions"}
else: ? js{"data", "user", "result", "timeline", "timeline", "instructions"}
if instructions.len == 0:
return
for i in instructions:
if i{"__typename"}.getStr == "TimelineAddEntries":
for e in i{"entries"}:
let entryId = e{"entryId"}.getStr
if entryId.startsWith("tweet"):
with tweetResult, e{"content", "content", "tweetResult", "result"}:
let tweet = parseGraphTweet(tweetResult, false)
if not tweet.available:
tweet.id = entryId.getId()
result.tweets.content.add tweet
elif "-conversation-" in entryId or entryId.startsWith("homeConversation"):
let (thread, self) = parseGraphThread(e)
result.tweets.content.add thread.content
elif entryId.startsWith("cursor-bottom"):
result.tweets.bottom = e{"content", "value"}.getStr
# TODO cleanup
if i{"type"}.getStr == "TimelineAddEntries":
for e in i{"entries"}:
let entryId = e{"entryId"}.getStr
@ -598,8 +602,8 @@ proc parseGraphTimeline*(js: JsonNode; root: string; after=""): Profile =
result.tweets.content.add thread.content
elif entryId.startsWith("cursor-bottom"):
result.tweets.bottom = e{"content", "value"}.getStr
if after.len == 0 and i{"__typename"}.getStr == "TimelinePinEntry":
with tweetResult, i{"entry", "content", "content", "tweetResult", "result"}:
if after.len == 0 and i{"type"}.getStr == "TimelinePinEntry":
with tweetResult, i{"entry", "content", "itemContent", "tweet_results", "result"}:
let tweet = parseGraphTweet(tweetResult, false)
tweet.pinned = true
if not tweet.available and tweet.tombstone.len == 0:

View File

@ -1,39 +1,49 @@
@import '_variables';
@import '_mixins';
@import "_variables";
@import "_mixins";
.panel-container {
margin: auto;
font-size: 130%;
margin: auto;
font-size: 130%;
}
.error-panel {
@include center-panel(var(--error_red));
text-align: center;
@include center-panel(var(--error_red));
text-align: center;
}
.search-bar > form {
@include center-panel(var(--darkest_grey));
@include center-panel(var(--darkest_grey));
button {
background: var(--bg_elements);
color: var(--fg_color);
border: 0;
border-radius: 3px;
cursor: pointer;
font-weight: bold;
width: 30px;
height: 30px;
}
button {
background: var(--bg_elements);
color: var(--fg_color);
border: 0;
border-radius: 3px;
cursor: pointer;
font-weight: bold;
width: 30px;
height: 30px;
}
input {
font-size: 16px;
width: 100%;
background: var(--bg_elements);
color: var(--fg_color);
border: 0;
border-radius: 4px;
padding: 4px;
margin-right: 8px;
height: unset;
}
input {
font-size: 16px;
width: 100%;
background: var(--bg_elements);
color: var(--fg_color);
border: 0;
border-radius: 4px;
padding: 4px;
margin-right: 8px;
height: unset;
}
}
.brand-badge {
margin-left: 4px;
}
.brand-badge-image {
width: 16px;
height: 16px;
border: 1px solid var(--accent_border);
margin-bottom: -4px;
}

View File

@ -83,7 +83,8 @@
.profile-joindate,
.profile-location,
.profile-website,
.profile-automated {
.profile-automated,
.profile-pcf {
color: var(--fg_faded);
margin: 1px 0;
width: 100%;

View File

@ -47,6 +47,11 @@
display: flex;
justify-content: space-between;
}
.tweet-label-row {
padding: 0;
display: flex;
gap: 0.4em;
}
.fullname-and-username {
display: flex;
@ -69,10 +74,10 @@
word-wrap: normal;
}
.user-automated {
.user-automated,
.user-pcf {
@include ellipsis;
min-width: 1px;
margin-left: 0.4em;
color: var(--fg_faded);
}

View File

@ -80,6 +80,11 @@ type
business = "Business"
government = "Government"
Badge* = object
name*: string
icon*: string
url*: string
User* = object
id*: string
username*: string
@ -101,6 +106,8 @@ type
joinDate*: DateTime
bot*: bool
botOwner*: string
pcf*: string
badge*: Badge
VideoType* = enum
m3u8 = "application/x-mpegURL"
@ -203,6 +210,8 @@ type
retweets*: int
likes*: int
quotes*: int
bookmarks*: int
views*: int
BirdwatchNote* = ref object
id*: string

View File

@ -55,7 +55,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
buildHtml(head):
link(rel="stylesheet", type="text/css", href="/css/style.css?v=20")
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=2")
link(rel="stylesheet", type="text/css", href="/css/fontello.css?v=3")
link(rel="stylesheet", href="/css/baguetteBox.min.css")
script(src="/js/baguetteBox.min.js", `async`="")
script(src="/js/zoom.js")
@ -183,7 +183,7 @@ proc renderHead*(prefs: Prefs; cfg: Config; req: Request; titleText=""; desc="";
# this is last so images are also preloaded
# if this is done earlier, Chrome only preloads one image for some reason
link(rel="preload", type="font/woff2", `as`="font",
href="/fonts/fontello.woff2?21002321", crossorigin="anonymous")
href="/fonts/fontello.woff2?76162212", crossorigin="anonymous")
proc renderMain*(body: VNode; req: Request; cfg: Config; prefs=defaultPrefs;
titleText=""; desc=""; ogTitle=""; rss=""; video="";

View File

@ -26,8 +26,8 @@ proc renderUserCard*(user: User; prefs: Prefs; path: string): VNode =
tdiv(class="profile-card-tabs-name-and-follow"):
tdiv():
linkUser(user, class="profile-card-fullname")
linkUser(user, class="profile-card-username")
linkUser(user, class="profile-card-fullname", prefs)
linkUser(user, class="profile-card-username", prefs)
let following = isFollowing(user.username, prefs.following)
if not following:
buttonReferer "/follow/" & user.username, "Follow", path, "profile-card-follow-button"
@ -44,6 +44,11 @@ proc renderUserCard*(user: User; prefs: Prefs; path: string): VNode =
else:
icon "cog", "Automated"
if user.pcf.len > 0:
tdiv(class="profile-pcf"):
span:
icon "pcf", &"{user.pcf} account"
if user.bio.len > 0:
tdiv(class="profile-bio"):
p(dir="auto"):

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
import strutils, strformat
import karax/[karaxdsl, vdom, vstyles]
import ".."/[types, utils]
import ".."/[types, utils, formatters]
const smallWebp* = "?name=small&format=webp"
@ -30,7 +30,7 @@ template verifiedIcon*(user: User): untyped {.dirty.} =
else:
text ""
proc linkUser*(user: User, class=""): VNode =
proc linkUser*(user: User, class="", prefs: Prefs): VNode =
let
isName = "username" notin class
href = "/" & user.username
@ -44,6 +44,10 @@ proc linkUser*(user: User, class=""): VNode =
if user.protected:
text " "
icon "lock", title="Protected account"
if user.badge.name.len > 0:
span(class="brand-badge"):
a(href=replaceUrls(user.badge.url, prefs), title=user.badge.name):
img(class="brand-badge-image", src=user.badge.icon, alt=user.badge.name)
proc linkText*(text: string; class=""): VNode =
let url = if "http" notin text: https & text else: text

View File

@ -74,8 +74,8 @@ proc renderUser*(user: User; prefs: Prefs): VNode =
tdiv(class="tweet-name-row"):
tdiv(class="fullname-and-username"):
linkUser(user, class="fullname")
linkUser(user, class="username")
linkUser(user, class="fullname", prefs)
linkUser(user, class="username", prefs)
tdiv(class="tweet-content media-body", dir="auto"):
verbatim replaceUrls(user.bio, prefs)

View File

@ -15,32 +15,40 @@ proc renderMiniAvatar(user: User; prefs: Prefs): VNode =
img(class=(prefs.getAvatarClass & " mini"), src=url)
proc renderHeader(tweet: Tweet; retweet: string; pinned: bool; prefs: Prefs): VNode =
let user = tweet.user
buildHtml(tdiv):
if pinned:
tdiv(class="pinned"):
span: icon "pin", "Pinned Tweet"
span: icon "pin", "Pinned"
elif retweet.len > 0:
tdiv(class="retweet-header"):
span: icon "retweet", retweet & " retweeted"
tdiv(class="tweet-header"):
a(class="tweet-avatar", href=("/" & tweet.user.username)):
a(class="tweet-avatar", href=("/" & user.username)):
var size = "_bigger"
if not prefs.autoplayGifs and tweet.user.userPic.endsWith("gif"):
if not prefs.autoplayGifs and user.userPic.endsWith("gif"):
size = "_400x400"
genImg(tweet.user.getUserPic(size), class=prefs.getAvatarClass)
genImg(user.getUserPic(size), class=prefs.getAvatarClass)
tdiv(class="tweet-name-row"):
tdiv(class="fullname-and-username"):
linkUser(tweet.user, class="fullname")
linkUser(tweet.user, class="username")
if tweet.user.bot:
tdiv(class="user-automated"): icon "cog", "Automated"
linkUser(user, class="fullname", prefs)
linkUser(user, class="username", prefs)
span(class="tweet-date"):
a(href=getLink(tweet), title=tweet.getTime):
text tweet.getShortTime
if user.pcf.len > 0 or user.bot:
tdiv(class="tweet-label-row"):
if user.bot:
tdiv(class="user-automated"): icon "cog", "Automated"
if user.pcf.len > 0:
tdiv(class="user-pcf"): icon "pcf", &"{user.pcf} account"
proc renderAlbum(tweet: Tweet): VNode =
let
groups = if tweet.photos.len < 3: @[tweet.photos]
@ -185,7 +193,7 @@ func formatStat(stat: int): string =
if stat > 0: insertSep($stat, ',')
else: ""
proc renderStats(stats: TweetStats; views: string; tweet: Tweet): VNode =
proc renderStats(stats: TweetStats; tweet: Tweet): VNode =
buildHtml(tdiv(class="tweet-stats")):
a(href=getLink(tweet)):
span(class="tweet-stat"): icon "comment", formatStat(stats.replies)
@ -193,11 +201,10 @@ proc renderStats(stats: TweetStats; views: string; tweet: Tweet): VNode =
span(class="tweet-stat"): icon "retweet", formatStat(stats.retweets)
a(href="/search?q=quoted_tweet_id:" & $tweet.id):
span(class="tweet-stat"): icon "quote", formatStat(stats.quotes)
a():
span(class="tweet-stat"): icon "heart", formatStat(stats.likes)
a(href=getLink(tweet)):
if views.len > 0:
span(class="tweet-stat"): icon "play", insertSep(views, ',')
a(): span(class="tweet-stat"): icon "heart", formatStat(stats.likes)
a(): span(class="tweet-stat"): icon "bookmark", formatStat(stats.bookmarks)
if stats.views > -1:
a(): span(class="tweet-stat"): icon "eye", formatStat(stats.views)
proc renderReply(tweet: Tweet): VNode =
buildHtml(tdiv(class="replying-to")):
@ -248,8 +255,8 @@ proc renderQuote(quote: Tweet; prefs: Prefs; path: string): VNode =
tdiv(class="tweet-name-row"):
tdiv(class="fullname-and-username"):
renderMiniAvatar(quote.user, prefs)
linkUser(quote.user, class="fullname")
linkUser(quote.user, class="username")
linkUser(quote.user, class="fullname", prefs)
linkUser(quote.user, class="username", prefs)
span(class="tweet-date"):
a(href=getLink(quote), title=quote.getTime):
@ -322,7 +329,6 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
a(class="tweet-link", href=getLink(tweet))
tdiv(class="tweet-body"):
var views = ""
renderHeader(tweet, retweet, pinned, prefs)
if not afterTweet and index == 0 and tweet.reply.len > 0 and
@ -347,10 +353,8 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
if tweet.video.isSome:
renderVideo(tweet.video.get(), prefs, path)
views = tweet.video.get().views
elif tweet.gif.isSome:
renderGif(tweet.gif.get(), prefs)
views = "GIF"
if tweet.poll.isSome:
renderPoll(tweet.poll.get())
@ -368,7 +372,7 @@ proc renderTweet*(tweet: Tweet; prefs: Prefs; path: string; class=""; index=0;
renderMediaTags(tweet.mediaTags)
if not prefs.hideTweetStats:
renderStats(tweet.stats, views, tweet)
renderStats(tweet.stats, tweet)
if showThread:
a(class="show-thread", href=("/i/status/" & $tweet.threadId)):