Pm/Html2.pm
Copying Source is Forbidden
5699 lines of code
1
use HTML::Entities;
2
3
package Html2;
4
5
#/
6
# a bigger, badder, bolder, faster Html.pm<br>
7
8
# damn, i wish i had thought of hash as a dynamic
9
# mechanism of passing function parameters sooner!<br>
10
11
# all functions use the <a href="#tag">tag</a> subroutine
12
# most named functions use the same parameter (or none at all, eg: br),
13
# and are generally just shorthand.<br>
14
15
# wouldn't it be cool if the tag subroutine could detect browser
16
# support for a given html element/attribute? if it's not supported,
17
# use an appropriate fallback. could it be done? i bet it could.
18
# and, further, can it be automated? of course it could!<br>
19
20
# the entire goal of this module will be to slowly upgrade the other
21
# modules which have html to use this module's subroutines instead,
22
# and to eventually completely deprecate Html.pm.
23
# see, most of Html.pm is hard coded HTML, which is very prone to
24
# errors (such as missing ending-tags). this module will solve that
25
# problem, at the very least. so far so good, too. most minor
26
# glitches in HTML are fading away. thousands of lines more to go.<br>
27
28
# as of May 1, 2021, Html2.pm is currently HTML 5 capable.<br>
29
#/
30
31
#CHLOG
32
# May 1, 2021
33
# - Added change log, modified module description
34
# - added several new subroutines:
35
# - <a href='#holy_grail'>holy_grail(\%)</a> and
36
# - <a href="#holy_grail_alternate">holy_grail_alternate(\%)</a><br>
37
#
38
# May 4, 2021
39
# - Modified module and some subroutine descriptions
40
#
41
# March 15, 2025
42
# - added missing documentation for Subdesc.pm to read
43
#CHLOG
44
45
binmode(STDIN, ":utf8");
46
binmode(STDOUT, ":utf8");
47
48
use strict;
49
use warnings;
50
use CGI::Carp qw(fatalsToBrowser);
51
use Exporter;
52
use vars qw($VERSION @ISA @EXPORT_OK @EXPORT);
53
use CGI;
54
use URI::Escape;
55
56
$VERSION = 1.00;
57
@ISA = qw(Exporter);
58
59
##########################
60
#use statements here
61
use lib "./Pm";
62
63
require Bc_misc;
64
require Bc_sql;
65
require User;
66
require Date;
67
require Likes;
68
69
##########################
70
71
my $MAX_NUM = "9999999999.99";
72
my $MAX_INT = "9999999999";
73
74
my $TABLE_BORDER = 0;
75
my $DEBUG = 0;
76
my $debugpl = 0;
77
$debugpl = 1 if ($0 =~ /\/debug\.pl/);
78
79
########################
80
push @EXPORT_OK, "output_http_header";
81
sub output_http_header(;$$$) {
82
#*
83
# creates an HTTP header
84
#*
85
my ($settings, $bare, $doc) = @_; # settings && bare && document
86
87
my $default_origin = "sameorigin";
88
my $default_status = "200 ok";
89
my $default_type = "text/html";
90
91
if (!defined $settings->{type}) {
92
$settings->{type} = $default_type;
93
}
94
95
my $output;
96
97
if ($bare) {
98
$output = "status: $default_status\n";
99
$output .= "origin: $default_origin\n";
100
$output .= "content-type: $settings->{type};charset=utf-8\n\n";
101
}
102
else {
103
my $max_age = get_max_age($settings);
104
my $e_tag = calculate_e_tag($settings, $doc);
105
106
$output = "etag: \"$e_tag\"\n";
107
$output .= "cache-control: max-age=$max_age, no-cache, must-validate, post-check=0, pre-check=0\n";
108
$output .= "x-content-type-options: nosniff\n";
109
110
if (defined $settings->{origin}) {
111
$output .= "x-frame-options: $settings->{origin}\n";
112
}
113
else {
114
$output .= "x-frame-options: $default_origin\n";
115
}
116
117
if (defined $settings->{status}) {
118
$output .= "status: $settings->{status}\n";
119
}
120
else {
121
$output .= "status: $default_status\n";
122
}
123
124
if (defined $settings->{cookies}) {
125
$output .= "$settings->{cookies}\n";
126
}
127
128
$output .= "content-type: $settings->{type};charset=utf-8\n\n";
129
}
130
131
return $output; # a scalar
132
#usage: print output_http_header({type=>"text/css"}, $css);
133
#test=output_http_header({type=>"text/plain"}, "hello, plain text!");
134
}
135
136
########################
137
sub get_max_age($) {
138
#*
139
# !this function's purpose is currently unknown
140
#*
141
142
#SUBCHLOG
143
# March 15, 2025
144
# added documentation for Subdesc.pm to read
145
# not sure why this function even exists
146
#SUBCHLOG
147
my ($settings) = @_; # settings
148
my $max_age;
149
150
if (defined $settings->{max_age}) {
151
$max_age = $settings->{max_age};
152
}
153
else {
154
$max_age = 86400 * 30;
155
}
156
157
return $max_age; # a scalar
158
#usage: print get_max_age();
159
#test=get_max_age();
160
}
161
162
########################
163
sub calculate_e_tag(;$$) {
164
#*
165
# to generate an etag, for HTTP headers
166
#*
167
my ($settings, $doc) = @_; # settings (optional) && document (optional)
168
my $e_tag;
169
170
if (defined $doc) {
171
my @hash = split("", $doc);
172
my $sum = 0;
173
174
foreach my $char (@hash) {
175
$sum += ord($char);
176
}
177
178
$e_tag = $sum;
179
}
180
else {
181
$e_tag = Bc_misc::new_id(256);
182
}
183
184
return $e_tag; # a scalar
185
#usage: my $etag = calculate_e_tag();
186
#test=calculate_e_tag();
187
}
188
189
########################
190
push @EXPORT_OK, "endless";
191
sub endless($) {
192
#*
193
# does the given "tag" have a corresponding end tag?
194
# not meant for general use. used internally
195
# by the <a href="#tag">tag</a> subroutine
196
#*
197
my ($tag) = @_; # a tag name (scalar only)
198
199
# Define a regular expression to match end-less tags
200
my $endless_regex = qr/^(img|input|hr|area|link|br|meta|base|col|embed|keygen|param|source|track|wbr)$/i;
201
202
# Check if the provided tag matches the regular expression
203
return $tag =~ $endless_regex; # a scalar
204
#usage: if (not endless($attributes->{tag})) { ...; }
205
#test=endless("img");
206
}
207
208
########################
209
push @EXPORT_OK, "tag";
210
sub tag($) {
211
#*
212
# creates a start and end tag, with innerHTML<br>
213
214
# <b>Notes</b>:
215
# each key of <i>$attributes</i> should be an html attribute. you may include <i>data-*</i> attributes, too! any keys that aren't html attributes (with the exception of
216
# the following) will be ignored (others may be added over time):
217
# tag
218
# html
219
# ??
220
# debug
221
# noblur (for buttons)
222
# spacing (for pretty printing)
223
# innerHTML<br>
224
225
# if the following attributes contain double quotes, they will be converted to <i>"</i>'s:
226
# title
227
# alt
228
# placeholder
229
# value
230
# content
231
# data
232
# data-*
233
# default
234
# all on* events<br>
235
236
# !if $attribrutes->{tag} eq "script" and not $attributes->{defer} then $attributes->{defer} = 1;
237
238
# <b>Caveats</b>:
239
# separator CAN be anything you want it to be. the caveat is this function uses that
240
# separator in a regex, so be careful what you set separator to. best option is set
241
# it to <i>""</i> or <i>\n</i> or <i><br></i>, or leave it <i>undef</i><br>
242
#
243
# innerHTML will not be spaced according to {spacing}. that's up to you to ensure, if you care at all.<br>
244
#
245
# {innerHTML} and {html} are fundamentally the same. {innerHTML} will over-write {html}<br>
246
#
247
# $attributes->{tabindex}="0" fails. Use "-0" instead. this MAY apply to other attributes over time.<br>
248
#
249
# this function will return nothing if {tag} is omitted. this is useful for debugging as
250
# you can wrap an if statement around the {tag} assignment, or comment it out.
251
#*
252
my ($attributes) = @_; # a hash ref
253
my $rv = "";
254
255
my $spacing = "";
256
if ($attributes->{spacing}) {
257
$spacing = $attributes->{spacing};
258
delete $attributes->{spacing};
259
}
260
261
if ($attributes->{tag}) {
262
# Convert tag name to lowercase
263
$attributes->{tag} = lc $attributes->{tag};
264
265
# If an innerHTML attribute is provided, copy it to html
266
if ($attributes->{innerHTML}) {
267
$attributes->{html} = $attributes->{innerHTML};
268
}
269
270
# Delete the innerHTML attribute
271
delete $attributes->{innerHTML};
272
273
# Filter attributes for double quotes
274
foreach my $attribute (keys %$attributes) {
275
if ($attribute =~ /^(title|alt|placeholder|value|content|data|default|data-.*|on.*)$/) {
276
$attributes->{$attribute} =~ s/"/"/g;
277
}
278
}
279
280
# Create a start and end tag
281
$rv = "<$attributes->{tag}";
282
283
# Add attributes to the start tag
284
foreach my $attribute (keys %$attributes) {
285
if ($attribute ne "html" and $attribute ne "tag") {
286
$rv .= qq[ $attribute="$attributes->{$attribute}"] if defined $attributes->{$attribute};
287
}
288
}
289
290
# Add a closing slash to the start tag if the tag has no corresponding end tag
291
$rv .= " /" if endless($attributes->{tag});
292
293
# Close the start tag and add the innerHTML
294
$rv .= ">";
295
$rv .= $attributes->{html} if defined $attributes->{html};
296
297
# Add an end tag if the tag has a corresponding end tag
298
$rv .= "</$attributes->{tag}>" unless endless($attributes->{tag});
299
}
300
301
return $rv; # a scalar
302
#usage: my %htmlattr = {tag => "br"};<br>my $html = tag(\%htmlattr);
303
}
304
305
########################
306
push @EXPORT_OK, "html_document";
307
sub html_document($$$) {
308
#*
309
# the open and close html tags,
310
# and, optionally, the content(s) wrapped within
311
# separator is used at the end of lines (can be empty)
312
#*
313
my ($doctype, $head_content, $body_content) = @_; # a valid DOCTYPE && a hash reference or scalar && a hash reference or scalar
314
315
# Check if the DOCTYPE is valid and default to HTML5 if not
316
if ($doctype !~ /^html( PUBLIC "[^"]+" "[^"]+")?$/) {
317
$doctype = "html";
318
}
319
320
# Generate the head tag with the provided content
321
my $head;
322
if (ref $head_content eq "HASH") {
323
$head = tag($head_content);
324
} else {
325
$head = tag({tag => "head", html => $head_content});
326
}
327
328
# Generate the body tag with the provided content
329
my $body;
330
if (ref $body_content eq "HASH") {
331
$body = tag($body_content);
332
} else {
333
$body = tag({tag => "body", html => $body_content});
334
}
335
336
# Generate the HTML document with the provided DOCTYPE and body content
337
return "<!DOCTYPE $doctype>\n" . tag({tag => "html", html => "$head\n$body"}); # a scalar
338
#usage: see <a href='#tag'>tag</a>
339
}
340
341
########################
342
push @EXPORT_OK, "br";
343
sub br(;$) {
344
#*
345
# an html br tag
346
#*
347
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
348
my $rv = "";
349
350
if (ref $attributes eq "HASH") {
351
$attributes->{tag} = "br";
352
$rv = tag($attributes);
353
}
354
else {
355
$rv = tag({tag => "br"});
356
}
357
358
return $rv; # a scalar
359
#usage: see <a href='#tag'>tag</a>
360
}
361
362
########################
363
push @EXPORT_OK, "hr";
364
sub hr(;$) {
365
#*
366
# an html hr tag
367
#*
368
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
369
my $rv = "";
370
371
if ($attributes) {
372
$attributes->{tag} = "hr";
373
$rv = tag($attributes);
374
}
375
else {
376
$rv = tag({tag => "hr"});
377
}
378
379
return $rv; # a scalar
380
#usage: see <a href='#tag'>tag</a>
381
}
382
383
########################
384
push @EXPORT_OK, "sup";
385
sub sup(;$) {
386
#*
387
# an html sup tag
388
#*
389
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
390
my $rv = "";
391
392
if ($attributes) {
393
$attributes->{tag} = "sup";
394
$rv = tag($attributes);
395
}
396
else {
397
$rv = tag({tag => "sup"});
398
}
399
400
return $rv; # a scalar
401
#usage: see <a href='#tag'>tag</a>
402
}
403
404
########################
405
push @EXPORT_OK, "classyhr";
406
sub classyhr(;$) {
407
#*
408
# an html hr tag, with css class of "hr"
409
#*
410
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
411
my $rv = "";
412
413
if ($attributes) {
414
$attributes->{tag} = "hr";
415
$attributes->{class} = "hr";
416
$rv = tag($attributes);
417
}
418
else {
419
$rv = tag({tag => "hr", class => "hr"});
420
}
421
422
return $rv; # a scalar
423
#usage: see <a href='#tag'>tag</a>
424
}
425
426
########################
427
push @EXPORT_OK, "ul";
428
sub ul(;$) {
429
#*
430
# an html ul tag
431
#*
432
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
433
my $rv = "";
434
435
if ($attributes) {
436
$attributes->{tag} = "ul";
437
$rv = tag($attributes);
438
}
439
else {
440
$rv = tag({tag => "ul"});
441
}
442
443
return $rv; # a scalar
444
#usage: see <a href='#tag'>tag</a>
445
}
446
447
########################
448
push @EXPORT_OK, "ol";
449
sub ol(;$) {
450
#*
451
# an html ol tag
452
#*
453
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
454
my $rv = "";
455
456
if ($attributes) {
457
$attributes->{tag} = "ol";
458
$rv = tag($attributes);
459
}
460
else {
461
$rv = tag({tag => "ol"});
462
}
463
464
return $rv; # a scalar
465
#usage: see <a href='#tag'>tag</a>
466
}
467
468
########################
469
push @EXPORT_OK, "li";
470
sub li(;$) {
471
#*
472
# an html li tag
473
#*
474
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
475
my $rv = "";
476
477
if ($attributes) {
478
$attributes->{tag} = "li";
479
$rv = tag($attributes);
480
}
481
else {
482
$rv = tag({tag => "li"});
483
}
484
485
return $rv; # a scalar
486
#usage: see <a href='#tag'>tag</a>
487
}
488
489
########################
490
push @EXPORT_OK, "td";
491
sub td(;$) {
492
#*
493
# an html td tag
494
#*
495
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
496
my $rv = "";
497
498
if ($attributes) {
499
$attributes->{tag} = "td";
500
$rv = tag($attributes);
501
}
502
else {
503
$rv = tag({tag => "td"});
504
}
505
506
return $rv; # a scalar
507
#usage: see <a href='#tag'>tag</a>
508
}
509
510
########################
511
push @EXPORT_OK, "tr";
512
sub tr(;$) {
513
#*
514
# an html tr tag
515
#*
516
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
517
my $rv = "";
518
519
if ($attributes) {
520
$attributes->{tag} = "tr";
521
$rv = tag($attributes);
522
}
523
else {
524
$rv = tag({tag => "tr"});
525
}
526
527
return $rv; # a scalar
528
#usage: see <a href='#tag'>tag</a>
529
}
530
531
########################
532
push @EXPORT_OK, "table";
533
sub table(;$) {
534
#*
535
# an html table tag
536
#*
537
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
538
my $rv = "";
539
540
if ($attributes) {
541
$attributes->{tag} = "table";
542
$rv = tag($attributes);
543
}
544
else {
545
$rv = tag({tag => "table"});
546
}
547
548
return $rv; # a scalar
549
#usage: see <a href='#tag'>tag</a>
550
}
551
552
########################
553
push @EXPORT_OK, "division";
554
sub division(;$) {
555
#*
556
# an html div tag
557
#*
558
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
559
my $rv = "";
560
561
if ($attributes) {
562
$attributes->{tag} = "div";
563
$rv = tag($attributes);
564
}
565
else {
566
$rv = tag({tag => "div"});
567
}
568
569
return $rv; # a scalar
570
#usage: see <a href='#tag'>tag</a>
571
}
572
573
########################
574
push @EXPORT_OK, "span";
575
sub span(;$) {
576
#*
577
# an html span tag
578
#*
579
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
580
my $rv = "";
581
582
if ($attributes) {
583
$attributes->{tag} = "span";
584
$rv = tag($attributes);
585
}
586
else {
587
$rv = tag({tag => "span"});
588
}
589
590
return $rv; # a scalar
591
#usage: see <a href='#tag'>tag</a>
592
}
593
594
########################
595
push @EXPORT_OK, "button";
596
sub button(;$) {
597
#*
598
# an html button tag
599
# in HTML, buttons defined without a type default to submit. this sucks.
600
# if you do not supply a type in the $attributes, the type will default to 'button'!
601
# assign {debug-onclick} a value to add {onclick} as new line to {title}
602
#*
603
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
604
my $rv = "";
605
606
if (ref $attributes eq "HASH") {
607
$attributes->{tag} = "button";
608
if (not $attributes->{type}) { $attributes->{type} = "button"; }
609
610
if ($attributes->{"debug-onclick"}) {
611
if ($attributes->{title})
612
{ $attributes->{title} .= "\n" . $attributes->{onclick}; } else
613
{ $attributes->{title} = $attributes->{onclick}; }
614
}
615
616
$rv = tag($attributes);
617
}
618
else {
619
my %attr;
620
$attr{tag} = "button";
621
$attr{type} = "button";
622
$attr{innerHTML} = $attributes;
623
624
$rv = tag(\%attr);
625
}
626
627
return $rv; # a scalar
628
#usage: see <a href='#tag'>tag</a>
629
}
630
631
########################
632
push @EXPORT_OK, "reloadbutton";
633
sub reloadbutton(;$) {
634
#*
635
# a simple reload document button
636
# $attributes acts the same as usual
637
# {tag} and {onclick} and {class} are over-written
638
# all other {tags} should work as expected
639
#*
640
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
641
642
if (ref $attributes ne "HASH") {
643
my %attr;
644
$attr{title} = $attributes;
645
$attributes = \%attr;
646
}
647
648
$attributes->{class} = "yellow padded-smallest";
649
$attributes->{onclick} = "reload();";
650
$attributes->{title} = "Reload this page";
651
$attributes->{innerHTML} = Html::img("i=" . Bc_sql::get_constant("IMAGE_RELOAD_ICON") . "&s=i", "", "", "", "", 1);
652
653
return button($attributes); # a reload button
654
#usage: see <a href='#tag'>tag</a>
655
}
656
657
########################
658
push @EXPORT_OK, "form";
659
sub form(;$) {
660
#*
661
# an html form tag
662
#*
663
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
664
my $rv = "";
665
666
if ($attributes) {
667
$attributes->{tag} = "form";
668
$rv = tag($attributes);
669
}
670
else {
671
$rv = tag({tag => "form"});
672
}
673
674
return $rv; # a scalar
675
#usage: see <a href='#tag'>tag</a>
676
}
677
678
########################
679
push @EXPORT_OK, "input";
680
sub input(;$) {
681
#*
682
# an html input tag
683
#*
684
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
685
my $rv = "";
686
687
if ($attributes) {
688
$attributes->{tag} = "input";
689
$rv = tag($attributes);
690
}
691
else {
692
$rv = tag({tag => "input"});
693
}
694
695
return $rv; # a scalar
696
#usage: see <a href='#tag'>tag</a>
697
}
698
699
########################
700
push @EXPORT_OK, "textarea";
701
sub textarea(;$) {
702
#*
703
# an html textarea tag
704
#*
705
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
706
my $rv = "";
707
708
if ($attributes) {
709
$attributes->{tag} = "textarea";
710
$rv = tag($attributes);
711
}
712
else {
713
$rv = tag({tag => "textarea"});
714
}
715
716
return $rv; # a scalar
717
#usage: see <a href='#tag'>tag</a>
718
}
719
720
########################
721
push @EXPORT_OK, "video";
722
sub video(;$) {
723
#*
724
# an html video tag
725
#*
726
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
727
my $rv = "";
728
729
if ($attributes) {
730
$attributes->{tag} = "video";
731
$rv = tag($attributes);
732
}
733
else {
734
$rv = tag({tag => "video"});
735
}
736
737
return $rv; # a scalar
738
#usage: see <a href='#tag'>tag</a>
739
}
740
741
########################
742
push @EXPORT_OK, "italicize";
743
sub italicize(;$) {
744
#*
745
# an html i tag
746
#*
747
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
748
my $rv = "";
749
750
if (ref $attributes eq "HASH") {
751
$attributes->{tag} = "i";
752
$rv = tag($attributes);
753
}
754
else {
755
$rv = tag({tag => "i", innerHTML => $attributes});
756
}
757
758
return $rv; # a scalar
759
#usage: see <a href='#tag'>tag</a>
760
}
761
762
########################
763
push @EXPORT_OK, "embolden";
764
sub embolden(;$) {
765
#*
766
# an html b tag
767
#*
768
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
769
my $rv = "";
770
771
if (ref $attributes eq "HASH") {
772
$attributes->{tag} = "b";
773
$rv = tag($attributes);
774
}
775
else {
776
$rv = tag({tag=>"b", innerHTML=>$attributes});
777
}
778
779
return $rv; # a scalar
780
#usage: see <a href='#tag'>tag</a>
781
}
782
783
########################
784
push @EXPORT_OK, "underline";
785
sub underline(;$) {
786
#*
787
# an html u tag
788
#*
789
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
790
my $rv = "";
791
792
if ($attributes) {
793
$attributes->{tag} = "u";
794
$rv = tag($attributes);
795
}
796
else {
797
$rv = tag({tag => "u"});
798
}
799
800
return $rv; # a scalar
801
#usage: see <a href='#tag'>tag</a>
802
}
803
804
########################
805
push @EXPORT_OK, "nav";
806
sub nav(;$) {
807
#*
808
# an html nav tag
809
#*
810
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
811
my $rv = "";
812
813
if ($attributes) {
814
$attributes->{tag} = "nav";
815
$rv = tag($attributes);
816
}
817
else {
818
$rv = tag({tag => "nav"});
819
}
820
821
return $rv; # a scalar
822
#usage: see <a href='#tag'>tag</a>
823
}
824
825
########################
826
push @EXPORT_OK, "img";
827
sub img(;$) {
828
#*
829
# an html img tag
830
#*
831
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
832
my $rv = "";
833
834
if ($attributes) {
835
$attributes->{tag} = "img";
836
$rv = tag($attributes);
837
}
838
else {
839
$rv = tag({tag => "img"});
840
}
841
842
return $rv; # a scalar
843
#usage: see <a href='#tag'>tag</a>
844
}
845
846
########################
847
push @EXPORT_OK, "big";
848
sub big(;$) {
849
#*
850
# an html big tag
851
#*
852
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
853
my $rv = "";
854
855
if (ref $attributes eq "HASH") {
856
$attributes->{tag} = "big";
857
$rv = tag($attributes);
858
}
859
else {
860
$rv = tag({tag => "big", innerHTML=>$attributes});
861
}
862
863
return $rv; # a scalar
864
#usage: see <a href='#tag'>tag</a>
865
}
866
867
########################
868
push @EXPORT_OK, "small";
869
sub small(;$) {
870
#*
871
# an html small tag
872
#*
873
my ($attributes) = @_; # a hash ref (see <a href='#tag'>tag</a> subroutine)
874
my $rv = "";
875
876
if (ref $attributes eq "HASH") {
877
$attributes->{tag} = "small";
878
$rv = tag($attributes);
879
}
880
else {
881
my %small; {
882
$small{tag} = "small";
883
$small{innerHTML} = $attributes;
884
}
885
886
$rv = tag(\%small);
887
}
888
889
return $rv; # a scalar
890
#usage: see <a href='#tag'>tag</a>
891
}
892
893
########################
894
push @EXPORT_OK, "center_content";
895
sub center_content($;$$$$) {
896
#*
897
# this subroutine uses the <a href="#tag">tag</a> subroutine, and it is recommended you are intimately familiar with its functioning.<br>
898
899
# outputs two html elements (usually divs). a CONTAINER and its CONTENTS.<br>
900
901
# CONTAINER, by default, positions horizontally centered and top aligned relative to its container.
902
# CONTENTS is positioned horizontally and vertically centered on CONTAINER.<br>
903
904
# the innerHTML of CONTENTS will be either <i>$contents</i> (if it's a string), or $contents->{"contents-attr"}->{innerHTML}.
905
# if <i>$contents</i> is just a string, that string is assigned to {"contents-attr"}->{innerHTML}. the output will be plain, and basically unstylized.<br>
906
907
# if <i>$contents</i> is an array reference, then {"contents-attr"}->{innerHTML} is filled with a <br> separated list of each element in the array.
908
# again, the output will be plain, and basically unstylized.<br>
909
910
# <i>$width</i><sup>*</sup> and <i>$height</i> define the width and height of CONTAINER, and are optional.
911
# <sup>*</sup><b>applied to both min-width and max-width.</b><br>
912
913
# <i>$debug</i> toggles the debugger on and off (ignored when <i>$contents</i> is a hash ref), and is also optional.<br>
914
915
# if <i>$contents</i> is a hash reference, then:<br>
916
917
# hash parameters, summary:
918
# {position}
919
# {alignment}
920
# {"contents-attr"}
921
# {"container-attr"}
922
# {debug}
923
924
# <br>hash parameters, descriptive:
925
# {position} applies to CONTENTS margin styling only
926
# - value is assumed to be in the format of "horiz,vert"
927
# eg: "center,top" or
928
# "right,bottom"
929
# - innerHTML of CONTENTS is unaffected.
930
# - valid values are (format: horiz,vert):
931
# - horiz: center, left, right
932
# - vert: center, top, bottom
933
# - any other values, or no values at all, means "center,center".
934
# - positions vertically and horizontally CONTENTS relative to CONTAINER.
935
# {alignment} applies to CONTAINER margin styling only
936
# - innerHTML of CONTENTS is unaffected.
937
# - valid values are: center, right, left.
938
# - any other value, or no value at all, means center.
939
# - positions CONTAINER relative to CONTAINER's container.
940
# {"contents-attr"} - optional; margin styling is overridden, completely!
941
# {"container-attr"} - optional; margin styling is overridden, compeletely!
942
# {debug} - optional; assign 1+ to add a black 1+ pixel thick border to container; effectively overrides container border styling<br>
943
944
# additional notes:
945
# {"container-attr"}->{innerHMTL} will be overridden.
946
# {"contents-attr"} and {"container-attr"}->{class} margins will be overridden.
947
# if {"contents-attr"} or {"container-attr"}->{tag} is omitted, div will be default (and is recommended).
948
#*
949
my ($contents, $width, $height, $alignment, $debug) = @_; # a hash, or a string of content && width of container (optional) && height of container (optional) && container horizontal alignment && debug toggle (optional)
950
if (not $alignment) { $alignment = "center"; } else { $alignment = lc $alignment; }
951
my (%containerdiv, %contentsdiv, $position);
952
$position = "center,center";
953
954
my $left = "margin: 0 0 0 0;";
955
my $right = "margin: 0 0 0 auto;";
956
my $center = "margin: 0 auto 0 auto;";
957
my $top = "margin: auto;";
958
my $bottom = "margin: auto;";
959
960
# two divs. a div class=centered
961
# and a div to contain the content
962
963
if (ref $contents eq "HASH") {
964
if ($contents->{debug}) { $debug = 1; } else { $debug = 0; }
965
if ($contents->{alignment}) { $alignment = lc $contents->{alignment}; } else { $alignment = "center"; }
966
if ($contents->{position}) { $position = lc $contents->{position}; } else { $position = "center,center"; }
967
}
968
if ($alignment ne "left" and $alignment ne "right" and $alignment ne "center") { $alignment = "center"; }
969
970
if (ref $contents eq "HASH") {
971
# force "display: flex;" and "margin: 0 auto;" on container
972
if ($contents->{"container-attr"}->{style} =~ /margin:/i) {
973
my $marginupdated = 0;
974
my $displayupdated = 0;
975
976
my @styles = split(";", $contents->{"container-attr"}->{style});
977
foreach my $style (@styles) {
978
if ($style =~ /^margin:/i) {
979
if ($alignment eq "center" or not $alignment) { $style = $center; }
980
elsif ($alignment eq "left") { $style = $left; }
981
elsif ($alignment eq "right") { $style = $right; }
982
983
$marginupdated = 1;
984
}
985
if ($style =~ /^display:/i) {
986
$style = "display: flex;";
987
$displayupdated = 1;
988
}
989
}
990
if (not $marginupdated) {
991
if ($contents->{"container-attr"}->{style}) {
992
if ($alignment eq "center" or not $alignment) { $contents->{"container-attr"}->{style} .= $center; }
993
elsif ($alignment eq "left") { $contents->{"container-attr"}->{style} .= " $left"; }
994
elsif ($alignment eq "right") { $contents->{"container-attr"}->{style} .= " $right"; }
995
}
996
else {
997
if ($alignment eq "center" or not $alignment) { $contents->{"container-attr"}->{style} = $center; }
998
elsif ($alignment eq "left") { $contents->{"container-attr"}->{style} = $left; }
999
elsif ($alignment eq "right") { $contents->{"container-attr"}->{style} = $right; }
1000
}
1001
}
1002
if (not $displayupdated) {
1003
if ($contents->{"container-attr"}->{style})
1004
{ $contents->{"container-attr"}->{style} .= " display: flex;"; } else
1005
{ $contents->{"container-attr"}->{style} = "display: flex;"; }
1006
}
1007
}
1008
else {
1009
if ($contents->{"container-attr"}->{style}) {
1010
my $marginupdated = 0;
1011
my $displayupdated = 0;
1012
1013
my @styles = split(";", $contents->{"container-attr"}->{style});
1014
foreach my $style (@styles) {
1015
if ($style =~ /^margin:/i) {
1016
if ($alignment eq "center" or not $alignment) { $style = $center; }
1017
elsif ($alignment eq "left") { $style = $left; }
1018
elsif ($alignment eq "right") { $style = $right; }
1019
$marginupdated = 1;
1020
}
1021
1022
if ($style =~ /^display:/i) {
1023
$style = "display: flex;";
1024
$displayupdated = 1;
1025
}
1026
}
1027
if (not $marginupdated) {
1028
if ($alignment eq "center" or not $alignment) { $contents->{"container-attr"}->{style} = $center; }
1029
elsif ($alignment eq "left") { $contents->{"container-attr"}->{style} = $left; }
1030
elsif ($alignment eq "right") { $contents->{"container-attr"}->{style} = $right; }
1031
}
1032
1033
if (not $displayupdated) { $contents->{"container-attr"}->{style} = "display: flex;"; }
1034
if ($marginupdated or $displayupdated) { $contents->{"container-attr"}->{style} = join("; ", @styles); }
1035
}
1036
else {
1037
if ($alignment eq "center" or not $alignment) { $contents->{"container-attr"}->{style} = $center; }
1038
elsif ($alignment eq "left") { $contents->{"container-attr"}->{style} = $left; }
1039
elsif ($alignment eq "right") { $contents->{"container-attr"}->{style} = $right; }
1040
}
1041
}
1042
1043
# force "margin: auto;" on contents
1044
my $marginupdated = 0;
1045
if ($contents->{"contents-attr"}->{style} =~ /margin:/i) {
1046
my @styles = split(";", $contents->{"contents-attr"}->{style});
1047
foreach my $style (@styles) {
1048
$style =~ s/^ *//;
1049
$style =~ s/ *$//;
1050
if ($style =~ /^margin:/i) {
1051
my $pos = "auto";
1052
if ($position) {
1053
if ($position eq "center,center") { $pos = "auto"; }
1054
elsif ($position eq "left,center") { $pos = "auto auto auto 0"; }
1055
elsif ($position eq "right,center") { $pos = "auto 0 auto auto"; }
1056
elsif ($position eq "center,top") { $pos = "0 auto auto auto"; }
1057
elsif ($position eq "center,bottom") { $pos = "auto auto 0 auto"; }
1058
elsif ($position eq "left,top") { $pos = "0 auto auto 0"; }
1059
elsif ($position eq "left,bottom") { $pos = "auto auto 0 0"; }
1060
elsif ($position eq "right,top") { $pos = "0 0 auto auto"; }
1061
elsif ($position eq "right,bottom") { $pos = "auto 0 0 auto"; }
1062
}
1063
$style = "margin: $pos;";
1064
$marginupdated = 1;
1065
}
1066
}
1067
$contents->{"contents-attr"}->{style} = join("; ", @styles);
1068
}
1069
1070
if (not $marginupdated) {
1071
my $pos = "auto";
1072
if ($position) {
1073
if ($position eq "center,center") { $pos = "auto"; }
1074
elsif ($position eq "left,center") { $pos = "auto auto auto 0"; }
1075
elsif ($position eq "right,center") { $pos = "auto 0 auto auto"; }
1076
elsif ($position eq "center,top") { $pos = "0 auto auto auto"; }
1077
elsif ($position eq "center,bottom") { $pos = "auto auto 0 auto"; }
1078
elsif ($position eq "left,top") { $pos = "0 auto auto 0"; }
1079
elsif ($position eq "left,bottom") { $pos = "auto auto 0 0"; }
1080
elsif ($position eq "right,top") { $pos = "0 0 auto auto"; }
1081
elsif ($position eq "right,bottom") { $pos = "auto 0 0 auto"; }
1082
}
1083
if ($contents->{"contents-attr"}->{style}) {
1084
$contents->{"contents-attr"}->{style} .= " margin: $pos;";
1085
}
1086
else {
1087
$contents->{"contents-attr"}->{style} = "margin: $pos;";
1088
}
1089
}
1090
1091
%contentsdiv = %{$contents->{"contents-attr"}};
1092
%containerdiv = %{$contents->{"container-attr"}};
1093
}
1094
else {
1095
$contentsdiv{style} = "margin: auto;";
1096
1097
if (ref $contents ne "ARRAY") {
1098
$contentsdiv{innerHTML} = $contents;
1099
}
1100
else {
1101
# hm. what to do with an array.
1102
# ok. just list it's contents one per line
1103
foreach my $line (@$contents) {
1104
$contentsdiv{innerHTML} .= $line . br;
1105
}
1106
}
1107
$containerdiv{style} = "display: flex;";
1108
if ($alignment eq "center" or not $alignment) { $containerdiv{style} .= " $center"; }
1109
elsif ($alignment eq "left") { $containerdiv{style} .= " $left"; }
1110
elsif ($alignment eq "right") { $containerdiv{style} .= " $right"; }
1111
}
1112
1113
$contentsdiv{tag} = "div";
1114
$containerdiv{tag} = "div";
1115
1116
if ($debug) { $containerdiv{style} .= " border: " . $debug . "px solid black;"; }
1117
if ($width) { $containerdiv{style} .= " min-width: $width; max-width: $width;"; }
1118
if ($height) { $containerdiv{style} .= " min-height: $height;"; }
1119
1120
# next, add in some debug info to contents div
1121
if ($debug) {
1122
$contentsdiv{innerHTML} .= hr;
1123
$contentsdiv{innerHTML} .= "\$contents is ";
1124
1125
if (ref $contents eq "HASH") { $contentsdiv{innerHTML} .= embolden("a hash ref"); }
1126
elsif (ref $contents eq "ARRAY") { $contentsdiv{innerHTML} .= embolden("an array reference"); }
1127
else { $contentsdiv{innerHTML} .= embolden("a string"); }
1128
1129
$contentsdiv{innerHTML} .= br;
1130
$contentsdiv{innerHTML} .= "min/max-width: " . embolden($width) . br;
1131
$contentsdiv{innerHTML} .= "min-height: " . embolden($height) . br;
1132
$contentsdiv{innerHTML} .= "alignment: " . embolden($alignment) . br;
1133
$contentsdiv{innerHTML} .= "position: " . embolden($position) . br;
1134
}
1135
1136
# and now, add the contents to the container
1137
$containerdiv{innerHTML} = tag(\%contentsdiv);
1138
1139
# finally, return the results
1140
return tag(\%containerdiv); # a scalar
1141
#usage: my $centered_content = center_content("this <b>string<b>");
1142
}
1143
1144
########################
1145
push @EXPORT_OK, "navdd";
1146
sub navdd($) {
1147
#*
1148
# dropdown box with navigation buttons
1149
# meant for very large lists
1150
# a "page" number display
1151
# ajax for each button must be included<br>
1152
1153
# layout will be dd over nav.
1154
# and nav will be home|prev|pnum|next|last|reload
1155
# unless {mini} eq 1, then layout
1156
# will be all buttons to right of dd
1157
# dd input will be shrunk to just the width of the down arrow,
1158
# and the buttons will be shrunk to the size of their content<br>
1159
1160
# <nobr>
1161
# valid keys for <i>$attr:</i>
1162
# NAME VALUE DESC<br>
1163
1164
# {list} (array ref) (each element is a name=value pair)
1165
# {mini} (1 or 0) (make all buttons and dd as tiny as possible)
1166
# {debug} (1 or 0) (include debug content)
1167
# {navclass} (css class name) (class for the navcontainer)
1168
# {page} (1+) (the current page number)
1169
# {lastpage} (1+) (the last page number)
1170
1171
# {endlabel} (an image/text) (icon/text for end button)
1172
# {prevlabel} (an image/text) (icon/text for prev button)
1173
# {nextlabel} (an image/text) (icon/text for next button)
1174
# {homelabel} (an image/text) (icon/text for home button)
1175
# {golabel} (an image/text) (icon/text for go button)
1176
# {pickonelabel} (text) (a label for the pickone option)
1177
1178
# {endclick} (javascript) (js for when end is clicked)
1179
# {prevclick} (javascript) (js for when prev is clicked)
1180
# {nextclick} (javascript) (js for when next is clicked)
1181
# {homeclick} (javascript) (js for when home is clicked)
1182
# {reloadclick} (javascript) (js for when reload is clicked)
1183
# {pagechange} (javascript) (js for when page input changes)
1184
# {goclick} (javascript) (js for when go is clicked)
1185
1186
# {pickoneclass} (css class name) (class for the pickone option)
1187
# {containerclass} (css class name) (class for the encapsulating container)
1188
1189
# {dropdownicon} (a url) (include an "icon" for the SELECT element)
1190
# {disablepickone} (1 or 0) (toggle 'pick one' option)
1191
# {inline} (1 or 0) (toggles move dropbox to left of nav)
1192
1193
# {html attribute} (any html attr) (any html attr. eg: onchange)*
1194
# </nobr>
1195
1196
# <br>{list} must be an array reference. this function will not auto-generate it!
1197
# as noted, each element is a name=value pairing.
1198
# a css class may be included for one or more items in
1199
# the list. to include a class: name=value=class<br>
1200
1201
# <b>*eg: {selected} or {class} or {onchange}
1202
# these will be applied to the SELECT element. see the <a href="#tag">tag</a> subroutine
1203
# for more details. if {style} is included, ensure its value ends with a semi-colon!
1204
# this subroutine may add on extra styling when {mini} eq 1</b><br>
1205
1206
# the select input gets an HTML ID of $attr->{id}
1207
# the page input gets an HTML ID of "ddnav_page_input_$attr->{id}"
1208
# the home button gets an HTML ID of "ddnav_home_button_" . $attr->{id}
1209
# the prev button gets an HTML ID of "ddnav_prev_button_" . $attr->{id}
1210
# the next button gets an HTML ID of "ddnav_next_button_" . $attr->{id}
1211
# the end button gets an HTML ID of "ddnav_end_button_" . $attr->{id}<br>
1212
1213
# if <i>$attr->{id}</i> has no value, the return value of "Bc_misc::new_id()" is used instead.
1214
# however, it's best to give it a value manually.<br>
1215
1216
# this subroutine is written so that if no attributes are given, it won't break your
1217
# script. it would be rather pointless (in most cases), but it could be a desirable
1218
# affect, so the 'feature' remains.<br>
1219
#*
1220
my ($attr) = @_; # a hash reference. see desc.
1221
my $rv = "";
1222
1223
# ensure we get attributes, and ensure we have a list of options, too!
1224
# or, do we ignore the lack of a list? empty lists are okay? sure, why
1225
# not? all dropdowns, by default, should include a "pick one" option that
1226
# isn't selectable anyway.
1227
if (ref $attr eq "HASH") {
1228
my $list = $attr->{list}; # cuz it's easier to think about, and type this way!
1229
# and now, let's just delete that list from $attr
1230
# because, if it's left in, the "tag" subroutine outputs $attr->{list}'s value
1231
# in the element's attributes! lol, so, bye bye {list}.
1232
delete $attr->{list};
1233
1234
# now, i'm sure it won't happen, but...
1235
$attr->{innerHTML} = ""; # just in case we're given innerHTML, let's clear it.
1236
if (not $attr->{lastpage} and $attr->{page}) { $attr->{lastpage} = $attr->{page}; }
1237
if (not $attr->{page}) { $attr->{page} = 1; }
1238
if (not $attr->{id}) { $attr->{id} = Bc_misc::new_id(); }
1239
1240
my %container; { # a div to contain it all!
1241
$container{tag} = "div";
1242
1243
# override {tag} setting
1244
$attr->{tag} = "select";
1245
1246
# container class
1247
if ($attr->{class}) { $attr->{class} .= " "; }
1248
$container{class} = $attr->{containerclass};
1249
if ($container{class} !~ /min-content/i) {
1250
if ($container{class}) { $container{class} .= " "; }
1251
$container{class} .= "min-content";
1252
}
1253
if ($attr->{class}) { $attr->{class} .= " "; }
1254
if ($attr->{debug}) { $attr->{class} .= "bordered"; }
1255
# end container class
1256
1257
$container{style} = "display: flex; align-items: center;";
1258
1259
if ($attr->{mini}) {
1260
if ($attr->{class} !~ /margins-none/i) {
1261
if ($attr->{class}) { $attr->{class} .= " "; }
1262
$attr->{class} .= "margins-none";
1263
}
1264
if ($attr->{class} !~ /padding-none/i) {
1265
if ($attr->{class}) { $attr->{class} .= " "; }
1266
$attr->{class} .= "padding-none";
1267
}
1268
if ($container{class} !~ /margins-none/i) {
1269
if ($container{class}) { $container{class} .= " "; }
1270
$container{class} .= "margins-none";
1271
}
1272
if ($container{class} !~ /padding-none/i) {
1273
if ($container{class}) { $container{class} .= " "; }
1274
$container{class} .= "padding-none";
1275
}
1276
1277
# make the dropbox "mini"
1278
my $w = "17px";
1279
if ($attr->{style}) { $attr->{style} .= " "; }
1280
$attr->{style} .= "max-width: $w; " .
1281
"min-width: $w; " .
1282
"width: $w; " .
1283
"margin-right: 1px !important; ";
1284
if ($attr->{class} !~ /neartiny/i) {
1285
if ($attr->{class}) { $attr->{class} .= " "; }
1286
$attr->{class} .= " neartiny";
1287
}
1288
1289
# and disable {label}
1290
$attr->{label} = "";
1291
}
1292
elsif (not $attr->{inline}) {
1293
$container{style} .= " flex-direction: column;";
1294
my $w = "95%";
1295
if ($attr->{mini}) { $w = "100px"; }
1296
if ($attr->{style}) { $attr->{style} .= " "; }
1297
$attr->{style} .= "max-width: $w; min-width: $w; width: $w;";
1298
if (not $attr->{mini}) { $attr->{style} .= " margin: 5px 0 0 0;"; }
1299
}
1300
}
1301
1302
{ # the dropdown, with contents
1303
# we'll need a list of options
1304
if (not $attr->{onchange} and $attr->{debug}) { $attr->{onchange} = "console.log('selection changed')"; }
1305
1306
if ($attr->{dropdownicon}) {
1307
# left align the "icon"
1308
# move dd text to the right by icon width
1309
# if {mini}, then hide the down arrow!
1310
if ($attr->{style}) { $attr->{style} .= " "; }
1311
$attr->{style} .= "background-image: url($attr->{dropdownicon});";
1312
$attr->{style} .= " background-repeat: no-repeat;";
1313
$attr->{style} .= " background-size: 16px 16px;";
1314
$attr->{style} .= " background-position: 2px;";
1315
$attr->{style} .= " padding-left: 16px;";
1316
if ($attr->{mini}) {
1317
# hide the down arrow
1318
}
1319
} # end if dropdownicon
1320
1321
# add tooltip to dd if one wasn't provided
1322
if (not $attr->{title}) { $attr->{title} = "Please select an item from the list..."; }
1323
1324
if ($attr->{debug}) {
1325
$attr->{title} .= "\n" .
1326
" id: $attr->{id}\n" .
1327
" onchange: $attr->{onchange}\n";
1328
}
1329
1330
# add "pick one" option?
1331
if (not $attr->{disablepickone}) {
1332
# then include the "pick one" option
1333
my %pickone; {
1334
$pickone{tag} = "option";
1335
$pickone{disabled} = 1;
1336
$pickone{value} = "";
1337
1338
if (not $attr->{selected}) {
1339
$pickone{selected} = 1;
1340
} else {
1341
# if "selected" has a value, but that
1342
# value isn't actually in the options, then
1343
# the top option should be the default
1344
# selected item!
1345
my $selected = 0;
1346
foreach my $option (@$list) {
1347
my ($name, $value, $cssclass) = split("=", $option);
1348
if ($value eq $attr->{selected}) { $selected = 1; }
1349
}
1350
if (not $selected) { $attr->{selected} = ""; }
1351
}
1352
1353
if ($attr->{pickoneclass}) { $pickone{class} = $attr->{pickoneclass}; }
1354
1355
if ($attr->{pickonelabel})
1356
{ $pickone{innerHTML} = $attr->{pickonelabel}; } else
1357
{ $pickone{innerHTML} = "Pick One"; }
1358
}
1359
1360
$attr->{innerHTML} .= tag(\%pickone);
1361
} # end if pick one enabled
1362
1363
# and now add the options to the dropbox
1364
if (ref $list eq "ARRAY") {
1365
foreach my $option (@$list) {
1366
my %option; {
1367
my ($value, $name, $cssclass) = split("=", $option);
1368
1369
$option{tag} = "option";
1370
$option{innerHTML} = $name;
1371
$option{value} = $value;
1372
if ($cssclass) { $option{class} = $cssclass; }
1373
if ($attr->{selected}) {
1374
# check if this option matches {selected}
1375
if ($value eq $attr->{selected}) {
1376
$option{selected} = 1;
1377
}
1378
}
1379
}
1380
1381
# now add that option to the dropdown
1382
$attr->{innerHTML} .= tag(\%option);
1383
}
1384
}
1385
else {
1386
$attr->{disabled} = 1;
1387
}
1388
} # end of dropdown
1389
1390
###############
1391
## NAVIGATOR ##
1392
###############
1393
my %navcontainer; {
1394
# table-ize this? flex?
1395
# or grid?? i like flex.
1396
$navcontainer{tag} = "div";
1397
1398
if ($attr->{navclass}) { $navcontainer{class} = $attr->{navclass}; }
1399
1400
if ($attr->{mini}) {
1401
if ($navcontainer{class}) { $navcontainer{class} .= " "; }
1402
if ($navcontainer{class} !~ /padding-none/i) { $navcontainer{class} .= "padding-none"; }
1403
if ($navcontainer{class}) { $navcontainer{class} .= " "; }
1404
if ($navcontainer{class} !~ /margins-none/i) { $navcontainer{class} .= "margins-none"; }
1405
}
1406
1407
if ($navcontainer{class} !~ /nowrap/i) {
1408
if ($navcontainer{class}) { $navcontainer{class} .= " "; }
1409
$navcontainer{class} .= "nowrap";
1410
}
1411
1412
if ($attr->{debug}) {
1413
if ($navcontainer{class}) { $navcontainer{class} .= " "; }
1414
$navcontainer{class} .= "bordered";
1415
}
1416
1417
my %home; { # first page button
1418
$home{tag} = "button";
1419
$home{type} = "button";
1420
$home{class} = "neartiny margins-none";
1421
if ($attr->{mini}) { $home{class} .= " padding-none"; }
1422
$home{title} = "Load First Page";
1423
$home{id} = "ddnav_home_button_" . $attr->{id};
1424
1425
if ($attr->{homelabel})
1426
{ $home{innerHTML} = $attr->{homelabel}; } else
1427
{ $home{innerHTML} = "<<"; }
1428
1429
if ($attr->{homeclick}) { $home{onclick} = $attr->{homeclick}; }
1430
elsif ($attr->{debug}) { $home{onclick} = "console.log('home clicked');"; }
1431
1432
if ($attr->{page} < 2)
1433
{ $home{disabled} = 1; $home{class} .= " grey"; } else
1434
{ $home{class} .= " green"; }
1435
1436
if ($attr->{debug}) {
1437
$home{title} .= "\n" .
1438
" onclick: $home{onclick}\n" .
1439
" id: $home{id}\n";
1440
}
1441
}
1442
1443
my %prev; { # prev page button
1444
$prev{tag} = "button";
1445
$prev{type} = "button";
1446
$prev{class} = "neartiny margins-none margins-left-right-small";
1447
if ($attr->{mini}) { $prev{class} .= " padding-none"; }
1448
$prev{title} = "Load Previous Page";
1449
$prev{id} = "ddnav_prev_button_" . $attr->{id};
1450
1451
if ($attr->{prevlabel})
1452
{ $prev{innerHTML} = $attr->{prevlabel}; } else
1453
{ $prev{innerHTML} = " < "; }
1454
1455
if ($attr->{page} < 2) { $prev{disabled} = 1; $prev{class} .= " grey"; } else { $prev{class} .= " green"; }
1456
1457
if ($attr->{prevclick}) {
1458
$prev{debug} = 1;
1459
$prev{onclick} = $attr->{prevclick};
1460
}
1461
elsif ($attr->{debug}) {
1462
$prev{onclick} = "console.log('prev clicked');";
1463
$prev{title} .= "\n" .
1464
" onclick: $prev{onclick}\n" .
1465
" id: $prev{id}\n";
1466
}
1467
else {
1468
$prev{onclick} = "console.log('prevclick missing!');";
1469
}
1470
}
1471
1472
my %page; { # page number display
1473
$page{tag} = "input";
1474
$page{name} = "pagenumber";
1475
$page{type} = "text";
1476
$page{min} = "1";
1477
$page{max} = $attr->{lastpage};
1478
$page{step} = "1";
1479
$page{value} = $attr->{page};
1480
$page{title} = "Page Number $attr->{page} of $attr->{lastpage}";
1481
$page{id} = "ddnav_page_input_" . $attr->{id};
1482
$page{onkeyup} = "acceptIntegerOnly(this);";
1483
if ($page{onfocus}) {
1484
if ($page{onfocus} !~ /;$/) { $page{onfocus} .= "; "; }
1485
$page{onfocus} .= "this.select();";
1486
}
1487
else {
1488
$page{onfocus} = "this.select();";
1489
}
1490
1491
if ($attr->{debug}) { $page{onchange} .= "console.log('page changed');"; }
1492
if ($attr->{pagechange}) { $page{onchange} .= $attr->{pagechange}; }
1493
1494
1495
my $w = "30px";
1496
$page{style} = "max-width: $w; min-width: $w; text-align: center; width: $w;";
1497
$page{class} = "margins-none";
1498
if ($attr->{mini})
1499
{ $page{class} .= " neartiny padding-none"; } else
1500
{ $page{class} .= " margins-left-right-small"; }
1501
if ($attr->{debug}) {
1502
$page{title} .= "\n" .
1503
" onclick: $page{onclick}\n" .
1504
" id: $page{id}\n";
1505
}
1506
}
1507
1508
my %go; {
1509
$go{tag} = "button";
1510
$go{type} = "button";
1511
$go{class} = "neartiny margins-none margins-left-right-small";
1512
if ($attr->{mini}) { $go{class} .= " padding-none"; }
1513
$go{title} = "Go to Page";
1514
$go{id} = "ddnav_go_button_" . $attr->{id};
1515
$go{onclick} = "";
1516
1517
if ($attr->{golabel})
1518
{ $go{innerHTML} = $attr->{golabel}; } else
1519
{ $go{innerHTML} = "Go"; }
1520
1521
if ($attr->{goclick}) { $go{onclick} .= $attr->{goclick}; }
1522
1523
if ($attr->{page} >= $attr->{lastpage}) { $go{disabled} = 1; $go{class} .= " grey"; } else { $go{class} .= " green"; }
1524
1525
if ($attr->{debug}) {
1526
if ($go{onclick} !~ /;$/)
1527
{ $go{onclick} .= "; "; } else
1528
{ $go{onclick} .= " "; }
1529
1530
$go{onclick} .= "console.log('go clicked');";
1531
$go{title} .= "\n" .
1532
" onclick: $go{onclick}\n" .
1533
" id: $go{id}\n";
1534
}
1535
}
1536
1537
my %next; { # next page button
1538
$next{tag} = "button";
1539
$next{type} = "button";
1540
$next{class} = "neartiny margins-none margins-left-right-small";
1541
if ($attr->{mini}) { $next{class} .= " padding-none"; }
1542
$next{title} = "Load Next Page";
1543
$next{id} = "ddnav_next_button_" . $attr->{id};
1544
1545
if ($attr->{nextlabel})
1546
{ $next{innerHTML} = $attr->{nextlabel}; } else
1547
{ $next{innerHTML} = " > "; }
1548
1549
if ($attr->{nextclick}) { $next{onclick} = $attr->{nextclick}; }
1550
elsif ($attr->{debug}) { $next{onclick} = "console.log('next clicked');"; }
1551
1552
if ($attr->{page} >= $attr->{lastpage})
1553
{ $next{disabled} = 1; $next{class} .= " grey"; } else
1554
{ $next{class} .= " green"; }
1555
1556
if ($attr->{debug}) {
1557
$next{title} .= "\n" .
1558
" onclick: $next{onclick}\n" .
1559
" id: $next{id}\n";
1560
}
1561
} # end next page button
1562
1563
my %end; { # last page button
1564
$end{tag} = "button";
1565
$end{type} = "button";
1566
$end{class} = "neartiny margins-none";
1567
if ($attr->{mini}) { $end{class} .= " padding-none"; }
1568
$end{title} = "Load Last Page";
1569
$end{id} = "ddnav_end_button_" . $attr->{id};
1570
1571
if ($attr->{endlabel})
1572
{ $end{innerHTML} = $attr->{endlabel}; } else
1573
{ $end{innerHTML} = ">>"; }
1574
1575
if ($attr->{endclick}) { $end{onclick} = $attr->{endclick}; }
1576
elsif (not $attr->{endclick} and $attr->{debug}) { $end{onclick} = "console.log('end clicked');"; }
1577
1578
if ($attr->{page} >= $attr->{lastpage})
1579
{ $end{disabled} = 1; $end{class} .= " grey"; } else
1580
{ $end{class} .= " green"; }
1581
1582
if ($attr->{debug}) {
1583
$end{title} .= "\n" .
1584
" onclick: $end{onclick}\n" .
1585
" id: $end{id}\n";
1586
}
1587
}
1588
1589
my %reload; { # reload DD options button
1590
$reload{tag} = "button";
1591
$reload{type} = "button";
1592
$reload{class} = "neartiny margins-none margins-left-right-small yellow";
1593
if ($attr->{mini}) { $reload{class} .= " padding-none"; }
1594
$reload{title} = "Reload Dropbox";
1595
$reload{id} = "ddnav_reload_button_" . $attr->{id};
1596
1597
if ($attr->{reloadlabel})
1598
{ $reload{innerHTML} = $attr->{reloadlabel}; } else
1599
{ $reload{innerHTML} = " R "; }
1600
1601
if ($attr->{reloadclick}) { $reload{onclick} = $attr->{reloadclick}; }
1602
elsif ($attr->{debug}) { $reload{onclick} = "console.log('reload clicked');"; }
1603
if ($attr->{debug}) {
1604
$reload{title} .= "\n" .
1605
" onclick: $reload{onclick}\n" .
1606
" id: $reload{id}\n";
1607
}
1608
}
1609
1610
# now add all the above to the NAV container!
1611
$navcontainer{innerHTML} = " " .
1612
tag(\%home) .
1613
tag(\%prev) .
1614
tag(\%page) .
1615
tag(\%go) .
1616
tag(\%next) .
1617
tag(\%end) .
1618
tag(\%reload);
1619
}
1620
1621
# attr will contain data the tag function won't care about
1622
# should we delete it from the attr hash? nah. not yet.
1623
# perhaps we should, though, to avoid any issues which
1624
# MAY arise from not removing them. later.
1625
# oh wait. it's a hash REFERENCE. we can't delete data.
1626
# damn.
1627
1628
# anyhow, add the dropbox and navcontainer
1629
$container{innerHTML} .= tag($attr) .
1630
tag(\%navcontainer);
1631
1632
$rv = tag(\%container);
1633
}
1634
1635
return $rv; # a scalar of HTML code
1636
#usage: $output .= navdd(\%attributes);
1637
}
1638
1639
########################
1640
push @EXPORT_OK, "Html2_account_settings";
1641
sub Html2_account_settings(;$) {
1642
#*
1643
# a remake of the user account settings page
1644
#*
1645
my ($uid) = @_; # a uid
1646
if (not $uid) { $uid = $Bc_sql::LOGGEDIN; }
1647
my $rv = "account settings";
1648
my $tab = Bc_misc::get_param("tab");
1649
if (not $tab) { $tab = "bio"; }
1650
1651
my %container; {
1652
$container{tag} = "form";
1653
#$container{class} = "bordered";
1654
$container{method} = "post";
1655
$container{action} = "/showparams.pl";
1656
$container{style} = "max-width: 330px;";
1657
1658
my %fs; {
1659
$fs{tag} = "fieldset";
1660
$fs{style} = "display: grid; " .
1661
"grid-template-areas: 'settingsbody', 'settingsbuttons'; " .
1662
"grid-template-rows: 1fr min-content; " .
1663
"max-height: 300px; " .
1664
"min-height: 300px; " .
1665
"height: 300px; ";
1666
1667
my %legend; {
1668
$legend{tag} = "legend";
1669
$legend{class} = "padded";
1670
$legend{style} = "display: flex; " .
1671
"flex-flow: row wrap; ";
1672
1673
my %bio; {
1674
$bio{tag} = "div";
1675
$bio{id} = "biodiv";
1676
$bio{class} = "margins-mini navbutton";
1677
if ($tab eq "bio") { $bio{class} .= "_selected"; }
1678
$bio{style} = "flex-grow: 1";
1679
$bio{innerHTML} = "Bio";
1680
$bio{onclick} = "toggle_display_element('bio', 'bio'); updateHiddenTabInput('bio');";
1681
}
1682
1683
my %vit; {
1684
$vit{tag} = "div";
1685
$vit{id} = "vitdiv";
1686
$vit{class} = "margins-mini navbutton";
1687
if ($tab eq "vit") { $vit{class} .= "_selected"; }
1688
$vit{style} = "flex-grow: 1";
1689
$vit{innerHTML} = "Vitals";
1690
$vit{onclick} = "toggle_display_element('vit', 'vit'); updateHiddenTabInput('vit');";
1691
}
1692
1693
my %loc; {
1694
$loc{tag} = "div";
1695
$loc{id} = "locdiv";
1696
$loc{class} = "margins-mini navbutton";
1697
if ($tab eq "loc") { $loc{class} .= "_selected"; }
1698
$loc{style} = "flex-grow: 1";
1699
$loc{innerHTML} = "Location";
1700
$loc{onclick} = "toggle_display_element('loc', 'loc'); updateHiddenTabInput('loc');";
1701
}
1702
1703
my %desc; {
1704
$desc{tag} = "div";
1705
$desc{id} = "descdiv";
1706
$desc{class} = "margins-mini navbutton";
1707
if ($tab eq "desc") { $desc{class} .= "_selected"; }
1708
$desc{style} = "flex-grow: 1";
1709
$desc{innerHTML} = "Description";
1710
$desc{onclick} = "toggle_display_element('desc', 'desc'); updateHiddenTabInput('desc');";
1711
}
1712
1713
my %pass; {
1714
$pass{tag} = "div";
1715
$pass{id} = "pwdiv";
1716
$pass{class} = "margins-mini navbutton";
1717
if ($tab eq "pw") { $pass{class} .= "_selected"; }
1718
$pass{style} = "flex-grow: 1";
1719
$pass{innerHTML} = "Password";
1720
$pass{onclick} = "toggle_display_element('pw', 'pw'); updateHiddenTabInput('pw');";
1721
}
1722
1723
my %theme; {
1724
$theme{tag} = "div";
1725
$theme{id} = "thdiv";
1726
$theme{class} = "margins-mini navbutton";
1727
if ($tab eq "th") { $theme{class} .= "_selected"; }
1728
$theme{style} = "flex-grow: 1";
1729
$theme{innerHTML} = "Theme";
1730
$theme{onclick} = "toggle_display_element('th', 'th'); updateHiddenTabInput('th');";
1731
}
1732
1733
my %membership; {
1734
$membership{tag} = "div";
1735
$membership{id} = "sudiv";
1736
$membership{class} = "margins-mini navbutton";
1737
if ($tab eq "su") { $membership{class} .= "_selected"; }
1738
$membership{style} = "flex-grow: 1";
1739
$membership{innerHTML} = "Membership";
1740
$membership{onclick} = "toggle_display_element('su', 'su'); updateHiddenTabInput('su');";
1741
}
1742
1743
my %coins; {
1744
$coins{tag} = "div";
1745
$coins{id} = "spdiv";
1746
$coins{class} = "margins-mini navbutton";
1747
if ($tab eq "sp") { $coins{class} .= "_selected"; }
1748
$coins{style} = "flex-grow: 1";
1749
$coins{innerHTML} = "Coins";
1750
$coins{onclick} = "toggle_display_element('sp', 'sp'); updateHiddenTabInput('sp');";
1751
}
1752
1753
my %blocks; {
1754
$blocks{tag} = "div";
1755
$blocks{id} = "bldiv";
1756
$blocks{class} = "margins-mini navbutton";
1757
if ($tab eq "bl") { $blocks{class} .= "_selected"; }
1758
$blocks{style} = "flex-grow: 1";
1759
$blocks{innerHTML} = "Blocks";
1760
$blocks{onclick} = "toggle_display_element('bl', 'bl'); updateHiddenTabInput('bl');";
1761
}
1762
1763
my %hiddentabinput; {
1764
$hiddentabinput{tag} = "input";
1765
$hiddentabinput{type} = "hidden";
1766
$hiddentabinput{id} = "hiddentabinput";
1767
$hiddentabinput{name} = "tab";
1768
$hiddentabinput{value} = $tab;
1769
}
1770
1771
my %script; {
1772
$script{tag} = "script";
1773
$script{defer} = 1;
1774
$script{innerHTML} = "function updateHiddenTabInput(t, debug) {\n" .
1775
" let e = document.getElementById('hiddentabinput');\n" .
1776
" if (e) {\n" .
1777
" e.value=t\n" .
1778
" if (debug) {\n" .
1779
" console.log('Html2::Html2_account_settings::js::updateHiddenTabInput->e.value: ' + e.value);\n" .
1780
" settingsStatus(debug);\n" .
1781
" console.log('Html2::Html2_account_settings::js::updateHiddenTabInput->LET is: ' + lastElementToggled);\n" .
1782
" }\n" .
1783
" } else {\n" .
1784
" if (debug) {\n" .
1785
" console.log('Html2::Html2_account_settings::js::updateHiddenTabInput->no such id!');\n" .
1786
" console.log('Html2::Html2_account_settings::js::lastElementToggled=' + lastElementToggled);\n" .
1787
" }\n" .
1788
" }\n" .
1789
"}\n" .
1790
"\n" .
1791
"function settingsStatus(debug) {\n" .
1792
" if (!debug) return;\n" .
1793
" let s = document.getElementById('settingsStatus');\n" .
1794
" let e = document.getElementById('hiddentabinput');\n" .
1795
" if (s) {\n" .
1796
" s.innerHTML = 'LET: ' + lastElementToggled;\n" .
1797
" s.innerHTML += ' e.value: ' + e.value;\n" .
1798
" } else {\n" .
1799
" if (debug) { console.log('Html2::Html2_account_settings::js::settingsStatus->no such element!'); }\n" .
1800
" }\n" .
1801
"}\n" .
1802
"\n";
1803
}
1804
1805
$legend{innerHTML} = tag(\%bio) .
1806
tag(\%vit) .
1807
tag(\%loc) .
1808
tag(\%desc) .
1809
tag(\%pass) .
1810
tag(\%theme) .
1811
tag(\%membership) .
1812
tag(\%coins) .
1813
tag(\%blocks) .
1814
tag(\%hiddentabinput) .
1815
tag(\%script);
1816
}
1817
1818
my %body; {
1819
$body{tag} = "div";
1820
#$body{class} = "bordered";
1821
$body{style} = "grid-area: settingsbody; " .
1822
"display: flex; " .
1823
"align-items: center; " .
1824
"justify-content: center; " .
1825
"width: 100% !important; " .
1826
"height: 180px !important; " .
1827
"max-height: 180px !important; ";
1828
1829
my %centered; {
1830
my %stats = User::get_user_stats($uid);
1831
$centered{tag} = "div";
1832
#$centered{class} = "bordered";
1833
$centered{style} = "width: 100%;";
1834
1835
my %bio; {
1836
$bio{tag} = "div";
1837
$bio{class} = "center";
1838
if ($tab ne "bio") { $bio{style} .= "display: none;"; }
1839
$bio{id} = "bio";
1840
my %email; {
1841
$email{tag} = "a";
1842
$email{href} = "mailto:$stats{email}";
1843
$email{innerHTML} = $stats{email};
1844
}
1845
1846
$bio{innerHTML} = "Nickname: " . embolden($stats{nickname}) . " " .
1847
embolden("(" . Bc_sql::get_config_asWord("genders", $stats{gender})) . ", " .
1848
embolden(Bc_sql::get_config_asWord("races", $stats{race}) . ")") .
1849
br .
1850
"Email: " . tag(\%email) . " ";
1851
1852
my %button; {
1853
$button{tag} = "button";
1854
$button{type} = "button";
1855
$button{innerHTML} = "Change";
1856
$button{class} = "green";
1857
$button{onclick} = "document.location=\"/?$Bc_sql::QUERY_PAGE=$Bc_sql::CONSTANTS{CHANGE_EMAIL_PAGE}\";";
1858
}
1859
1860
$bio{innerHTML} .= tag(\%button) . br .
1861
"Birthday: " . embolden(Date::expand_date($stats{dob})) . " " .
1862
Date::determine_zodiac($stats{dob}, "i") . " " .
1863
embolden("(" . Date::determine_zodiac($stats{dob}, "") . ")") .
1864
br;
1865
1866
my %showdob; {
1867
$showdob{tag} = "div";
1868
$showdob{class} = "text-align-right";
1869
my %doblabel; {
1870
$doblabel{tag} = "label";
1871
$doblabel{for} = "show_bday";
1872
1873
my %dob; {
1874
$dob{tag} = "input";
1875
$dob{type} = "checkbox";
1876
$dob{id} = "show_bday";
1877
$dob{name} = "show_bday";
1878
if ($stats{showbday} eq 2) { $dob{checked} = 1; }
1879
}
1880
1881
$doblabel{innerHTML} = tag(\%dob) . " Make Birthday Public";
1882
}
1883
1884
$showdob{innerHTML} = tag(\%doblabel);
1885
1886
$bio{innerHTML} .= tag(\%showdob);
1887
}
1888
1889
$bio{innerHTML} .= Html::dropdown("orientation",
1890
"Orientation",
1891
$stats{orientation},
1892
"", # class
1893
"", # onchange
1894
"", # spacing
1895
"", # extras
1896
"", # separator
1897
Html::get_config_forDropdowns("orientations", 1) # dataRef
1898
) . br;
1899
1900
my %seeking; {
1901
$seeking{tag} = "div";
1902
$seeking{style} = "display: inline";
1903
my @seekingList = Html::get_config_forDropdowns("genders");
1904
push @seekingList, "Couple=3";
1905
$seeking{innerHTML} = Html::dropdown(
1906
"seeking_gender",
1907
"I'm seeking a",
1908
$stats{seeking_gender},
1909
"", # class
1910
"", # onchange
1911
"", # spacing
1912
"", # extras
1913
"", # separator
1914
\@seekingList # dataRef
1915
);
1916
$bio{innerHTML} .= tag(\%seeking);
1917
}
1918
1919
my @stylesList = Html::get_config_forDropdowns("styles");
1920
$bio{innerHTML} .= Html::dropdown(
1921
"seeking",
1922
" for ",
1923
$stats{seeking},
1924
"",
1925
"",
1926
"",
1927
"style='width: 80px;'",
1928
"",
1929
\@stylesList
1930
);
1931
}
1932
1933
my %vit; {
1934
$vit{tag} = "div";
1935
$vit{class} = "center";
1936
if ($tab ne "vit") { $vit{style} .= "display: none;"; }
1937
$vit{id} = "vit";
1938
$vit{innerHTML} = embolden(Bc_sql::get_config_asWord("eyes", $stats{eye_clr})) . " eyed, " .
1939
embolden(Bc_sql::get_config_asWord("hair", $stats{hair_clr})) . " haired" .
1940
br .
1941
#($id, $title, $selected, $class, $onChange, $spacing, $extras, $separator, $dataRef, $add999, $special_select_arrayRef, $special_select_colour) = @_; # id && title for before select && selected item && css class && onchange event && spacing for pretty printing HTML output && extra html attributes && an ending for after the select && an array containing 'name=value' pairs for the options in the list (this ought to be a reference!) && add "999=--Any--"? (optional) && a list of options to colourize (optional) && the bg colour of the "specially selected" items
1942
Html::dropdown("height", "Height", $stats{height}, "", "", "", "", "", Html::get_config_forDropdowns("heights", 1)) .
1943
Html::dropdown("wheels", "Have wheels", $stats{wheels}, "", "", "", "", "", Html::get_config_forDropdowns("yesno", 1)) .
1944
br .
1945
Html::dropdown("weight", "Weight", $stats{weight}, "", "", "", "", "", Html::get_config_forDropdowns("weights", 1)) .
1946
Html::dropdown("smoker", "Smoking ok?", $stats{smoker}, "", "", "", "", "", Html::get_config_forDropdowns("yesno", 1)) .
1947
br;
1948
if ($stats{gender} eq 2) {
1949
# girl
1950
$vit{innerHTML} .= Html::dropdown("bust", "Bust", $stats{bust}, "", "", "", "", "", Html::get_config_forDropdowns("busts", 1));
1951
}
1952
else {
1953
# boy
1954
$vit{innerHTML} .= Html::dropdown("erection", "Erection", $stats{erection}, "", "", "", "", "", Html::get_config_forDropdowns("erections", 1));
1955
}
1956
1957
$vit{innerHTML} .= Html::dropdown("drinker", "Drinking ok?", $stats{drinker}, "", "", "", "", "", Html::get_config_forDropdowns("yesno", 1)) .
1958
br .
1959
Html::dropdown("body", "Body", $stats{body}, "", "", "", "", "", Html::get_config_forDropdowns("bodies", 1)) .
1960
Html::dropdown("drugs", "420 ok?", $stats{drugs}, "", "", "", "", "", Html::get_config_forDropdowns("yesno", 1)) .
1961
br .
1962
Html::dropdown("can_host", "Can Host", $stats{can_host}, "", "", "", "", "", Html::get_config_forDropdowns("yesno", 1));
1963
}
1964
1965
my %loc; {
1966
$loc{tag} = "div";
1967
$loc{class} = "center";
1968
if ($tab ne "loc") { $loc{style} .= "display: none;"; }
1969
$loc{id} = "loc";
1970
1971
my $locName = get_location($stats{location});
1972
1973
my %flag; {
1974
$flag{tag} = "img";
1975
my $locFlag = lc Bc_sql::get_country_name($stats{location}) . ".png";
1976
$flag{src} = "/images/flags/flag_$locFlag";
1977
$flag{width} = "80px";
1978
}
1979
1980
$loc{innerHTML} = "Your Location" .
1981
br .
1982
tag(\%flag) .
1983
br;
1984
1985
my %citydd; {
1986
$citydd{tag} = "select";
1987
}
1988
1989
my %countrydd; {
1990
$countrydd{tag} = "select";
1991
}
1992
1993
$loc{innerHTML} .= Html::display_city_names_asDropdown(
1994
Bc_sql::get_country_id($stats{location}),
1995
Bc_sql::get_city_id($stats{location}),
1996
"city", # id
1997
"City", # title/label
1998
"", # onchange
1999
"", # spacing
2000
"", # separator
2001
"", # add999
2002
"style=\"width: 100px;\"" # extras
2003
) .
2004
br .
2005
Html::display_country_names_asDropdown(
2006
Bc_sql::get_country_id($stats{location}),
2007
"country", # id
2008
"Country", # title/label
2009
"", # onchange
2010
"", # spacing
2011
"", # separator
2012
"", # add999
2013
"style=\"width: 100px;\"" # extras
2014
);
2015
}
2016
2017
my %desc; {
2018
$desc{tag} = "textarea";
2019
$desc{id} = "desc";
2020
$desc{name} = "desc";
2021
$desc{style} = "min-width: 100%; min-height: 159px;";
2022
if ($tab ne "desc") { $desc{style} .= "display: none;"; }
2023
$desc{innerHTML} = $stats{description};
2024
}
2025
2026
my %pass; {
2027
my %title; {
2028
$title{tag} = "div";
2029
$title{class} = "subnavbar_dark";
2030
$title{innerHTML} = "Change Password";
2031
}
2032
2033
$pass{tag} = "div";
2034
$pass{class} = "text-align-center";
2035
if ($tab ne "pw") { $pass{style} .= "display: none;"; }
2036
$pass{id} = "pw";
2037
2038
my %pw; {
2039
$pw{tag} = "input";
2040
$pw{name} = "currentpw";
2041
$pw{type} = "password";
2042
$pw{style} = "width: 123px;";
2043
$pw{label} = "Current Password ";
2044
}
2045
2046
my %newpw; {
2047
$newpw{tag} = "input";
2048
$newpw{name} = "newpw";
2049
$newpw{type} = "password";
2050
$newpw{style} = "width: 123px;";
2051
$newpw{label} = "New Password ";
2052
}
2053
2054
my %again; {
2055
$again{tag} = "input";
2056
$again{name} = "retypedpw";
2057
$again{type} = "password";
2058
$again{style} = "width: 123px;";
2059
$again{label} = "Password Again ";
2060
}
2061
2062
$pass{innerHTML} = tag(\%title) . br . br .
2063
tag(\%pw) . br .
2064
tag(\%newpw) . br .
2065
tag(\%again);
2066
}
2067
2068
my %theme; {
2069
$theme{tag} = "div";
2070
if ($tab ne "th") { $theme{style} = "display: none;"; }
2071
$theme{id} = "th";
2072
2073
my %dd; {
2074
$dd{tag} = "select";
2075
$dd{name} = "user_theme";
2076
$dd{size} = "2";
2077
$dd{style} = "width: 130px; height: 150px; margin: 0;";
2078
2079
# and now...the "options"!
2080
2081
my @optionsList = Html::get_themes();
2082
my @purchases = Html::get_theme_purchases();
2083
if (@optionsList) {
2084
foreach my $option (@optionsList) {
2085
my ($opvalue, $opname, $premium) = split("=", $option);
2086
my $ok = 0;
2087
2088
if (not User::isUserAdmin()) {
2089
if ($premium eq 2) {
2090
foreach my $purchase (@purchases) {
2091
my ($pvalue, $pname) = split("=", $purchase);
2092
if ($pname eq $opname) { $ok = 1; }
2093
}
2094
} else {
2095
$ok = 1; # admins get to see {EDITOR} themes
2096
}
2097
} else {
2098
# no one under admin can see {EDITOR} themes
2099
if ($opname !~ /^\{EDITOR\}/) { $ok = 1; }
2100
}
2101
2102
if ($ok) {
2103
my %option; {
2104
$option{tag} = "option";
2105
$option{value} = $opvalue;
2106
$option{innerHTML} = $opname;
2107
}
2108
2109
$dd{innerHTML} .= tag(\%option);
2110
}
2111
}
2112
} else {
2113
my %option; {
2114
$option{tag} = "option";
2115
$option{innerHTML} = "no list!";
2116
}
2117
2118
$dd{innerHTML} = tag(\%option);
2119
}
2120
}
2121
2122
my %blurb; {
2123
$blurb{tag} = "div";
2124
$blurb{innerHTML} = "<a href='?" . $Bc_sql::QUERY_PAGE . "=" . Bc_sql::get_constant("STORE_PAGE") . "'>Buy</a> a premium theme!<br><small><br></small>" .
2125
"Premium themes come with " .
2126
"tasteful backgrounds and " .
2127
"can be seen on searches and " .
2128
"your profile!";
2129
}
2130
2131
$theme{innerHTML} = "<table border=0 cellpadding=0 cellspacing=0><tr><td>" .
2132
tag(\%dd) .
2133
"</td><td class=spacerx_large></td><td>" .
2134
tag(\%blurb) .
2135
"</td></tr></table>";
2136
}
2137
2138
my %membership; {
2139
$membership{tag} = "div";
2140
if ($tab ne "su") { $membership{style} .= " display: none;"; }
2141
$membership{id} = "su";
2142
$membership{innerHTML} = "Security Level: " . embolden(Bc_sql::get_security_asWord_friendly($stats{security})) .
2143
br .
2144
"Member Since: " . embolden(Date::Date::expand_date($stats{enrolled})) .
2145
br . br;
2146
2147
my %status; {
2148
my $expires = $stats{enrolled};
2149
if (User::isUserSuperAdmin()) {
2150
$expires = "Never";
2151
} else {
2152
if ($stats{subscription_type} eq "2") { # monthly
2153
$expires = Date::add_date($stats{enrolled}, "1", "m");
2154
} elsif ($stats{subscription_type} eq "3") { # yearly
2155
$expires = Date::add_date($stats{enrolled}, "1", "y");
2156
} elsif ($stats{subscription_type} eq "4") { # persistent
2157
$expires = "Never";
2158
} elsif ($stats{subscription_type} eq "5") { # 6months
2159
$expires = Date::add_date($stats{enrolled}, "6", "m");
2160
}
2161
2162
if ($stats{subscription_type} eq "1") {
2163
# basic (no subscription!)
2164
$expires = "Non-Subscriber";
2165
} else {
2166
$expires = Date::expand_date($stats{enrolled});
2167
}
2168
}
2169
$status{tag} = "div";
2170
$status{innerHTML} = "<table border=0 cellpadding=0 cellspacing=0 width=\"100%\"><tr><td colspan=3 align=center>" .
2171
" <div class='subnavbar'>Status</div>" .
2172
"</td><tr><td class=spacery colspan=3>" .
2173
"</td></tr><tr><td class=center>" .
2174
" Expires" .
2175
"</td><td class=center>" .
2176
" Type" .
2177
"</td></tr><tr><td class=center>";
2178
$status{innerHTML} .= embolden($expires);
2179
$status{innerHTML} .= "</td><td class=center>";
2180
$status{innerHTML} .= embolden(ucfirst(Bc_sql::Bc_sql::get_config_asWord("membership_types", $stats{subscription_type})));
2181
$status{innerHTML} .= "</td></tr></table>";
2182
2183
}
2184
2185
$membership{innerHTML} .= tag(\%status);
2186
}
2187
2188
my %coins; {
2189
$coins{tag} = "div";
2190
$coins{class} = "center";
2191
if ($tab ne "sp") { $coins{style} = "display: none;"; }
2192
$coins{id} = "sp";
2193
2194
my $sql = "select * from coins where ID=" . $Bc_sql::DB->quote($stats{ID});
2195
my $result = Bc_sql::sql_execute($sql, "Html2::Html2_account_settings::get coins");
2196
if (ref $result eq "HASH") {
2197
$coins{innerHTML} = "You have" . br .
2198
embolden($result->{points}) . " coins";
2199
} else {
2200
$coins{innerHTML} = "not cool!";
2201
}
2202
2203
my %link; {
2204
$link{tag} = "a";
2205
$link{href} = "/?" .
2206
$Bc_sql::QUERY_PAGE . "=" . Bc_sql::get_constant("STORE_PAGE");
2207
$link{innerHTML} = "Spend Some!";
2208
}
2209
2210
$coins{innerHTML} .= br .
2211
tag(\%link);
2212
}
2213
2214
my %blocks; {
2215
$blocks{tag} = "div";
2216
$blocks{class} = "center";
2217
if ($tab ne "bl") { $blocks{style} .= " display: none;"; }
2218
$blocks{id} = "bl";
2219
2220
my %title; {
2221
$title{tag} = "div";
2222
$title{class} = "subnavbar_dark";
2223
$title{innerHTML} = "Cock Blocks";
2224
}
2225
2226
$blocks{innerHTML} = tag(\%title) . br . br;
2227
2228
# get a list of blocked users for this user
2229
my @blocked = User::get_user_blocked_users($stats{ID});
2230
2231
if (@blocked) {
2232
my %blockedList; {
2233
$blockedList{tag} = "div";
2234
$blockedList{class} = "padded sunken scrolling_both text-align-left";
2235
#$blockedList{class} .= " bordered";
2236
$blockedList{style} = "height: 100px; width: 75%;";
2237
2238
foreach my $block (@blocked) {
2239
my %bStats = User::get_user_stats($block);
2240
2241
my %removeBlock; {
2242
$removeBlock{tag} = "button";
2243
$removeBlock{type} = "button";
2244
$removeBlock{class} = "green";
2245
$removeBlock{title} = "Unblock";
2246
$removeBlock{onclick} = "document.location.href='/block.pl?" .
2247
Bc_sql::get_constant("QUERY_UID") . "=$bStats{ID}&r=1';";
2248
2249
my %img; {
2250
$img{tag} = "img";
2251
$img{src} = "/images/site/block-remove2.png";
2252
$img{height} = "16";
2253
}
2254
$removeBlock{innerHTML} = tag(\%img);
2255
}
2256
2257
# the user's nickname could be pretty long, so
2258
# we will "shorten" it.
2259
my %profile; {
2260
$profile{tag} = "a";
2261
$profile{title} = "View " . Bc_misc::word_as_possessive($bStats{nickname}) . " Profile";
2262
$profile{href} = "/?" . $Bc_sql::QUERY_PAGE . "=" .
2263
Bc_sql::get_constant("PROFILE_PAGE") .
2264
"&" . Bc_sql::get_constant("QUERY_UID") . "=" .
2265
$bStats{ID};
2266
$profile{innerHTML} = Bc_misc::Bc_misc::shorten_str($bStats{nickname}, 15);
2267
}
2268
2269
$blockedList{innerHTML} .= tag(\%profile) .
2270
" " .
2271
tag(\%removeBlock) .
2272
br;
2273
}
2274
$blockedList{innerHTML} =~ s/<br>$//; # remove trailing <br> for fun
2275
}
2276
2277
$blocks{innerHTML} .= tag(\%blockedList);
2278
} else {
2279
$blocks{innerHTML} .= "No Blocked Users" . br .
2280
embolden("Good Job!");
2281
}
2282
}
2283
2284
$centered{innerHTML} = tag(\%bio) .
2285
tag(\%vit) .
2286
tag(\%loc) .
2287
tag(\%desc) .
2288
tag(\%pass) .
2289
tag(\%theme) .
2290
tag(\%membership) .
2291
tag(\%coins) .
2292
tag(\%blocks);
2293
}
2294
2295
$body{innerHTML} = tag(\%centered);
2296
}
2297
2298
my %buttons; {
2299
$buttons{tag} = "div";
2300
#$buttons{class} = "bordered";
2301
$buttons{style} = "grid-area: settingsbuttons; " .
2302
"display: grid; " .
2303
"grid-template-columns: 1fr 1fr 1fr;";
2304
2305
my %savebutton; {
2306
my %button; {
2307
$button{tag} = "button";
2308
$button{type} = "submit";
2309
$button{class} = "green";
2310
$button{innerHTML} = "Save";
2311
}
2312
2313
$savebutton{tag} = "div";
2314
$savebutton{innerHTML} = tag(\%button);
2315
}
2316
2317
my %status; {
2318
$status{tag} = "div";
2319
$status{class} = "text-align-center";
2320
$status{id} = "settingsStatus";
2321
}
2322
2323
my %resetbutton; {
2324
my %button; {
2325
$button{tag} = "button";
2326
$button{type} = "button";
2327
$button{class} = "yellow";
2328
$button{innerHTML} = "Reset";
2329
}
2330
2331
$resetbutton{tag} = "div";
2332
$resetbutton{class} = "text-align-right";
2333
$resetbutton{innerHTML} = tag(\%button);
2334
}
2335
2336
$buttons{innerHTML} = tag(\%savebutton) .
2337
tag(\%status) .
2338
tag(\%resetbutton);
2339
}
2340
2341
my %script; {
2342
$script{tag} = "script";
2343
$script{defer} = 1;
2344
$script{innerHTML} = "lastElementToggled = \"";
2345
if ($tab eq "bio" ) { $script{innerHTML} .= "bio"; }
2346
elsif ($tab eq "vit") { $script{innerHTML} .= "vit"; }
2347
elsif ($tab eq "loc") { $script{innerHTML} .= "loc"; }
2348
elsif ($tab eq "desc") { $script{innerHTML} .= "desc"; }
2349
elsif ($tab eq "pw") { $script{innerHTML} .= "pw"; }
2350
elsif ($tab eq "th") { $script{innerHTML} .= "th"; }
2351
elsif ($tab eq "su") { $script{innerHTML} .= "su"; }
2352
elsif ($tab eq "sp") { $script{innerHTML} .= "sp"; }
2353
elsif ($tab eq "bl") { $script{innerHTML} .= "bl"; }
2354
else { $script{innerHTML} = "bio"; }
2355
$script{innerHTML} .= "\";\n" .
2356
"console.log('Html2::Html2_account_settings::js::init->LET is: ' + lastElementToggled);\n" .
2357
"//settingsStatus(1);\n";
2358
}
2359
2360
$fs{innerHTML} = tag(\%legend) .
2361
tag(\%body) .
2362
tag(\%buttons) .
2363
tag(\%script);
2364
}
2365
2366
$container{innerHTML} = tag(\%fs);
2367
}
2368
2369
$rv = tag(\%container);
2370
2371
return $rv; # a scalar of HTML code
2372
#usage: $output .= Html2_account_settings();
2373
}
2374
2375
########################
2376
push @EXPORT_OK, "mod_messages_editor";
2377
sub mod_messages_editor(;$$) {
2378
#*
2379
# editor for messages that moderators
2380
# can send to users.
2381
# if a msgid is not supplied, a list of
2382
# messages to edit is displayed
2383
#*
2384
my ($msgid, $debug) = @_; # a msgid to edit (optional) && to enable/disable debug info (optional)
2385
my $rv = "";
2386
2387
my %title; {
2388
$title{tag} = "div";
2389
$title{class} = "center";
2390
2391
my %text; {
2392
$text{tag} = "div";
2393
$text{class} = "subnavbar_dark nowrap";
2394
$text{innerHTML} = "Moderator Messages Editor";
2395
}
2396
2397
$title{innerHTML} = tag(\%text);
2398
}
2399
2400
$rv .= tag(\%title) . br;
2401
2402
if ($msgid) {
2403
# msg editor
2404
my $sql = "select * from mod_msgs where ID=" . $Bc_sql::DB->quote($msgid);
2405
my $results = Bc_sql::sql_execute($sql, "Html2::mod_messages_editor()->$sql", 1); # returns an array ref
2406
if (@$results eq 1) {
2407
my $msg = @$results[0]; # msg should be a hash reference
2408
if (ref $msg eq "HASH") {
2409
my %fs; {
2410
$fs{tag} = "fieldset";
2411
$fs{style} = "max-width: 320px;";
2412
2413
my %legend; {
2414
$legend{tag} = "legend";
2415
$legend{class} = "nowrap padded";
2416
2417
my %id; {
2418
$id{tag} = "div";
2419
$id{class} = "inline blue-panel rounded margin";
2420
if ($debug)
2421
{ $id{innerHTML} = "#" . $msg->{ID}; } else
2422
{ $id{innerHTML} = "Name"; }
2423
2424
my %hidden; {
2425
$hidden{tag} = "input";
2426
$hidden{type} = "hidden";
2427
$hidden{name} = Bc_sql::get_constant("QUERY_MSGID");
2428
$hidden{value} = $msg->{ID};
2429
}
2430
2431
$id{innerHTML} .= tag(\%hidden);
2432
}
2433
2434
my %name; {
2435
$name{tag} = "input";
2436
$name{class} = "margins-none";
2437
$name{name} = "name";
2438
$name{required} = 1;
2439
$name{placeholder} = "message name";
2440
$name{value} = $msg->{name};
2441
}
2442
2443
$legend{innerHTML} = tag(\%id) .
2444
" " .
2445
tag(\%name);
2446
}
2447
2448
my %value; {
2449
$value{tag} = "textarea";
2450
$value{name} = "value";
2451
$value{style} = "width: 100%; height: 150px; resize: none;";
2452
if (not $debug) { $value{autofocus} = 1; }
2453
$value{required} = 1;
2454
$value{placeholder} = "Please fill in this field";
2455
$value{innerHTML} = $msg->{value};
2456
}
2457
2458
my %buttons; {
2459
$buttons{tag} = "div";
2460
$buttons{class} = "nowrap";
2461
$buttons{style} = "display: flex;";
2462
2463
my %save; {
2464
$save{tag} = "button";
2465
$save{type} = "submit";
2466
$save{class} = "green";
2467
$save{innerHTML} = "Save";
2468
}
2469
2470
my %reset; {
2471
$reset{tag} = "button";
2472
$reset{type} = "reset";
2473
$reset{class} = "purple";
2474
$reset{innerHTML} = "Reset";
2475
}
2476
2477
my %cancel; {
2478
$cancel{tag} = "button";
2479
$cancel{type} = "button";
2480
$cancel{class} = "yellow";
2481
2482
$cancel{onclick} = "document.location='";
2483
if ($debug) {
2484
$cancel{onclick} .= "/debug.pl?" . Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2#subtests";
2485
} else {
2486
$cancel{onclick} .= "/?" .
2487
$Bc_sql::QUERY_PAGE . "=" .
2488
Bc_sql::get_constant("ADMIN_PAGE") . "&" .
2489
Bc_sql::get_constant("QUERY_ADMIN_PAGE") . "=" .
2490
Bc_sql::get_constant("ADMIN_SYSTEM_PAGE") . "&" .
2491
"t=modmsgs";
2492
}
2493
$cancel{onclick} .= "'";
2494
2495
$cancel{title} = $cancel{onclick};
2496
$cancel{innerHTML} = "Cancel";
2497
}
2498
2499
my %dele; {
2500
$dele{tag} = "button";
2501
$dele{type} = "button";
2502
$dele{class} = "red";
2503
$dele{onclick} = "document.location='";
2504
if ($debug) {
2505
} else {
2506
$dele{onclick} .= "/updatemodmsg.pl?" .
2507
Bc_sql::get_constant("QUERY_MSGID") . "=" .
2508
$msg->{ID} . "&" .
2509
"del=1";
2510
}
2511
$dele{onclick} .= "'";
2512
$dele{title} = $dele{onclick};
2513
$dele{innerHTML} = "Delete";
2514
}
2515
2516
$buttons{innerHTML} = tag(\%save);
2517
$buttons{innerHTML} .= tag(\%reset);
2518
$buttons{innerHTML} .= tag(\%cancel);
2519
$buttons{innerHTML} .= tag(\%dele);
2520
}
2521
2522
my %form; {
2523
$form{tag} = "form";
2524
$form{method} = "post";
2525
if ($debug)
2526
{ $form{action} = "/showparams.pl"; } else
2527
{ $form{action} = "/updatemodmsg.pl"; }
2528
$form{innerHTML} = tag(\%legend) .
2529
tag(\%value) .
2530
tag(\%buttons);
2531
}
2532
2533
$fs{innerHTML} = tag(\%form);
2534
}
2535
2536
$rv .= tag(\%fs);
2537
}
2538
} else {
2539
if ($msgid eq "new") {
2540
my %title; {
2541
$title{tag} = "div";
2542
$title{class} = "padded-mini green-panel rounded";
2543
$title{innerHTML} = "New";
2544
}
2545
2546
my %form; {
2547
$form{tag} = "form";
2548
$form{method} = "post";
2549
if (not $debug)
2550
{ $form{action} = "/updatemodmsg.pl"; } else
2551
{ $form{action} = "/showparams.pl"; }
2552
2553
my %msgid; {
2554
$msgid{tag} = "input";
2555
$msgid{type} = "hidden";
2556
$msgid{name} = Bc_sql::get_constant("QUERY_MSGID");
2557
$msgid{value} = "new";
2558
}
2559
2560
my %msgname; {
2561
$msgname{tag} = "input";
2562
$msgname{name} = "name";
2563
if (not $debug) { $msgname{autofocus} = 1; }
2564
$msgname{required} = 1;
2565
$msgname{placeholder} = "message name";
2566
$msgname{label} = "Message Name " . small("(not a subject line)" . br);
2567
}
2568
2569
my %msg; {
2570
$msg{tag} = "textarea";
2571
$msg{name} = "value";
2572
$msg{label} = "Message Text" . br;
2573
$msg{style} = "width: 200px; height: 150px; resize: none;";
2574
$msg{required} = 1;
2575
$msg{placeholder} = "message text";
2576
}
2577
2578
my %buttons; {
2579
$buttons{tag} = "div";
2580
$buttons{style} = "display: flex; justify-content: space-between";
2581
2582
my %save; {
2583
$save{tag} = "button";
2584
$save{type} = "submit";
2585
$save{class} = "green";
2586
$save{innerHTML} = "Save";
2587
}
2588
2589
my %cancel; {
2590
$cancel{tag} = "button";
2591
$cancel{type} = "button";
2592
$cancel{class} = "red";
2593
$cancel{style} = "margin-right: 0;";
2594
2595
$cancel{onclick} = "document.location='";
2596
if (not $debug) {
2597
$cancel{onclick} .= "/?" . $Bc_sql::QUERY_PAGE . "=" .
2598
Bc_sql::get_constant("ADMIN_PAGE") . "&" .
2599
Bc_sql::get_constant("QUERY_ADMIN_PAGE") . "=" .
2600
Bc_sql::get_constant("ADMIN_SYSTEM_PAGE") . "&" .
2601
"t=modmsgs";
2602
} else {
2603
$cancel{onclick} .= "/debug.pl?" . Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2#subtests";
2604
}
2605
$cancel{onclick} .= "';";
2606
2607
$cancel{title} = $cancel{onclick};
2608
$cancel{innerHTML} = "Cancel";
2609
}
2610
2611
$buttons{innerHTML} = tag(\%save) .
2612
tag(\%cancel);
2613
}
2614
2615
$form{innerHTML} = tag(\%msgid) .
2616
tag(\%msgname) . br .
2617
tag(\%msg) . br .
2618
tag(\%buttons);
2619
}
2620
2621
my %wrapper; {
2622
$wrapper{tag} = "div";
2623
$wrapper{class} = "center";
2624
$wrapper{innerHTML} = tag(\%title) .
2625
tag(\%form);
2626
}
2627
2628
$rv .= tag(\%wrapper);
2629
2630
} else {
2631
$rv .= "msgid " . embolden($msgid) . " is invalid!";
2632
}
2633
}
2634
}
2635
else {
2636
# show a list of msgs
2637
my $sql = "select * from mod_msgs order by name";
2638
my $results = Bc_sql::sql_execute($sql, "Html2::mod_messages_editor()->$sql", 1); # returns array ref
2639
if (@$results) {
2640
my %msgcount; {
2641
$msgcount{tag} = "div";
2642
$msgcount{innerHTML} = "Found " . embolden((@$results)) . " " . Bc_misc::pluralize("message", @$results) . hr;
2643
}
2644
2645
$rv .= tag(\%msgcount);
2646
2647
my %list; {
2648
$list{tag} = "div";
2649
$list{class} = "sunken text-align-left scrolling_both";
2650
$list{style} = "max-height: 150px; height: 150px; width: 100%;";
2651
foreach my $result (@$results) {
2652
my %link; {
2653
$link{tag} = "a";
2654
$link{class} = "nowrap";
2655
if (not $debug) {
2656
$link{href} = "/?" . $Bc_sql::QUERY_PAGE . "=" .
2657
Bc_sql::get_constant("ADMIN_PAGE") . "&" .
2658
Bc_sql::get_constant("QUERY_ADMIN_PAGE") . "=" .
2659
Bc_sql::get_constant("ADMIN_SYSTEM_PAGE") . "&" .
2660
Bc_sql::get_constant("QUERY_MSGID") . "=" .
2661
$result->{ID} . "&" .
2662
"t=modmsgs";
2663
$link{innerHTML} = $result->{name};
2664
} else {
2665
$link{href} = "/debug.pl?" . Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2&" .
2666
Bc_sql::get_constant("QUERY_MSGID") . "=" .
2667
$result->{ID} . "#subtests";
2668
$link{innerHTML} = embolden($result->{ID}) . ": " . $result->{name};
2669
}
2670
}
2671
2672
$list{innerHTML} .= tag(\%link) . br;
2673
}
2674
2675
$rv .= tag(\%list);
2676
}
2677
2678
my %nmsg; {
2679
$nmsg{tag} = "button";
2680
$nmsg{type} = "button";
2681
$nmsg{class} = "green";
2682
$nmsg{onclick} = "document.location='";
2683
2684
if (not $debug) {
2685
$nmsg{onclick} .= "/?" . Bc_sql::get_constant("QUERY_PAGE") . "=" .
2686
Bc_sql::get_constant("ADMIN_PAGE") . "&" .
2687
Bc_sql::get_constant("QUERY_ADMIN_PAGE") . "=" .
2688
Bc_sql::get_constant("ADMIN_SYSTEM_PAGE") . "&" .
2689
Bc_sql::get_constant("QUERY_MSGID") . "=new" .
2690
"&t=modmsgs";
2691
} else {
2692
$nmsg{onclick} .= "/debug.pl?" . Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2&";
2693
$nmsg{onclick} .= Bc_sql::get_constant("QUERY_MSGID") . "=new#subtests";
2694
}
2695
$nmsg{onclick} .= "';";
2696
$nmsg{title} = $nmsg{onclick};
2697
2698
$nmsg{innerHTML} = "Add New Message";
2699
}
2700
2701
$rv .= hr . tag(\%nmsg);
2702
2703
} else {
2704
$rv .= "no msgs to edit!";
2705
}
2706
}
2707
2708
my %wrapper; {
2709
$wrapper{tag} = "div";
2710
$wrapper{class} = "subnavbar padded";
2711
$wrapper{innerHTML} = $rv;
2712
}
2713
2714
$rv = tag(\%wrapper);
2715
2716
return $rv; # a scalar
2717
#usage: $output .= mod_messages_editor(\%attributes);
2718
}
2719
2720
########################
2721
push @EXPORT_OK, "display_user_profile";
2722
sub display_user_profile(;$) {
2723
#*
2724
# displays a user's profile
2725
# $params is a hash reference.
2726
# keys are many, and this isn't yet exhaustive:
2727
# PRIMARY:
2728
# {uid} = a user ID (defaults to $Bc_sql::LOGGEDIN)
2729
# {card} = condense to business card size (0=disabled, anything else=enabled)
2730
# {mini} = condense to mini size (0=disabled, anything else=enabled - will override {card})
2731
# {notheme} = apply user theme (0=disabled, anything else=enabled)
2732
2733
# DEBUGGING (0=disable option, anything else=enable option):
2734
# {debug} = show debugger
2735
# {debug_borders} = show borders
2736
# {debug_attr} = show attributes param
2737
# {debug_tdata} = show theme data
2738
# {debug_borders} = show borders
2739
# {debug_onclicks} = include onclick string in title attribute
2740
# {debug_actasuser} = act as if a user is viewing this profile
2741
# {debug_actasguest} = act as if a guest (!$Bc_sql::LOGGEDIN) is viewing this profile
2742
#*
2743
my ($attributes) = @_; # a hash reference of attributes (optional, see description)
2744
my $rv = "";
2745
2746
my $admin = 0;
2747
my $moderator = 0;
2748
2749
if (User::isUserAdmin()) { $admin = 1; }
2750
if (User::isUserModerator()) { $moderator = 1; }
2751
2752
my $url = Bc_misc::referrer();
2753
my %params = Bc_misc::get_params_asHash();
2754
if (Bc_sql::is_debuggerAllowed()) {
2755
$url =~ s/^\///;
2756
if ($url !~ /^\/debug.pl/) { $url = "/debug.pl?" . Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2" . $url; }
2757
if ($params{dbg} and $params{dbu}) { delete $params{dbu}; }
2758
2759
if ($params{db}) { $attributes->{debug} = 1; $url = Bc_misc::add_param("db", 1, $url); } else { delete $attributes->{debug}; $url = Bc_misc::remove_param("db", $url); }
2760
if ($params{dba}) { $attributes->{debug_attributes} = 1; $url = Bc_misc::add_param("dba", 1, $url); } else { delete $attributes->{debug_attributes}; $url = Bc_misc::remove_param("dba", $url); }
2761
if ($params{dbb}) { $attributes->{debug_borders} = 1; $url = Bc_misc::add_param("dbb", 1, $url); } else { delete $attributes->{debug_borders}; $url = Bc_misc::remove_param("dbb", $url); }
2762
if ($params{dbc}) { $attributes->{debug_jsconsole} = 1; $url = Bc_misc::add_param("dbc", 1, $url); } else { delete $attributes->{debug_jsconsole}; $url = Bc_misc::remove_param("dbc", $url); }
2763
if ($params{dbg}) { $attributes->{debug_actasguest} = 1; $url = Bc_misc::add_param("dbg", 1, $url); } else { delete $attributes->{debug_actasguest}; $url = Bc_misc::remove_param("dbg", $url); }
2764
if ($params{dbo}) { $attributes->{debug_onclicks} = 1; $url = Bc_misc::add_param("dbo", 1, $url); } else { delete $attributes->{debug_onclicks}; $url = Bc_misc::remove_param("dbo", $url); }
2765
if ($params{dbt}) { $attributes->{debug_themedata} = 1; $url = Bc_misc::add_param("dbt", 1, $url); } else { delete $attributes->{debug_themedata}; $url = Bc_misc::remove_param("dbt", $url); }
2766
if ($params{dbu}) { $attributes->{debug_actasuser} = 1; $url = Bc_misc::add_param("dbu", 1, $url); } else { delete $attributes->{debug_actasuser}; $url = Bc_misc::remove_param("dbu", $url); }
2767
if ($params{dbs}) { $attributes->{debug_stats} = 1; $url = Bc_misc::add_param("dbs", 1, $url); } else { delete $attributes->{debug_stats}; $url = Bc_misc::remove_param("dbs", $url); }
2768
} else {
2769
delete $attributes->{debug};
2770
delete $attributes->{debug_attributes};
2771
delete $attributes->{debug_themedata};
2772
delete $attributes->{debug_borders};
2773
delete $attributes->{debug_onclicks};
2774
delete $attributes->{debug_actasuser};
2775
delete $attributes->{debug_actasguest};
2776
delete $attributes->{debug_jsconsole};
2777
delete $attributes->{debug_stats};
2778
}
2779
2780
if ($params{$Bc_sql::QUERY_UID}) {
2781
$attributes->{$Bc_sql::QUERY_UID} = $params{$Bc_sql::QUERY_UID};
2782
$url = Bc_misc::add_param($Bc_sql::QUERY_UID, $params{$Bc_sql::QUERY_UID}, $url);
2783
} else {
2784
#delete $attributes->{$Bc_sql::QUERY_UID};
2785
$url = Bc_misc::remove_param($Bc_sql::QUERY_UID, $url);
2786
}
2787
2788
if (ref $attributes eq "HASH") {
2789
if ($attributes->{debug}) {
2790
if ($attributes->{debug_actasuser}) { $admin = 0; $moderator = 0; }
2791
if ($attributes->{debug_actasguest}) { $admin = 0; $moderator = 0; $Bc_sql::LOGGEDIN = 0; }
2792
}
2793
if (not $Bc_sql::LOGGEDIN) { $attributes->{mini} = 1; }
2794
2795
if (Bc_sql::user_exists($attributes->{uid})) {
2796
my %stats = User::get_user_stats($attributes->{uid});
2797
my %theme = User::get_user_theme_data($attributes->{uid});
2798
2799
# show attributes, and/or theme data, and/or user stats
2800
# when debug = 1
2801
if ($attributes->{debug}) {
2802
################
2803
## ATTRIBUTES ##
2804
################
2805
if ($attributes->{debug_attributes}) {
2806
my %paramsfs; {
2807
$paramsfs{tag} = "fieldset";
2808
$paramsfs{class} = "padded green-panel";
2809
$paramsfs{style} = "margin-bottom: 15px; width: 100%;";
2810
$paramsfs{innerHTML} = "";
2811
2812
my %legend; {
2813
$legend{tag} = "legend";
2814
$legend{innerHTML} = "Attributes";
2815
2816
$paramsfs{innerHTML} .= tag(\%legend);
2817
}
2818
2819
my %scrollingdiv; {
2820
$scrollingdiv{tag} = "div";
2821
$scrollingdiv{class} = "padded sunken scrolling_vertical blue-bg nobgimg";
2822
$scrollingdiv{style} = "height: 80px; margin-bottom: 5px;";
2823
2824
foreach my $key (sort keys %$attributes) {
2825
$scrollingdiv{innerHTML} .= "\$attributes->{\"$key\"}=$attributes->{$key}" . br;
2826
} # end foreach param
2827
2828
$paramsfs{innerHTML} .= tag(\%scrollingdiv);
2829
}
2830
2831
$rv .= tag(\%paramsfs);
2832
} # end params div
2833
} # end if debug attributes
2834
2835
################
2836
## THEME DATA ##
2837
################
2838
if ($attributes->{debug_themedata}) {
2839
my %themedatafs; {
2840
$themedatafs{tag} = "fieldset";
2841
$themedatafs{class} = "padded yellow-panel";
2842
$themedatafs{style} = "margin-bottom: 15px; width: 100%;";
2843
$themedatafs{innerHTML} = "";
2844
2845
my %legend; {
2846
$legend{tag} = "legend";
2847
$legend{innerHTML} = "User's Theme Data (YOUR theme ID: $Bc_sql::USER_DATA->{TID})";
2848
2849
$themedatafs{innerHTML} .= tag(\%legend);
2850
} # end legend div
2851
2852
my %data; {
2853
$data{tag} = "div";
2854
$data{class} = "padded sunken scrolling_vertical blue-bg nobgimg";
2855
$data{style} = "height: 80px; margin-bottom: 5px;";
2856
if (keys %theme) {
2857
foreach my $key (sort keys %theme) {
2858
$data{innerHTML} .= "\$theme{\"$key\"}=" . $theme{$key} . br;
2859
}
2860
} else {
2861
$data{innerHTML} .= "No keys!!" . br;
2862
}
2863
2864
$themedatafs{innerHTML} .= tag(\%data);
2865
} # end data div
2866
2867
$rv .= tag(\%themedatafs);
2868
} # end theme data fieldset
2869
} # end if debug theme data
2870
2871
################
2872
## USER STATS ##
2873
################
2874
if ($attributes->{debug_stats}) {
2875
{ my %statsfs;
2876
$statsfs{tag} = "fieldset";
2877
$statsfs{class} = "padded cyan-panel";
2878
$statsfs{style} = "margin-bottom: 15px; width: 100%;";
2879
$statsfs{innerHTML} = "";
2880
2881
{ my %legend;
2882
$legend{tag} = "legend";
2883
$legend{innerHTML} = "User's Stats";
2884
2885
$statsfs{innerHTML} .= tag(\%legend);
2886
} # end legend div
2887
2888
{ my %data;
2889
$data{tag} = "div";
2890
$data{class} = "padded sunken scrolling_vertical blue-bg nobgimg";
2891
$data{style} = "height: 80px; margin-bottom: 5px;";
2892
if (keys %stats) {
2893
foreach my $key (sort keys %stats) {
2894
$data{innerHTML} .= "\$stats{\"$key\"}=" . $stats{$key} . br;
2895
}
2896
} else {
2897
$data{innerHTML} .= "No keys!!" . br;
2898
}
2899
2900
$statsfs{innerHTML} .= tag(\%data);
2901
} # end data div
2902
2903
$rv .= tag(\%statsfs);
2904
} # end user stats fieldset
2905
} # end if debug stats
2906
} # end if debug
2907
2908
# now, show the given profile
2909
my %profilecontainer; {
2910
$profilecontainer{tag} = "div";
2911
$profilecontainer{class} = "body_$stats{ID} center";
2912
if (not $attributes->{mini}) { $profilecontainer{class} .= " padded-large"; }
2913
if ((not $attributes->{mini} and $stats{ID} ne $Bc_sql::LOGGEDIN) or $theme{premium} eq 2) { $profilecontainer{class} .= " bordered_$stats{ID} rounded"; }
2914
$profilecontainer{innerHTML} = "";
2915
2916
#########
2917
## CSS ##
2918
#########
2919
my %css; {
2920
my $fade = "55"; # i'm aware, this is kind of random
2921
$css{tag} = "style";
2922
$css{innerHTML} = ".body_$stats{ID} {\n" .
2923
" display: flex;\n" .
2924
" flex-wrap: wrap;\n" .
2925
" flex-flow: row;\n" .
2926
" column-gap: 15px;\n" .
2927
" width: min-content: flex;\n" .
2928
" min-width: 290px;\n";
2929
if ($stats{ID} ne $Bc_sql::LOGGEDIN and not $attributes->{mini}) { $css{innerHTML} .= " background-color: #$theme{bg_clr}88;\n"; }
2930
if (not $attributes->{mini}) { $css{innerHTML} .= "max-width: min-content; "; }
2931
if ($theme{premium} eq 2 and $theme{bg_image} =~ /wallpaper/ and $stats{ID} ne $Bc_sql::LOGGEDIN) {
2932
$css{innerHTML} .= " background-image: url('/img.pl?s=f&i=$theme{bg_image}');\n" .
2933
" background-repeat: no-repeat;\n" .
2934
" background-size: 100% 100%;\n";
2935
}
2936
$css{innerHTML} .= " color: #$theme{body_clr};\n" .
2937
"}\n" .
2938
"\n" .
2939
".a_$stats{ID}, .a_$stats{ID}:visited, .a_$stats{ID}:hover {\n" .
2940
" color: #$theme{link_clr};\n" .
2941
"}\n" .
2942
"\n" .
2943
".title_$stats{ID} {\n" .
2944
" color: #$theme{title_clr};\n" .
2945
" font-size: var(--giant-font-size);\n" .
2946
" text-shadow: 2px 2px 1px rgba(0, 0, 0, 0.25);\n" .
2947
"}\n" .
2948
"\n" .
2949
".subtitle_$stats{ID} {\n" .
2950
" color: #$theme{subtitle_clr};\n" .
2951
" font-size: var(--large-font-size);\n" .
2952
" text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.25);\n" .
2953
"}\n" .
2954
"\n" .
2955
".fieldset_$stats{ID} {\n" .
2956
" background-color: #$theme{gradient_light};\n" .
2957
" border: 2px inset #$theme{borders_clr};\n" .
2958
" color: #$theme{body_clr};\n" .
2959
"}\n" .
2960
"\n" .
2961
".normal-panel_$stats{ID} {\n" .
2962
" box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.25);\n" .
2963
"}\n" .
2964
"\n" .
2965
".legend_$stats{ID} {\n" .
2966
" background-color: #$theme{bg_clr};\n" .
2967
" background-image: linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2968
" background-image: -o-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2969
" background-image: -ms-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2970
" background-image: -moz-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2971
" background-image: -webkit-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2972
" border: 2px inset #$theme{borders_clr};\n" .
2973
" color: #$theme{subtitle_clr};\n" .
2974
"}\n" .
2975
"\n" .
2976
".sunken_$stats{ID} {\n" .
2977
" background-color: #$theme{bg_clr};\n" .
2978
" background-image: linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2979
" background-image: -o-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2980
" background-image: -ms-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2981
" background-image: -moz-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2982
" background-image: -webkit-linear-gradient(left, #$theme{gradient_light}, #$theme{gradient_dark}, #$theme{gradient_light});\n" .
2983
" border: 2px inset #$theme{borders_clr};\n" .
2984
" color: #$theme{subtitle_clr};\n" .
2985
"}\n" .
2986
"\n" .
2987
".scrolling_vertical_$stats{ID} {\n" .
2988
" overflow-x: hidden;\n" .
2989
" overflow-y: scroll;\n" .
2990
"}\n" .
2991
"\n" .
2992
".scrolling_vertical_$stats{ID}::-webkit-scrollbar {\n" .
2993
" border: 1px inset #$theme{borders_clr};\n" .
2994
" border-radius: 5px;\n" .
2995
" background-color: #$theme{gradient_dark};\n" .
2996
" height: 8px;\n" .
2997
" width: 8px;\n" .
2998
"}\n" .
2999
"\n" .
3000
".scrolling_vertical_$stats{ID}::-webkit-scrollbar-thumb {\n" .
3001
" border: 1px outset #$theme{borders_clr};\n" .
3002
" border-radius: 5px;\n" .
3003
" background-color: #$theme{gradient_light};\n" .
3004
" width: 6px;\n" .
3005
"}\n" .
3006
"\n" .
3007
".bordered_$stats{ID} {\n" .
3008
" border: 2px outset #$theme{borders_clr};\n" .
3009
"}\n" .
3010
"\n" .
3011
".b_$stats{ID} {\n" .
3012
" color: #$theme{bold_clr};\n" .
3013
" text-shadow: 1px 1px 0 #$theme{bold_shadow_clr}88;\n" .
3014
"}\n" .
3015
"\n" .
3016
".i_$stats{ID} {\n" .
3017
" color: #$theme{body_clr};\n" .
3018
" text-shadow: 1px 1px 0 #$theme{bold_shadow_clr}88;\n" .
3019
"}\n" .
3020
"\n" .
3021
".hr_$stats{ID} {\n" .
3022
" background: #$theme{borders_clr};\n" .
3023
"}\n" .
3024
"\n" .
3025
".translucent_$stats{ID} {\n" .
3026
" background-color: #$theme{bg_clr}$fade;\n" .
3027
" background-image: linear-gradient(left, #$theme{gradient_light}$fade, #$theme{gradient_dark}$fade, #$theme{gradient_light}$fade);\n" .
3028
" background-image: -o-linear-gradient(left, #$theme{gradient_light}$fade, #$theme{gradient_dark}$fade, #$theme{gradient_light}$fade);\n" .
3029
" background-image: -ms-linear-gradient(left, #$theme{gradient_light}$fade, #$theme{gradient_dark}$fade, #$theme{gradient_light}$fade);\n" .
3030
" background-image: -moz-linear-gradient(left, #$theme{gradient_light}$fade, #$theme{gradient_dark}$fade, #$theme{gradient_light}$fade);\n" .
3031
" background-image: -webkit-linear-gradient(left, #$theme{gradient_light}$fade, #$theme{gradient_dark}$fade, #$theme{gradient_light}$fade);\n" .
3032
"}\n" .
3033
"\n" .
3034
".bordered_$stats{ID} {\n" .
3035
" border: 2px outset #$theme{borders_clr};\n" .
3036
"}\n" .
3037
"\n" .
3038
"\@media only screen and (max-width: 1200px) {\n" .
3039
" .body_$stats{ID} {\n" .
3040
" flex-wrap: wrap;\n" .
3041
" flex-flow: column;\n" .
3042
" row-gap: 15px;\n" .
3043
" }\n" .
3044
"}\n" .
3045
"\n";
3046
$profilecontainer{innerHTML} .= tag(\%css);
3047
} # end CSS
3048
3049
###################
3050
## PROFILE STATS ##
3051
###################
3052
my %fs; {
3053
if (not $attributes->{mini}) {
3054
$fs{tag} = "fieldset";
3055
$fs{class} = "fieldset_$stats{ID} center min-content";
3056
} else {
3057
$fs{tag} = "div";
3058
$fs{class} = "center";
3059
if ($attributes->{debug} and $attributes->{debug_borders}) { $fs{class} .= " bordered"; }
3060
$fs{style} = "background-color: transparent; background-image: none; width: min-content;";
3061
3062
if ($attributes->{mini}) {
3063
$fs{class} .= " padding-none";
3064
}
3065
}
3066
if ($theme{premium} eq 2) { $fs{class} .= " translucent_$stats{ID}"; }
3067
$fs{innerHTML} = "";
3068
3069
my %legend; {
3070
$legend{tag} = "legend";
3071
$legend{class} = "legend_$stats{ID} padded";
3072
if ($theme{premium} eq 2) { $legend{class} .= " translucent_$stats{ID}"; }
3073
if ($attributes->{mini}) {
3074
$legend{style} = "display: grid; " .
3075
"column-gap: 10px; " .
3076
"grid-template-columns: min-content min-content min-content; " .
3077
"grid-template-rows: min-content min-content min-content min-content; " .
3078
"grid-template-areas: 'dp nickname nickname' " .
3079
"'location age icons' " .
3080
"'comms comms comms' " .
3081
"'seeking seeking seeking'; ";
3082
} else {
3083
$legend{style} = "display: grid; " .
3084
"column-gap: 10px; " .
3085
"grid-template-columns: min-content min-content min-content min-content min-content; " .
3086
"grid-template-rows: min-content 1fr min-content min-content min-content; " .
3087
"grid-template-areas: 'dp uid uid tid tid' " .
3088
"'dp nickname nickname nickname nickname' " .
3089
"'location age age age icons' " .
3090
"'comms comms comms comms comms' " .
3091
"'seeking seeking seeking seeking seeking'; ";
3092
}
3093
$legend{innerHTML} = "";
3094
3095
my %dpdiv; {
3096
$dpdiv{tag} = "div";
3097
$dpdiv{style} = "grid-area: dp; " .
3098
"margin: auto auto; " .
3099
"min-height: 36px; ";
3100
$dpdiv{class} = "text-align-center";
3101
if ($attributes->{debug} and $attributes->{debug_borders}) { $dpdiv{class} .= " bordered"; }
3102
3103
my %dpimg; {
3104
$dpimg{tag} = "img";
3105
$dpimg{class} = "sunken_$stats{ID} sunken margins";
3106
if ($theme{premium} eq 2) { $dpimg{class} .= " translucent_$stats{ID}"; }
3107
if ($attributes->{card}) {
3108
$dpimg{class} .= " enlarge clickable";
3109
$dpimg{onclick} = "document.location='/?" . Bc_sql::get_constant("QUERY_PAGE") . "=" .
3110
Bc_sql::get_constant("PROFILE_PAGE") . "&" .
3111
Bc_sql::get_constant("QUERY_UID") . "=" . $stats{ID} .
3112
"';";
3113
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $dpimg{title} = "($dpimg{onclick})"; }
3114
}
3115
3116
my $dpid = User::get_user_dpID($stats{ID});
3117
if ($dpid) {
3118
$dpimg{src} = "/getimage.pl?id=" . User::get_user_dpID($stats{ID});
3119
if (not $attributes->{mini})
3120
{ $dpimg{src} .= "&th=1"; } else
3121
{ $dpimg{src} .= "&th=2"; }
3122
} else {
3123
$dpimg{src} = "/img.pl?i=";
3124
if ($stats{gender} eq 1) {
3125
$dpimg{src} .= "site/default_boy.png";
3126
} elsif ($stats{gender} eq 2) {
3127
$dpimg{src} .= "site/default_girl.png";
3128
} else {
3129
$dpimg{src} .= "grey/user_gray.png";
3130
}
3131
3132
if (not $attributes->{mini})
3133
{ $dpimg{src} .= "&s=dp"; } else
3134
{ $dpimg{src} .= "&s=dpm"; }
3135
}
3136
} # end dp id
3137
3138
$dpdiv{innerHTML} = tag(\%dpimg);
3139
3140
$legend{innerHTML} .= tag(\%dpdiv);
3141
} # end dp div
3142
3143
if (not $attributes->{mini} and $moderator) {
3144
my %uiddiv; {
3145
$uiddiv{tag} = "div";
3146
$uiddiv{style} = "grid-area: uid;";
3147
$uiddiv{class} = "sunken_$stats{ID} sunken small text-align-center padded-small";
3148
if ($theme{premium} eq 2) { $uiddiv{class} .= " translucent_$stats{ID}"; }
3149
$uiddiv{innerHTML} = embolden({innerHTML=>"User ID",class=>"b_$stats{ID}"}) . br .
3150
italicize({innerHTML=>$stats{ID},class=>"i_$stats{ID}"});
3151
3152
$legend{innerHTML} .= tag(\%uiddiv);
3153
} # end uid div
3154
3155
my %tiddiv; {
3156
$tiddiv{tag} = "div";
3157
$tiddiv{style} = "grid-area: tid;";
3158
$tiddiv{class} = "sunken_$stats{ID} sunken small text-align-center padded-small";
3159
if ($theme{premium} eq 2) { $tiddiv{class} .= " translucent_$stats{ID}"; }
3160
$tiddiv{innerHTML} = embolden({innerHTML=>"Theme ID",class=>"b_$stats{ID}"}) . br .
3161
italicize({innerHTML=>$stats{TID},class=>"i_$stats{ID}"});
3162
3163
$legend{innerHTML} .= tag(\%tiddiv);
3164
} # end tid div
3165
} # end if not $attributes->{mini} and $moderator
3166
3167
my %locationdiv; {
3168
$locationdiv{tag} = "div";
3169
$locationdiv{style} = "grid-area: location;";
3170
$locationdiv{class} = "center copyright text-align-left nowrap embolden sunken_$stats{ID} sunken padded-small";
3171
if ($theme{premium} eq 2) { $locationdiv{class} .= " translucent_$stats{ID}"; }
3172
if ($attributes->{debug} and $attributes->{debug_borders}) { $locationdiv{class} .= " bordered"; }
3173
$locationdiv{innerHTML} = "";
3174
3175
my %cflag; {
3176
$cflag{tag} = "img";
3177
$cflag{src} = "/images/flags/flag_" . lc Bc_sql::get_country_name($stats{location}) . ".png";
3178
$cflag{height} = "16";
3179
3180
$locationdiv{innerHTML} .= tag(\%cflag) . " ";
3181
}
3182
3183
$locationdiv{innerHTML} .= Bc_sql::get_city_name($stats{location});
3184
$legend{innerHTML} .= tag(\%locationdiv);
3185
} # end location div
3186
3187
my %nickname; {
3188
$nickname{tag} = "div";
3189
$nickname{class} = "text-align-center title_$stats{ID} nowrap italic margins";
3190
if ($attributes->{debug} and $attributes->{debug_borders}) { $nickname{class} .= " bordered"; }
3191
$nickname{style} = "grid-area: nickname; " .
3192
"margin: auto auto; ";
3193
$nickname{innerHTML} = Bc_misc::shorten_str($stats{nickname}, 12);
3194
if ($stats{nickname} ne Bc_misc::shorten_str($stats{nickname}, 12)) {
3195
$nickname{title} = $stats{nickname};
3196
}
3197
3198
if ($Bc_sql::LOGGEDIN) {
3199
# now, is the nickname flagged?
3200
my %flag; {
3201
$flag{tag} = "div";
3202
$flag{class} = "small inline super";
3203
if ($attributes->{debug} and $attributes->{debug_borders}) { $flag{class} .= " bordered"; }
3204
if (Bc_sql::is_flagged($stats{ID}, $stats{ID}, "n")) {
3205
$flag{class} .= " error";
3206
$flag{style} = "margin-left: 5px;";
3207
$flag{innerHTML} = "Flagged";
3208
} else {
3209
my %img; {
3210
$img{tag} = "img";
3211
$img{class} = "enlarge clickable";
3212
$img{onclick} = "document.location='/flag.pl?id=$stats{ID}&$Bc_sql::QUERY_UID=$stats{ID}&" . Bc_sql::get_constant("FLAG_TYPE") . "=n';";
3213
if ($attributes->{debug} and $attributes->{debug_borders}) {
3214
$img{onclick} =~ s/';/&dbpl=1';/;
3215
$img{class} .= " bordered";
3216
}
3217
3218
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $img{title} = $img{onclick}; }
3219
$img{src} = "/images/site/flagged.png";
3220
$img{height} = 12;
3221
}
3222
$flag{innerHTML} = " " . tag(\%img);
3223
}
3224
} # end flag div
3225
3226
$nickname{innerHTML} .= tag(\%flag);
3227
} # end if $Bc_sql::LOGGEDIN
3228
3229
$legend{innerHTML} .= tag(\%nickname);
3230
} # end nickname div
3231
3232
my %iconsdiv; {
3233
$iconsdiv{tag} = "div";
3234
$iconsdiv{style} = "grid-area: icons; align-content: center;";
3235
if ($Bc_sql::LOGGEDIN) {
3236
$iconsdiv{class} = "blue-panel nowrap padded-small";
3237
if ($theme{premium} eq 2) { $iconsdiv{class} .= " translucent_$stats{ID}"; }
3238
if ($attributes->{debug} and $attributes->{debug_borders}) { $iconsdiv{class} .= " bordered"; }
3239
$iconsdiv{innerHTML} = User::get_user_stats_asIcons($stats{ID});
3240
}
3241
3242
$legend{innerHTML} .= tag(\%iconsdiv);
3243
} # end icons div
3244
3245
if ($Bc_sql::LOGGEDIN) {
3246
my %agediv; {
3247
my $age = User::get_user_age($stats{ID});
3248
$agediv{tag} = "div";
3249
$agediv{style} = "grid-area: age; align-content: center;";
3250
if ($stats{showbday} eq 2) {
3251
$agediv{class} = "text-align-center embolden nowrap copyright padded-small ";
3252
$agediv{class} .= "rounded padded-left-right ";
3253
$agediv{class} .= "green-panel";
3254
$agediv{style} .= "margin: auto auto;";
3255
$agediv{title} = "Only you will see this!";
3256
3257
$agediv{innerHTML} = $age;
3258
if (not $attributes->{mini}) {
3259
$agediv{innerHTML} .= " years old, " . Date::expand_date($stats{dob}, 1);
3260
} else {
3261
$agediv{innerHTML} .= " yrs";
3262
}
3263
}
3264
3265
$legend{innerHTML} .= tag(\%agediv);
3266
} # end age div
3267
3268
my %seeking; {
3269
$seeking{tag} = "div";
3270
$seeking{class} = "bordered center sunken_$stats{ID} sunken small padded-left-right";
3271
$seeking{style} = "grid-area: seeking; margin-top: 2px;";
3272
$seeking{innerHTML} = "Seeking " . embolden({innerHTML=>Bc_misc::a_or_an(Bc_sql::get_config_asWord("genders", $stats{seeking_gender})), class=>"b_$stats{ID}"}) .
3273
" for " .
3274
embolden({innerHTML=>Bc_sql::get_config_asWord("styles", $stats{seeking}), class=>"b_$stats{ID}"});
3275
3276
$legend{innerHTML} .= tag(\%seeking);
3277
} # end seeking div
3278
3279
if (User::isUserSubscriber($Bc_sql::LOGGEDIN)) {
3280
my %commsdiv; {
3281
$commsdiv{tag} = "div";
3282
$commsdiv{style} = "grid-area: comms; " .
3283
"display: flex; " .
3284
"justify-content: space-around; " .
3285
"align-content: center; " .
3286
"align-items: center; ";
3287
$commsdiv{class} = "sunken sunken_$stats{ID} margins-top-bottom translucent_$stats{ID}";
3288
$commsdiv{innerHTML} = "";
3289
3290
if (User::blockedUser($stats{ID})) {
3291
my %unblocklink; {
3292
$unblocklink{tag} = "img";
3293
$unblocklink{class} = "enlarge clickable";
3294
if ($attributes->{debug} and $attributes->{debug_borders}) { $unblocklink{class} .= " bordered"; }
3295
3296
$unblocklink{onclick} = "document.location='/block.pl?r=1&" . $Bc_sql::QUERY_UID . "=$stats{ID}';";
3297
$unblocklink{src} = "/img.pl?i=site/block-remove.png&s=i";
3298
$unblocklink{title} .= "Remove from Cock Blocks";
3299
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $unblocklink{title} .= " ($unblocklink{onclick})"; }
3300
3301
$commsdiv{innerHTML} .= tag(\%unblocklink);
3302
}
3303
} # end if User::blockedUser($stats{ID})
3304
else {
3305
if ($attributes->{mini}) {
3306
my %profilelink; {
3307
$profilelink{tag} = "img";
3308
$profilelink{class} = "enlarge clickable";
3309
if ($attributes->{debug} and $attributes->{debug_borders}) { $profilelink{class} .= " bordered"; }
3310
$profilelink{src} = "/img.pl?i=site/profile.png&s=i";
3311
$profilelink{onclick} = "document.location='/?" . $Bc_sql::QUERY_PAGE . "=" .
3312
Bc_sql::get_constant("PROFILE_PAGE") . "&" .
3313
Bc_sql::get_constant("QUERY_UID") . "=" .
3314
$stats{ID} .
3315
"';";
3316
$profilelink{title} = "View Full Profile";
3317
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $profilelink{title} .= " ($profilelink{onclick})"; }
3318
3319
$commsdiv{innerHTML} .= tag(\%profilelink);
3320
} # end profile link
3321
}
3322
3323
my %msglink; {
3324
$msglink{tag} = "img";
3325
$msglink{class} = "enlarge clickable";
3326
if ($attributes->{debug} and $attributes->{debug_borders}) { $msglink{class} .= " bordered"; }
3327
$msglink{src} = "/img.pl?i=site/compose.png&s=i";
3328
$msglink{onclick} = "document.location='/?" . $Bc_sql::QUERY_PAGE . "=" .
3329
Bc_sql::get_constant("MAIL_PAGE") . "&" .
3330
Bc_sql::get_constant("QUERY_MAIL_PAGE") . "=" .
3331
Bc_sql::get_constant("MAIL_COMPOSE_PAGE") . "&" .
3332
Bc_sql::get_constant("QUERY_MAIL_TO") . "=" .
3333
$stats{ID} .
3334
"';";
3335
$msglink{title} = "Send Message";
3336
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $msglink{title} .= " ($msglink{onclick})"; }
3337
3338
$commsdiv{innerHTML} .= tag(\%msglink);
3339
} # end msg link
3340
3341
my %fmalink; {
3342
$fmalink{tag} = "img";
3343
$fmalink{class} = "enlarge clickable";
3344
if ($attributes->{debug} and $attributes->{debug_borders}) { $fmalink{class} .= " bordered"; }
3345
if (not Bc_sql::fma_exists($Bc_sql::LOGGEDIN, $stats{ID})) {
3346
$fmalink{src} = "/img.pl?i=site/hardon.png&s=i";
3347
$fmalink{onclick} = "document.location='/fma.pl?r=1&dbpl=$attributes->{debug}&" . $Bc_sql::QUERY_UID . "=" . $stats{ID} . "';";
3348
$fmalink{title} = "Send Fuck Me Now Alert";
3349
} else {
3350
$fmalink{src} = "/img.pl?i=site/hardon.png&s=i&wmi=site/delete.png";
3351
$fmalink{onclick} = "document.location='/fma.pl?r=1&debug=$attributes->{debug}&i=3&" . $Bc_sql::QUERY_UID . "=" . $stats{ID} . "';";
3352
$fmalink{title} = "Retract Fuck Me Now Alert";
3353
}
3354
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $fmalink{title} .= " ($fmalink{onclick})"; }
3355
$commsdiv{innerHTML} .= tag(\%fmalink);
3356
} # end fma link
3357
3358
my %separator; {
3359
$separator{tag} = "font";
3360
$separator{innerHTML} = "|";
3361
if ($attributes->{debug} and $attributes->{debug_borders}) { $separator{class} = " bordered"; }
3362
3363
$commsdiv{innerHTML} .= tag(\%separator);
3364
} # end separator
3365
3366
my %buddylink; {
3367
$buddylink{tag} = "img";
3368
$buddylink{class} = "enlarge clickable";
3369
if ($attributes->{debug} and $attributes->{debug_borders}) { $buddylink{class} .= " bordered"; }
3370
3371
$buddylink{onclick} = "document.location='/addfriend.pl?" . $Bc_sql::QUERY_UID . "=$stats{ID}';";
3372
if (User::isFriend($stats{ID})) {
3373
$buddylink{src} = "/img.pl?i=site/user_delete.png&s=i";
3374
$buddylink{title} .= "Remove from Fuck Buddies";
3375
} else {
3376
$buddylink{src} = "/img.pl?i=site/user_add.png&s=i";
3377
$buddylink{title} .= "Add to Fuck Buddies";
3378
}
3379
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $buddylink{title} .= " ($buddylink{onclick})"; }
3380
3381
$commsdiv{innerHTML} .= tag(\%buddylink);
3382
} # end buddy link
3383
3384
my %blocklink; {
3385
$blocklink{tag} = "img";
3386
$blocklink{class} = "enlarge clickable";
3387
if ($attributes->{debug} and $attributes->{debug_borders}) { $blocklink{class} .= " bordered"; }
3388
3389
$blocklink{onclick} = "document.location='/block.pl?r=1&" . $Bc_sql::QUERY_UID . "=$stats{ID}';";
3390
$blocklink{src} = "/img.pl?i=site/block1.png&s=i";
3391
$blocklink{title} .= "Add to Cock Blocks";
3392
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $blocklink{title} .= " ($blocklink{onclick})"; }
3393
3394
$commsdiv{innerHTML} .= tag(\%blocklink);
3395
} # end block link
3396
} # end else of if User::blockedUser($stats{ID})
3397
3398
$legend{innerHTML} .= tag(\%commsdiv);
3399
} # end comms div
3400
} # end if $Bc_sql::LOGGEDIN is subscriber
3401
else {
3402
my %subscribe; {
3403
$subscribe{tag} = "div";
3404
$subscribe{style} = "grid-area: comms;";
3405
my %link; {
3406
$link{tag} = "a";
3407
$link{href} = "/?$Bc_sql::QUERY_PAGE=$Bc_sql::CONSTANTS{PAY_PAGE}";
3408
$link{innerHTML} = "Subscribers";
3409
}
3410
$subscribe{innerHTML} = tag(\%link) . " " . embolden("Get More!");
3411
3412
$legend{innerHTML} .= tag(\%subscribe);
3413
} # end subscribe div
3414
} # end else of if user is subscriber
3415
} # end if $Bc_sql::LOGGEDIN
3416
else {
3417
my %getmore; {
3418
$getmore{tag} = "div";
3419
$getmore{class} = "center nowrap margins small yellow-panel";
3420
if ($attributes->{debug} and $attributes->{debug_borders}) { $getmore{class} .= " bordered"; }
3421
$getmore{style} = "grid-area: seeking; " .
3422
"margin: auto auto; ";
3423
3424
my %link; {
3425
$link{tag} = "a";
3426
$link{href} = "";
3427
$link{innerHTML} = italicize("Get More");
3428
} # end link
3429
3430
$getmore{innerHTML} = italicize("Members ") . tag(\%link) . "!";
3431
3432
$legend{innerHTML} .= tag(\%getmore);
3433
} # end getmore div
3434
} # end else of if $Bc_sql::LOGGEDIN
3435
3436
$fs{innerHTML} .= tag(\%legend);
3437
} # end if legend
3438
3439
if ($Bc_sql::LOGGEDIN and not $attributes->{mini}) {
3440
my %statsdiv; {
3441
$statsdiv{tag} = "div";
3442
if ($attributes->{debug} and $attributes->{debug_borders}) { $statsdiv{class} = "bordered"; }
3443
$statsdiv{style} = "display: grid; " .
3444
"grid-template-areas: 'body body' 'eyes hair' 'height weight' 'drive host' 'smoker drinker' 'drugs drugs'; " .
3445
"grid-template-columns: min-content min-content; " .
3446
"grid-template-rows: repeat(6, min-content); " .
3447
"column-gap: 15px; " .
3448
"row-gap: 8px; " .
3449
"justify-content: center; ";
3450
3451
my %body; {
3452
$body{tag} = "div";
3453
$body{style} = "grid-area: body;";
3454
$body{class} = "nowrap center";
3455
if ($attributes->{debug} and $attributes->{debug_borders}) { $body{class} .= " bordered"; }
3456
$body{innerHTML} = "Body Type: " . embolden({innerHTML=>Bc_sql::get_config_asWord("bodies", $stats{body}), class=>"b_$stats{ID}"});
3457
3458
$statsdiv{innerHTML} .= tag(\%body);
3459
} # end body
3460
3461
my %wheels; {
3462
$wheels{tag} = "div";
3463
$wheels{class} = "nowrap";
3464
if ($attributes->{debug} and $attributes->{debug_borders}) { $wheels{class} .= " bordered"; }
3465
$wheels{style} = "grid-area: drive;";
3466
$wheels{innerHTML} = "Can Drive: " . embolden({innerHTML=>Bc_sql::get_config_asWord("yesno", $stats{wheels}), class=>"b_$stats{ID}"});
3467
3468
$statsdiv{innerHTML} .= tag(\%wheels);
3469
} # end wheels
3470
3471
my %host; {
3472
$host{tag} = "div";
3473
$host{class} = "nowrap text-align-right";
3474
if ($attributes->{debug} and $attributes->{debug_borders}) { $host{class} .= " bordered"; }
3475
$host{style} = "grid-area: host;";
3476
$host{innerHTML} = "Can Host: " . embolden({innerHTML=>Bc_sql::get_config_asWord("yesno", $stats{can_host}), class=>"b_$stats{ID}"});
3477
3478
$statsdiv{innerHTML} .= tag(\%host);
3479
} # end host
3480
3481
my %smoker; {
3482
$smoker{tag} = "div";
3483
$smoker{class} = "nowrap";
3484
if ($attributes->{debug} and $attributes->{debug_borders}) { $smoker{class} .= " bordered"; }
3485
$smoker{style} = "grid-area: smoker;";
3486
$smoker{innerHTML} = "Smoking OK: " . embolden({innerHTML=>Bc_sql::get_config_asWord("yesno", $stats{smoker}), class=>"b_$stats{ID}"});
3487
3488
$statsdiv{innerHTML} .= tag(\%smoker);
3489
} # end smoker
3490
3491
my %drugs; {
3492
$drugs{tag} = "div";
3493
$drugs{class} = "nowrap center";
3494
if ($attributes->{debug} and $attributes->{debug_borders}) { $drugs{class} .= " bordered"; }
3495
$drugs{style} = "grid-area: drugs;";
3496
$drugs{innerHTML} = "Drugs OK: " . embolden({innerHTML=>Bc_sql::get_config_asWord("yesno", $stats{drugs}), class=>"b_$stats{ID}"});
3497
3498
$statsdiv{innerHTML} .= tag(\%drugs);
3499
} # end drugs
3500
3501
my %booze; {
3502
$booze{tag} = "div";
3503
$booze{class} = "nowrap text-align-right";
3504
if ($attributes->{debug} and $attributes->{debug_borders}) { $booze{class} .= " bordered"; }
3505
$booze{style} = "grid-area: drinker;";
3506
$booze{innerHTML} = "Drinking OK: " . embolden({innerHTML=>Bc_sql::get_config_asWord("yesno", $stats{drinker}), class=>"b_$stats{ID}"});
3507
3508
$statsdiv{innerHTML} .= tag(\%booze);
3509
} # end booze
3510
3511
my %height; {
3512
$height{tag} = "div";
3513
$height{class} = "nowrap";
3514
if ($attributes->{debug} and $attributes->{debug_borders}) { $height{class} .= " bordered"; }
3515
$height{style} = "grid-area: height;";
3516
$height{innerHTML} = "Height: " . embolden({innerHTML=>Bc_sql::get_config_asWord("heights", $stats{height}), class=>"b_$stats{ID}"});
3517
3518
$statsdiv{innerHTML} .= tag(\%height);
3519
} # end height
3520
3521
my %weight; {
3522
$weight{tag} = "div";
3523
$weight{class} = "nowrap text-align-right";
3524
if ($attributes->{debug} and $attributes->{debug_borders}) { $weight{class} .= " bordered"; }
3525
$weight{style} = "grid-area: weight;";
3526
$weight{innerHTML} = "Weight: " . embolden({innerHTML=>Bc_sql::get_config_asWord("weights", $stats{weight}), class=>"b_$stats{ID}"});
3527
3528
$statsdiv{innerHTML} .= tag(\%weight);
3529
} # end weight
3530
3531
my %hair; {
3532
$hair{tag} = "div";
3533
$hair{class} = "nowrap text-align-right";
3534
if ($attributes->{debug} and $attributes->{debug_borders}) { $hair{class} .= " bordered"; }
3535
$hair{style} = "grid-area: hair;";
3536
$hair{innerHTML} = embolden({innerHTML=>Bc_sql::get_config_asWord("hair", $stats{hair_clr}), class=>"b_$stats{ID}"}) . " hair";
3537
3538
$statsdiv{innerHTML} .= tag(\%hair);
3539
} # end hair
3540
3541
my %eyes; {
3542
$eyes{tag} = "div";
3543
$eyes{class} = "nowrap";
3544
if ($attributes->{debug} and $attributes->{debug_borders}) { $eyes{class} .= " bordered"; }
3545
$eyes{style} = "grid-area: eyes;";
3546
$eyes{innerHTML} = embolden({innerHTML=>Bc_sql::get_config_asWord("eyes", $stats{eye_clr}), class=>"b_$stats{ID}"}) . " eyes";
3547
3548
$statsdiv{innerHTML} .= tag(\%eyes);
3549
} # end eyes
3550
3551
$fs{innerHTML} .= tag(\%statsdiv);
3552
} # end statsdiv
3553
3554
my %descdiv; {
3555
$descdiv{tag} = "div";
3556
$descdiv{style} = "margin-top: 5px; margin-bottom: 5px;";
3557
if ($attributes->{debug} and $attributes->{debug_borders}) { $descdiv{class} = "bordered"; }
3558
$descdiv{innerHTML} = "";
3559
3560
my %title; {
3561
$title{tag} = "div";
3562
$title{class} = "center subtitle_$stats{ID} italic";
3563
if ($attributes->{debug} and $attributes->{debug_borders}) { $title{class} .= " bordered"; }
3564
$title{innerHTML} = "<table border=";
3565
if ($attributes->{debug} and $attributes->{debug_borders}) { $title{innerHTML} .= "1"; } else { $title{innerHTML} .= "0"; }
3566
$title{innerHTML} .= " cellpadding=0 cellspacing=0 width=100%><tr><td><hr class=\"hr_$stats{ID}\"></td><td class=spacerx></td><td width=1>Introduction</td><td class=spacerx></td><td><hr class=\"hr_$stats{ID}\"></td></tr></table>";
3567
3568
$descdiv{innerHTML} .= tag(\%title);
3569
} # end title
3570
3571
my %desc; {
3572
$desc{tag} = "div";
3573
$desc{style} = "max-height: 100px; min-height: 100px; ";
3574
$desc{class} = "sunken_$stats{ID} sunken scrolling_vertical_$stats{ID} text-align-left";
3575
if ($theme{premium} eq 2) { $desc{class} .= " translucent_$stats{ID}"; }
3576
$desc{innerHTML} = $stats{description};
3577
3578
$descdiv{innerHTML} .= tag(\%desc);
3579
} # end desc
3580
3581
my %flag; {
3582
$flag{tag} = "div";
3583
$flag{class} = "text-align-right";
3584
if ($attributes->{debug} and $attributes->{debug_borders}) { $flag{class} .= " bordered"; }
3585
if (Bc_sql::is_flagged($stats{ID}, $stats{ID}, "d")) {
3586
my %flagged; {
3587
$flagged{tag} = "div";
3588
$flagged{class} = "error small italic";
3589
$flagged{innerHTML} = "Flagged";
3590
} # end flagged
3591
$flag{innerHTML} = tag(\%flagged);
3592
} else {
3593
my %img; {
3594
$img{tag} = "img";
3595
$img{onclick} = "document.location='/flag.pl?id=$stats{ID}&$Bc_sql::QUERY_UID=$stats{ID}&" . Bc_sql::get_constant("FLAG_TYPE") . "=d';";
3596
$img{class} = "enlarge clickable";
3597
if ($attributes->{debug} and $attributes->{debug_borders}) {
3598
$img{onclick} =~ s/';/&dbpl=1';/;
3599
$img{class} .= " bordered";
3600
}
3601
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $img{title} = $img{onclick}; }
3602
$img{src} = "/images/site/flagged.png";
3603
$img{height} = 12;
3604
$flag{innerHTML} = " " . tag(\%img);
3605
} # end img
3606
} # end else of if is description flagged
3607
3608
$descdiv{innerHTML} .= tag(\%flag);
3609
} # end flag div
3610
3611
$fs{innerHTML} .= tag(\%descdiv);
3612
} # end description div
3613
} # end if $Bc_sql::LOGGEDIN
3614
3615
$profilecontainer{innerHTML} .= tag(\%fs);
3616
} # end stats fieldset
3617
3618
####################
3619
## PROFILE PHOTOS ##
3620
####################
3621
if (not $attributes->{mini}) {
3622
my %photos; {
3623
$photos{tag} = "div";
3624
$photos{class} = "photosfsgrid fieldset_$stats{ID} rounded padded-small";
3625
$photos{innerHTML} = "";
3626
3627
my %pcss; {
3628
$pcss{tag} = "style";
3629
$pcss{innerHTML} = ".photosfsgrid {\n" .
3630
" display: grid;\n" .
3631
" grid-template-areas: 'legend' 'count' 'pics';\n" .
3632
" grid-template-columns: min-content;\n" .
3633
" grid-template-rows: min-content min-content 1fr;\n" .
3634
" justify-content: center;\n" .
3635
"}\n" .
3636
"\n";
3637
$photos{innerHTML} .= tag(\%pcss);
3638
}
3639
3640
if ($theme{premium} eq 2) { $photos{class} .= " translucent_$stats{ID}"; }
3641
3642
my %legend; {
3643
$legend{tag} = "div";
3644
$legend{class} = "centered";
3645
if ($attributes->{debug} and $attributes->{debug_borders}) { $legend{class} .= " bordered"; }
3646
$legend{style} = "grid-area: legend;";
3647
3648
my %omg; {
3649
$omg{tag} = "div";
3650
$omg{class} = "legend_$stats{ID} rounded padded-small subtitle_$stats{ID} min-content";
3651
if ($attributes->{debug} and $attributes->{debug_borders}) { $omg{class} .= " bordered"; }
3652
if ($theme{premium} eq 2) { $omg{class} .= " translucent_$stats{ID}"; }
3653
$omg{innerHTML} = "Photos";
3654
} # end omg
3655
3656
$legend{innerHTML} = tag(\%omg);
3657
} # end legend
3658
3659
$photos{innerHTML} .= tag(\%legend);
3660
3661
my @pics = User::get_user_pics($attributes->{uid});
3662
if (@pics) {
3663
my %pcountdiv; {
3664
$pcountdiv{tag} = "div";
3665
$pcountdiv{style} = "grid-area: count;";
3666
if ($attributes->{debug} and $attributes->{debug_borders}) { $pcountdiv{class} = "bordered"; }
3667
my %omg; {
3668
$omg{tag} = "div";
3669
$omg{class} = "center";
3670
if ($attributes->{debug} and $attributes->{debug_borders}) { $omg{class} .= " bordered"; }
3671
$omg{innerHTML} = hr({class=>"hr_$stats{ID}"}) . "Found " . embolden({innerHTML=>"" . (@pics), class=>"b_$stats{ID}"}) . " " . pluralize("photo", @pics) . hr({class=>"hr_$stats{ID}"});
3672
}
3673
$pcountdiv{innerHTML} = tag(\%omg);
3674
3675
$photos{innerHTML} .= tag(\%pcountdiv);
3676
} # end pcountdiv
3677
3678
my %photosdiv; {
3679
$photosdiv{tag} = "div";
3680
$photosdiv{innerHTML} = "";
3681
3682
{ my %pcss;
3683
my $h = 400;
3684
if ($moderator) { $h += 10; }
3685
3686
$pcss{tag} = "style";
3687
$pcss{innerHTML} = ".photosdiv {\n" .
3688
" grid-area: pics;\n" .
3689
" max-height: " . $h . "px;\n" .
3690
" min-height: " . $h . "px;\n" .
3691
" width: inherit;\n" .
3692
" gap: 8px;\n" .
3693
" justify-content: center;\n" .
3694
"}\n" .
3695
"\n" .
3696
"\@media only screen and (max-width: 1200px) {\n" .
3697
" .photosdiv {\n" .
3698
" max-height: 150px;\n" .
3699
" min-height: min-content;\n" .
3700
" }\n" .
3701
"}\n" .
3702
"\n";
3703
$photosdiv{innerHTML} .= tag(\%pcss);
3704
}
3705
3706
$photosdiv{class} = "photosdiv flexboxed padded sunken_$stats{ID} scrolling_vertical_$stats{ID} rounded";
3707
if ($theme{premium} eq 2) { $photosdiv{class} .= " translucent_$stats{ID}"; }
3708
3709
foreach my $photo (@pics) {
3710
my $flagged = Bc_sql::is_flagged($stats{ID}, $photo->{ID}, "i");
3711
3712
{ my %pic;
3713
$pic{tag} = "fieldset";
3714
$pic{class} = "fieldset_$stats{ID} text-align-center padded-small";
3715
$pic{style} = "max-height: min-content; height: min-content;";
3716
if ($theme{premium} eq 2) { $pic{class} .= " translucent_$stats{ID}"; }
3717
$pic{innerHTML} = "";
3718
3719
my %legend; {
3720
$legend{tag} = "legend";
3721
$legend{class} = "legend_$stats{ID} text-align-center nowrap small";
3722
if ($theme{premium} eq 2) { $legend{class} .= " translucent_$stats{ID}"; }
3723
if ($photo->{name}) {
3724
my $n = Bc_misc::shorten_str($photo->{name}, 10);
3725
if ($n ne $photo->{name}) { $legend{title} = $photo->{name}; }
3726
$legend{innerHTML} = $n;
3727
} else {
3728
$legend{innerHTML} = "(unnamed)";
3729
}
3730
3731
$pic{innerHTML} .= tag(\%legend);
3732
} # end legend
3733
3734
my %img; {
3735
$img{tag} = "img";
3736
$img{src} = "/getimage.pl?id=" . $photo->{ID};
3737
if ($attributes->{debug_borders}) { $img{class} .= " bordered"; }
3738
} # end img
3739
3740
if (ref $photo eq "HASH")
3741
{ $pic{innerHTML} .= Html::display_modal("pid" . $photo->{ID}, "img,$photo->{name},/getimage.pl?id=$photo->{ID}&th=1", "", $photo->{name}, tag(\%img), $photo->{desc}, "", "normal-panel_$stats{ID} bordered_$stats{ID} rounded"); } else
3742
{ $pic{innerHTML} .= "no image! how tf?"; }
3743
3744
{ my %pictoolsfs;
3745
if (not $flagged) { $pictoolsfs{tag} = "fieldset"; }
3746
$pictoolsfs{class} = "fieldset_$stats{ID} block";
3747
if ($theme{premium} eq 2) { $pictoolsfs{class} .= " translucent_$stats{ID}"; }
3748
$pictoolsfs{style} = "margin-top: 8px; margin-bottom: 3px;";
3749
3750
{ my %div;
3751
$div{tag} = "div";
3752
$div{class} = "center padded-small";
3753
if ($attributes->{debug} and $attributes->{debug_borders}) { $div{class} .= "bordered"; }
3754
$div{style} = "display: flex;";
3755
$div{innerHTML} = "";
3756
3757
{ my %like;
3758
$like{tag} = "img";
3759
$like{src} = "/img.pl?i=yellow/thumb_up.png";
3760
if ($stats{ID} ne $Bc_sql::LOGGEDIN) {
3761
$like{title} = "Click to ";
3762
$like{onclick} = "document.location=\"/like.pl?id=$photo->{ID}\";";
3763
$like{class} = "enlarge clickable";
3764
if (not liked($photo->{ID})) { $like{src} .= "&gs=1"; $like{title} .= "Like"; } else { $like{title} .= "Unlike"; }
3765
} else {
3766
#$like{src} .= "&gs=1";
3767
$like{title} = "Number of likes";
3768
}
3769
$like{height} = "24";
3770
3771
$div{innerHTML} .= tag(\%like);
3772
}
3773
3774
{ my %likecount;
3775
my $c = liked({item_id=>$photo->{ID}, count=>1});
3776
$likecount{tag} = "div";
3777
$likecount{class} = "centered";
3778
$likecount{innerHTML} = embolden({innerHTML=>" $c", class=>"b_$stats{ID}"});
3779
3780
$div{innerHTML} .= tag(\%likecount);
3781
}
3782
3783
{ my %dislike;
3784
$dislike{tag} = "img";
3785
$dislike{src} = "/img.pl?i=yellow/thumb_down.png";
3786
if ($stats{ID} ne $Bc_sql::LOGGEDIN) {
3787
$dislike{title} = "Click to ";
3788
$dislike{onclick} = "document.location=\"/like.pl?id=$photo->{ID}&dislike=1\";";
3789
$dislike{class} = "enlarge clickable";
3790
if (not disliked($photo->{ID})) { $dislike{src} .= "&gs=1"; $dislike{title} .= "Dislike"; } else { $dislike{title} .= "not Dislike"; }
3791
} else {
3792
#$dislike{src} .= "&gs=1";
3793
$dislike{title} = "Number of dislikes";
3794
}
3795
$dislike{height} = "24";
3796
$dislike{style} = "margin-left: 5px;";
3797
3798
$div{innerHTML} .= tag(\%dislike);
3799
}
3800
3801
{ my %dislikecount;
3802
my $c = disliked({item_id=>$photo->{ID}, count=>1});
3803
$dislikecount{tag} = "div";
3804
$dislikecount{class} = "centered";
3805
$dislikecount{innerHTML} = embolden({innerHTML=>" $c", class=>"b_$stats{ID}"});
3806
3807
$div{innerHTML} .= tag(\%dislikecount);
3808
}
3809
3810
if ($stats{ID} ne $Bc_sql::LOGGEDIN) { $div{innerHTML} .= embolden({innerHTML=>" | ", class=>"b_$stats{ID}"}); }
3811
3812
{ my %flag;
3813
if ($flagged) {
3814
$flag{tag} = "div";
3815
$flag{class} = "red-panel inline center";
3816
$flag{innerHTML} = "";
3817
3818
{ my %img;
3819
$img{tag} = "src";
3820
$img{src} = "/img.pl?i=site/flagged.png";
3821
$img{title} = "Flagged";
3822
3823
$flag{innerHTML} .= tag(\%img);
3824
}
3825
}
3826
else {
3827
if ($stats{ID} ne $Bc_sql::LOGGEDIN) {
3828
$flag{tag} = "img";
3829
$flag{class} = "enlarge clickable";
3830
$flag{src} = "/img.pl?i=site/flagged.png";
3831
$flag{onclick} = "console.log('dbl nice');";
3832
$flag{title} = "Flag Image";
3833
$flag{height} = "24";
3834
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $flag{title} .= " ($flag{onclick})"; }
3835
} # end if $stats{ID} ne $Bc_sql::LOGGEDIN
3836
}
3837
3838
# sometimes, tag will NOT be set, this is ok.
3839
if ($flag{tag}) { $div{innerHTML} .= tag(\%flag); }
3840
} # end flag
3841
3842
$pictoolsfs{innerHTML} = tag(\%div);
3843
} # end div
3844
3845
$pic{innerHTML} .= tag(\%pictoolsfs);
3846
} # end of pic tools fieldset
3847
3848
$photosdiv{innerHTML} .= tag(\%pic);
3849
} # end pic fieldset
3850
} # end foreach $photo
3851
} # end photos div
3852
3853
$photos{innerHTML} .= tag(\%photosdiv);
3854
} # end if @pics
3855
else {
3856
$photos{innerHTML} .= "no photos!";
3857
} # end else of if @pics
3858
3859
$profilecontainer{innerHTML} .= tag(\%photos);
3860
} # end photos fieldset
3861
} # end if not $attributes->{mini}
3862
3863
$rv .= tag(\%profilecontainer);
3864
} # end profilecontainer
3865
3866
if (not $attributes->{mini}) {
3867
######################
3868
## MODERATORS PANEL ##
3869
######################
3870
if ($moderator or User::isUserModerator()) {
3871
my %moddiv; {
3872
$moddiv{tag} = "div";
3873
$moddiv{class} = "padded blue-panel modgrid min-content";
3874
$moddiv{innerHTML} = "";
3875
3876
my %css; {
3877
$css{tag} = "style";
3878
$css{innerHTML} = ".modgrid {\n" .
3879
" display: grid;\n" .
3880
" grid-template-areas: 'usersdd title' 'usersdd seclev' 'buttons flagged';\n" .
3881
" grid-template-columns: min-content min-content;\n" .
3882
" grid-template-rows: min-content min-content min-content;\n" .
3883
" margin-top: 15px;\n" .
3884
" column-gap: 15px;\n" .
3885
" row-gap: 5px;\n" .
3886
"}\n" .
3887
"\n" .
3888
"\@media screen and (max-width: 1200px) {\n" .
3889
" .modgrid {\n" .
3890
" grid-template-areas: 'title' 'seclev' 'usersdd' 'buttons' 'flagged';\n" .
3891
" grid-template-columns: min-content;\n" .
3892
" grid-template-rows: min-content min-content min-content min-content min-content;\n" .
3893
" text-align: center;\n" .
3894
" justify-content: center;\n" .
3895
" align-content: center;\n" .
3896
" align-items: center;\n" .
3897
" }\n" .
3898
"}\n" .
3899
"\n";
3900
3901
$moddiv{innerHTML} .= tag(\%css);
3902
} # end css
3903
3904
my %title; {
3905
$title{tag} = "div";
3906
$title{class} = "yellow-panel nowrap center";
3907
$title{style} = "grid-area: title; margin-bottom: 5px;";
3908
$title{innerHTML} = "Moderate " . italicize(Bc_misc::word_as_possessive($stats{nickname})) . " Account";
3909
3910
$moddiv{innerHTML} .= tag(\%title);
3911
} # end title
3912
3913
my %seclev; {
3914
$seclev{tag} = "div";
3915
$seclev{class} = "center nowrap";
3916
if ($attributes->{debug} and $attributes->{debug_borders}) { $seclev{class} .= " bordered"; }
3917
$seclev{style} = "grid-area: seclev; margin-bottom: 5px; ";
3918
$seclev{innerHTML} = "Account Security Level: " . embolden(Bc_sql::get_config_asWord("sec_levels", $stats{security}, "friendly_name"));
3919
3920
$moddiv{innerHTML} .= tag(\%seclev);
3921
} # end seclev
3922
3923
my %buttonsdiv; {
3924
$buttonsdiv{tag} = "div";
3925
$buttonsdiv{class} = "padded subnavbar min-content";
3926
$buttonsdiv{style} = "grid-area: buttons; " .
3927
"display: flex; " .
3928
"flex-direction: column; " .
3929
"justify-content: center; " .
3930
"align-items: center; " .
3931
"align-content: center; ";
3932
$buttonsdiv{innerHTML} = "";
3933
3934
my %btitle; {
3935
$btitle{tag} = "div";
3936
$btitle{class} = "yellow-panel nowrap";
3937
$btitle{style} = "grid-area: title; margin-bottom: 15px;";
3938
$btitle{innerHTML} = "Button Panel";
3939
3940
$buttonsdiv{innerHTML} .= tag(\%btitle) ;
3941
} # end buttons title div
3942
3943
if ($stats{ID} ne $Bc_sql::LOGGEDIN) {
3944
my %banbutton; {
3945
$banbutton{tag} = "button";
3946
$banbutton{type} = "button";
3947
$banbutton{class} = "red nowrap";
3948
$banbutton{onclick} = "document.location='/ban.pl?$Bc_sql::QUERY_UID=$stats{ID}";
3949
if (Security::banned($stats{ID})) {
3950
$banbutton{onclick} .= "&u=1";
3951
$banbutton{title} = "UnBan this Account!";
3952
$banbutton{innerHTML} = "UnBan Account";
3953
} else {
3954
$banbutton{title} = "Ban this Account!";
3955
$banbutton{innerHTML} = "Ban Account";
3956
}
3957
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $banbutton{title} .= " ($banbutton{onclick})"; }
3958
3959
$buttonsdiv{innerHTML} .= tag(\%banbutton);
3960
} # end ban button
3961
} # end if $stats{ID} ne $Bc_sql::LOGGEDIN
3962
3963
my %msgbutton; {
3964
$msgbutton{tag} = "button";
3965
$msgbutton{type} = "button";
3966
$msgbutton{class} = "yellow nowrap";
3967
$msgbutton{innerHTML} = "Send Message";
3968
$msgbutton{onclick} = "document.location='/?" .
3969
$Bc_sql::QUERY_PAGE . "=" .
3970
Bc_sql::get_constant("MAIL_PAGE") . "&" .
3971
Bc_sql::get_constant("QUERY_MAIL_PAGE") . "=" .
3972
Bc_sql::get_constant("MAIL_COMPOSE_PAGE") . "&" .
3973
Bc_sql::get_constant("QUERY_MAIL_TO") . "=" .
3974
$stats{ID} .
3975
"'";
3976
$msgbutton{title} = "Send a Precomposed Message";
3977
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $msgbutton{title} .= " ($msgbutton{onclick})"; }
3978
3979
$buttonsdiv{innerHTML} .= tag(\%msgbutton);
3980
} # end msg button
3981
3982
my %homebutton; {
3983
$homebutton{tag} = "button";
3984
$homebutton{type} = "button";
3985
$homebutton{class} = "green nowrap";
3986
$homebutton{onclick} = "document.location='/?" . $Bc_sql::QUERY_PAGE . "=" .
3987
Bc_sql::get_constant("MOD_PAGE") .
3988
"';";
3989
$homebutton{title} .= "Jump to Moderator Home";
3990
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $homebutton{title} .= " ($homebutton{onclick})"; }
3991
$homebutton{innerHTML} = "Moderator Home";
3992
3993
$buttonsdiv{innerHTML} .= tag(\%homebutton);
3994
} # end home button
3995
3996
my %flagcontrol; {
3997
$flagcontrol{tag} = "button";
3998
$flagcontrol{type} = "button";
3999
$flagcontrol{class} = "blue nowrap";
4000
$flagcontrol{onclick} = "document.location='/?" . $Bc_sql::QUERY_PAGE . "=" .
4001
Bc_sql::get_constant("MOD_PAGE") . "&" .
4002
Bc_sql::get_constant("QUERY_MOD_PAGE") . "=" .
4003
Bc_sql::get_constant("MOD_FLAGMAN_PAGE") . "&" .
4004
$Bc_sql::QUERY_UID . "=" .
4005
$stats{ID} .
4006
"';";
4007
$flagcontrol{title} = "Jump to Flagged Content Home";
4008
$flagcontrol{innerHTML} = "Flagged Content";
4009
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $flagcontrol{title} .= " ($flagcontrol{onclick})"; }
4010
4011
$buttonsdiv{innerHTML} .= tag(\%flagcontrol);
4012
} # end flag control
4013
4014
my %editorbutton; {
4015
$editorbutton{tag} = "button";
4016
$editorbutton{type} = "button";
4017
$editorbutton{class} = "purple nowrap";
4018
$editorbutton{innerHTML} = "User Editor";
4019
$editorbutton{onclick} = "document.location='/?" .
4020
$Bc_sql::QUERY_PAGE . "=" .
4021
Bc_sql::get_constant("ADMIN_PAGE") . "&" .
4022
Bc_sql::get_constant("QUERY_ADMIN_PAGE") . "=" .
4023
Bc_sql::get_constant("ADMIN_USER_EDITOR_PAGE") . "&" .
4024
Bc_sql::get_constant("QUERY_UID") . "=" .
4025
$stats{ID} .
4026
"'";
4027
$editorbutton{title} = "Open User Editor";
4028
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $editorbutton{title} .= " ($editorbutton{onclick})"; }
4029
4030
$buttonsdiv{innerHTML} .= tag(\%editorbutton);
4031
} # end editor button
4032
4033
$moddiv{innerHTML} .= tag(\%buttonsdiv);
4034
} # end buttons div
4035
4036
my %flagsdiv; {
4037
$flagsdiv{tag} = "div";
4038
$flagsdiv{style} = "display: grid; " .
4039
"grid-area: flagged; " .
4040
"grid-template-areas: 'title' 'flags'; " .
4041
"grid-template-columns: min-content; " .
4042
"grid-template-rows: min-content 1fr; ";
4043
$flagsdiv{class} = "padded subnavbar center";
4044
$flagsdiv{innerHTML} = "";
4045
4046
my %title; {
4047
$title{tag} = "div";
4048
$title{class} = "yellow-panel nowrap";
4049
$title{style} = "grid-area: title; margin-bottom: 15px;";
4050
$title{innerHTML} .= "Flagged Content";
4051
4052
$flagsdiv{innerHTML} .= tag(\%title);
4053
}
4054
4055
my $numFlags = get_user_flag_count($stats{ID}, "all");
4056
my %fullcount; {
4057
$fullcount{tag} = "div";
4058
$fullcount{class} = "nowrap centered";
4059
$fullcount{style} = "grid-area: flags;";
4060
if ($attributes->{debug} and $attributes->{debug_borders}) { $fullcount{class} .= " bordered"; }
4061
if ($numFlags) {
4062
$fullcount{innerHTML} = "Lifetime: " . embolden($numFlags) . " " .
4063
pluralize("item", $numFlags) .
4064
" items flagged";
4065
} else {
4066
$fullcount{innerHTML} = embolden("None");
4067
}
4068
4069
$flagsdiv{innerHTML} .= tag(\%fullcount);
4070
} # end fullcount div
4071
4072
if ($numFlags) {
4073
my %nflagcount; {
4074
my $numFlags = get_user_flag_count($stats{ID}, "n");
4075
4076
if ($numFlags) { $nflagcount{tag} = "div"; }
4077
$nflagcount{class} = "nowrap text-align-left";
4078
if ($attributes->{debug} and $attributes->{debug_borders}) { $nflagcount{class} .= " bordered"; }
4079
4080
$nflagcount{innerHTML} = "Nickname flagged " . embolden($numFlags) . " " .
4081
pluralize("time", $numFlags);
4082
4083
$flagsdiv{innerHTML} .= tag(\%nflagcount);
4084
} # end nflagcount div
4085
4086
my %dflagcount; {
4087
my $numFlags = get_user_flag_count($stats{ID}, "d");
4088
4089
if ($numFlags) { $dflagcount{tag} = "div"; }
4090
$dflagcount{class} = "nowrap text-align-left";
4091
if ($attributes->{debug} and $attributes->{debug_borders}) { $dflagcount{class} .= " bordered"; }
4092
4093
$dflagcount{innerHTML} = "Description flagged " . embolden($numFlags) . " " .
4094
pluralize("time", $numFlags);
4095
4096
$flagsdiv{innerHTML} .= tag(\%dflagcount);
4097
} # end dflagcount div
4098
4099
my %iflagcount; {
4100
my $numFlags = get_user_flag_count($stats{ID}, "i");
4101
if ($numFlags) { $iflagcount{tag} = "div"; }
4102
$iflagcount{class} = "nowrap text-align-left";
4103
if ($attributes->{debug} and $attributes->{debug_borders}) { $iflagcount{class} .= " bordered"; }
4104
4105
$iflagcount{innerHTML} = "Images flagged " . embolden($numFlags) . " " .
4106
pluralize("time", $numFlags);
4107
4108
$flagsdiv{innerHTML} .= tag(\%iflagcount);
4109
} # end iflagcount div
4110
4111
my %mflagcount; {
4112
my $numFlags = get_user_flag_count($stats{ID}, "m");
4113
if ($numFlags) { $mflagcount{tag} = "div"; }
4114
$mflagcount{class} = "nowrap text-align-left";
4115
if ($attributes->{debug} and $attributes->{debug_borders}) { $mflagcount{class} .= " bordered"; }
4116
4117
$mflagcount{innerHTML} = "Messages flagged " . embolden($numFlags) . " " .
4118
pluralize("time", $numFlags);
4119
4120
$flagsdiv{innerHTML} .= tag(\%mflagcount);
4121
} # end mflagcount div
4122
} # end if $numFlags
4123
4124
$moddiv{innerHTML} .= tag(\%flagsdiv);
4125
} # end flagsdiv
4126
4127
my %usernavdddiv; {
4128
$usernavdddiv{tag} = "div";
4129
$usernavdddiv{class} = "padded subnavbar min-content center";
4130
if ($attributes->{debug} and $attributes->{debug_borders}) { $usernavdddiv{class} .= " bordered"; }
4131
$usernavdddiv{style} = "grid-area: usersdd;";
4132
$usernavdddiv{innerHTML} = "";
4133
4134
my %untitle; {
4135
$untitle{tag} = "div";
4136
$untitle{class} = "yellow-panel nowrap center";
4137
$untitle{style} = "grid-area: title; margin-bottom: 5px;";
4138
$untitle{innerHTML} = "User Navigation";
4139
4140
$usernavdddiv{innerHTML} .= tag(\%untitle);
4141
} # end untitle
4142
4143
my %usernavddattr; {
4144
my %homeimg; {
4145
$homeimg{tag} = "img";
4146
$homeimg{src} = "/img.pl?i=site/first.png&s=s";
4147
}
4148
4149
my %previmg; {
4150
$previmg{tag} = "img";
4151
$previmg{src} = "/img.pl?i=site/prev.png&s=s";
4152
}
4153
4154
my %nextimg; {
4155
$nextimg{tag} = "img";
4156
$nextimg{src} = "/img.pl?i=site/next.png&s=s";
4157
}
4158
4159
my %lastimg; {
4160
$lastimg{tag} = "img";
4161
$lastimg{src} = "/img.pl?i=site/last.png&s=s";
4162
}
4163
4164
my %reloadimg; {
4165
$reloadimg{tag} = "img";
4166
$reloadimg{src} = "/img.pl?i=site/reload.png&s=s";
4167
}
4168
4169
my @users = get_users_forDropdowns();
4170
$usernavddattr{list} = \@users;
4171
$usernavddattr{mini} = 1;
4172
$usernavddattr{page} = 1;
4173
$usernavddattr{lastpage} = ceil((get_user_count()) / 1000);
4174
$usernavddattr{inline} = 1;
4175
$usernavddattr{mini} = 1;
4176
$usernavddattr{id} = Bc_misc::new_id();
4177
$usernavddattr{homeclick} = "usernavhome_$usernavddattr{id}();";
4178
$usernavddattr{homelabel} = tag(\%homeimg);
4179
$usernavddattr{prevclick} = "usernavprev_$usernavddattr{id}();";
4180
$usernavddattr{prevlabel} = tag(\%previmg);
4181
$usernavddattr{nextclick} = "usernavnext_$usernavddattr{id}();";
4182
$usernavddattr{nextlabel} = tag(\%nextimg);
4183
$usernavddattr{endclick} = "usernavend_$usernavddattr{id}();";
4184
$usernavddattr{endlabel} = tag(\%lastimg);
4185
$usernavddattr{reloadclick} = "usernavreload_$usernavddattr{id}();";
4186
$usernavddattr{reloadlabel} = tag(\%reloadimg);
4187
$usernavddattr{pagechange} = "usernavpagechange_$usernavddattr{id}();";
4188
$usernavddattr{onchange} = "usernavselectchange_$usernavddattr{id}();";
4189
$usernavddattr{title} = "Please, select a nickname to change to...";
4190
4191
$usernavddattr{debug} = $attributes->{debug_borders};
4192
4193
my %js; {
4194
# the select input gets an HTML ID of $attr->{id}
4195
# the page input gets an HTML ID of "ddnav_page_input_" . $attr->{id}
4196
# the home button gets an HTML ID of "nav_home_button_" . $attr->{id}
4197
# the prev button gets an HTML ID of "nav_prev_button_" . $attr->{id}
4198
# the next button gets an HTML ID of "nav_next_button_" . $attr->{id}
4199
# the end button gets an HTML ID of "nav_end_button_" . $attr->{id}
4200
# the reload button gets an HTML ID of "nav_reload_button_" . $attr->{id}
4201
# if $attr->{id} has no value, the return value of "Bc_misc::new_id()" is used instead,
4202
# and $attr->{id} will be set to that value.<br>
4203
my $u = Bc_misc::remove_param(Bc_sql::get_constant("QUERY_UID"), $url);
4204
$u = Bc_misc::remove_param(Bc_sql::get_constant("QUERY_DEBUG_PAGE"), $u);
4205
if ($debugpl) {
4206
$u =~ s/^\///;
4207
$u =~ s/^\?/debug.pl?/;
4208
if ($u)
4209
{ $u .= "&"; } else
4210
{ $u = "/debug.pl?"; }
4211
$u .= Bc_sql::get_constant("QUERY_DEBUG_PAGE") . "=html2";
4212
} else {
4213
$u = "/?" . $Bc_sql::QUERY_PAGE . "=" . Bc_sql::get_constant("PROFILE_PAGE");
4214
}
4215
$u .= "&" . Bc_sql::get_constant("QUERY_UID") . "=";
4216
4217
$js{tag} = "script";
4218
4219
$js{innerHTML} = "\nlet debugpl=\"";
4220
if ($debugpl) { $js{innerHTML} .= "#subtestsend"; }
4221
$js{innerHTML} .= "\";\n";
4222
4223
use POSIX;
4224
$js{innerHTML} .= "let navlastpage_$usernavddattr{id} = $usernavddattr{lastpage};\n" .
4225
"let navcurrpage_$usernavddattr{id} = $usernavddattr{page};\n" .
4226
"console.log('init navlastpage_$usernavddattr{id}: ' + navlastpage_$usernavddattr{id});\n" .
4227
"console.log('init navcurrpage_$usernavddattr{id}: ' + navcurrpage_$usernavddattr{id});\n" .
4228
"console.log('init url: $u');\n" .
4229
"console.log('init debugpl: $debugpl');\n" .
4230
4231
"\n" . ######## home
4232
"function usernavhome_$usernavddattr{id}() {\n" .
4233
" navcurrpage_$usernavddattr{id} = 1;\n" .
4234
" usernavupdate_$usernavddattr{id}();\n" .
4235
"}\n" .
4236
4237
"\n" . ######## prev
4238
"function usernavprev_$usernavddattr{id}() {\n" .
4239
" navcurrpage_$usernavddattr{id}--;\n" .
4240
" usernavupdate_$usernavddattr{id}();\n" .
4241
"}\n" .
4242
4243
"\n" . ######## next
4244
"function usernavnext_$usernavddattr{id}() {\n" .
4245
" navcurrpage_$usernavddattr{id}++;\n" .
4246
" usernavupdate_$usernavddattr{id}();\n" .
4247
"}\n" .
4248
4249
"\n" . ######## end
4250
"function usernavend_$usernavddattr{id}() {\n" .
4251
" navcurrpage_$usernavddattr{id} = navlastpage_$usernavddattr{id};\n" .
4252
" usernavupdate_$usernavddattr{id}();\n" .
4253
"}\n" .
4254
4255
"\n" . ######## reload
4256
"function usernavreload_$usernavddattr{id}() {\n" .
4257
" usernavupdate_$usernavddattr{id}();\n" .
4258
"}\n" .
4259
4260
"\n" . ######## page change
4261
"function usernavpagechange_$usernavddattr{id}() {\n" .
4262
" let pi = document.getElementById(\"ddnav_page_input_$usernavddattr{id}\");\n" .
4263
"\n" .
4264
" if (pi.value < 1) { pi.value = 1; }\n" .
4265
" else if (pi.value > navlastpage_$usernavddattr{id}) { pi.value = navlastpage_$usernavddattr{id}; }\n" .
4266
"\n" .
4267
" navcurrpage_$usernavddattr{id} = pi.value;\n" .
4268
" console.log('page change: ' + navcurrpage_$usernavddattr{id});\n" .
4269
" usernavupdate_$usernavddattr{id}();\n" .
4270
"}\n" .
4271
4272
"\n" . ######## select change
4273
"function usernavselectchange_$usernavddattr{id}() {\n" .
4274
" let e = document.getElementById('$usernavddattr{id}');\n" .
4275
" if (!e) { console.log('no such select element ID!'); r" . "eturn false; }\n" .
4276
" let v = e.options[e.selectedIndex];\n" .
4277
" console.log('\\nselect change:\\n\\nurl: " . $u . "\\n\\nselected:' + v.value + \"\\n\\ndebugpl: \" + debugpl);\n" .
4278
" document.location.href=\"$u\" + v.value + debugpl;\n" .
4279
"}\n" .
4280
4281
"\n" . ######## enable buttons
4282
"function usernavenablebuttons_$usernavddattr{id}() {\n" .
4283
" let dd = document.getElementById(\"$usernavddattr{id}\");\n" .
4284
" let hb = document.getElementById(\"ddnav_home_button_$usernavddattr{id}\");\n" .
4285
" let pb = document.getElementById(\"ddnav_prev_button_$usernavddattr{id}\");\n" .
4286
" let pi = document.getElementById(\"ddnav_page_input_$usernavddattr{id}\");\n" .
4287
" let nb = document.getElementById(\"ddnav_next_button_$usernavddattr{id}\");\n" .
4288
" let lb = document.getElementById(\"ddnav_end_button_$usernavddattr{id}\");\n" .
4289
" let rb = document.getElementById(\"ddnav_reload_button_$usernavddattr{id}\");\n" .
4290
"\n" .
4291
" dd.disabled = false;\n" .
4292
" pi.disabled = false;\n" .
4293
" rb.disabled = false;\n" .
4294
"\n" .
4295
" if (navcurrpage_$usernavddattr{id} >= 2) { hb.disabled = false; pb.disabled = false; } else { hb.disabled = true; pb.disabled = true; }\n" .
4296
" if (navcurrpage_$usernavddattr{id} <= navlastpage_$usernavddattr{id}-1) { nb.disabled = false; lb.disabled = false; } else { nb.disabled = true; lb.disabled = true; }\n" .
4297
"}\n" .
4298
4299
"\n" . ######## disable buttons
4300
"function usernavdisablebuttons_$usernavddattr{id}() {\n" .
4301
" let dd = document.getElementById(\"$usernavddattr{id}\");\n" .
4302
" let hb = document.getElementById(\"ddnav_home_button_$usernavddattr{id}\");\n" .
4303
" let pb = document.getElementById(\"ddnav_prev_button_$usernavddattr{id}\");\n" .
4304
" let pi = document.getElementById(\"ddnav_page_input_$usernavddattr{id}\");\n" .
4305
" let nb = document.getElementById(\"ddnav_next_button_$usernavddattr{id}\");\n" .
4306
" let lb = document.getElementById(\"ddnav_end_button_$usernavddattr{id}\");\n" .
4307
" let rb = document.getElementById(\"ddnav_reload_button_$usernavddattr{id}\");\n" .
4308
"\n" .
4309
" dd.disabled = true;\n" .
4310
" hb.disabled = true;\n" .
4311
" pb.disabled = true;\n" .
4312
" pi.disabled = true;\n" .
4313
" nb.disabled = true;\n" .
4314
" lb.disabled = true;\n" .
4315
" rb.disabled = true;\n" .
4316
"}\n" .
4317
4318
"\n" . ######## update
4319
"function usernavupdate_$usernavddattr{id}() {\n" .
4320
" let dd = document.getElementById(\"$usernavddattr{id}\");\n" .
4321
" dd.innerHTML = '';\n" .
4322
" usernavdisablebuttons_$usernavddattr{id}();\n" .
4323
"\n" .
4324
" hrequest(\"/getusers.pl?s=\" + ((navcurrpage_$usernavddattr{id} - 1)*1000) + \"&\").then(function(data) {\n" .
4325
" // list is ID=nickname separated by -----\n" .
4326
" //console.log(data);\n" .
4327
"\n" .
4328
" let oa = document.createElement('option');\n" .
4329
" oa.value = '';\n" .
4330
" oa.innerHTML = 'Pick One';\n" .
4331
" oa.disabled = true;\n" .
4332
" oa.selected = true;\n" .
4333
" dd.appendChild(oa);\n" .
4334
"\n" .
4335
" let L = data.split('-----');\n" .
4336
" for (let i = 0; i < L.length; i++) {\n" .
4337
" let o = L[i].split('=');\n" .
4338
" let oe = document.createElement('option');\n" .
4339
"\n" .
4340
" oe.value = o[0];\n" .
4341
" oe.innerHTML = o[1];\n" .
4342
" dd.appendChild(oe);\n" .
4343
" }\n" .
4344
" document.getElementById(\"ddnav_page_input_$usernavddattr{id}\").value = navcurrpage_$usernavddattr{id};\n" .
4345
" usernavenablebuttons_$usernavddattr{id}();\n" .
4346
" });\n" .
4347
"}\n" .
4348
4349
"\n" . ######## init
4350
"function usernavinit_$usernavddattr{id}() {\n" .
4351
" let hb = document.getElementById(\"ddnav_home_button_$usernavddattr{id}\")\n" .
4352
" let pb = document.getElementById(\"ddnav_prev_button_$usernavddattr{id}\")\n" .
4353
" let nb = document.getElementById(\"ddnav_next_button_$usernavddattr{id}\")\n" .
4354
" let lb = document.getElementById(\"ddnav_end_button_$usernavddattr{id}\")\n" .
4355
"\n" .
4356
" if (navcurrpage_$usernavddattr{id} === 1) {\n" .
4357
" hb.disabled = true;\n" .
4358
" pb.disabled = true;\n" .
4359
" } else {\n" .
4360
" hb.disabled = false;\n" .
4361
" pb.disabled = false;\n" .
4362
" }\n" .
4363
"\n" .
4364
" if (navcurrpage_$usernavddattr{id} === navlastpage_$usernavddattr{id}) {\n" .
4365
" nb.disabled = true;\n" .
4366
" lb.disabled = true;\n" .
4367
" } else {\n" .
4368
" nb.disabled = false;\n" .
4369
" lb.disabled = false;\n" .
4370
" }\n" .
4371
"}\n" .
4372
"\n" .
4373
"document.addEventListener('DOMContentLoaded', usernavinit_$usernavddattr{id}, false);\n" .
4374
"\n";
4375
4376
$usernavdddiv{innerHTML} .= tag(\%js);
4377
} # end of js
4378
4379
$usernavdddiv{innerHTML} .= navdd(\%usernavddattr);
4380
} # end of usernavddattr
4381
4382
$moddiv{innerHTML} .= tag(\%usernavdddiv);
4383
} # end usernavdd div
4384
4385
$rv .= tag(\%moddiv);
4386
} # end mod div
4387
} # end if $moderator
4388
4389
##########################
4390
## ADMINISTRATORS PANEL ##
4391
##########################
4392
if ($admin or User::isUserAdmin()) {
4393
my %adminfs; {
4394
$adminfs{tag} = "div";
4395
$adminfs{class} = "padded purple-panel min-content";
4396
$adminfs{style} = "display: grid; ";
4397
if ($debugpl)
4398
{ $adminfs{style} .= "grid-template-areas: 'title title' 'toggles buttons'; "; } else
4399
{ $adminfs{style} .= "grid-template-areas: 'title title' 'buttons buttons'; "; }
4400
$adminfs{style} .= "grid-template-columns: min-content min-content'; " .
4401
"grid-template-rows: min-content min-content'; " .
4402
"margin-top: 15px; " .
4403
"column-gap: 15px; " .
4404
"align-items: center; " .
4405
"align-content: center; " .
4406
"justify-content: center;";
4407
$adminfs{innerHTML} = "";
4408
4409
my %title; {
4410
$title{tag} = "div";
4411
$title{style} = "grid-area: title; " .
4412
"margin-bottom: 15px; ";
4413
$title{class} = "center";
4414
if ($attributes->{debug} and $attributes->{debug_borders}) { $title{class} .= " bordered"; }
4415
my %omg; {
4416
$omg{tag} = "div";
4417
$omg{class} = "yellow-panel nowrap center min-content";
4418
$omg{innerHTML} = "Administrate " . italicize(Bc_misc::word_as_possessive($stats{nickname})) . " Account";
4419
4420
$title{innerHTML} = tag(\%omg);
4421
}
4422
4423
$adminfs{innerHTML} .= tag(\%title);
4424
} # end legend
4425
4426
if ($debugpl) {
4427
my %togglesfs; {
4428
$togglesfs{tag} = "fieldset";
4429
$togglesfs{class} = "padded subnavbar";
4430
$togglesfs{style} = "grid-area: toggles; " .
4431
"display: flex; " .
4432
"flex-direction: column; " .
4433
"row-gap: 5px; ";
4434
$togglesfs{innerHTML} = "";
4435
4436
my %togglestitle; {
4437
$togglestitle{tag} = "legend";
4438
$togglestitle{class} = "noborders nowrap transparent";
4439
$togglestitle{style} = "margin-bottom: 15px; ";
4440
4441
my %debug; {
4442
$debug{tag} = "button";
4443
$debug{type} = "button";
4444
$debug{class} = "green min-content nowrap center";
4445
$debug{innerHTML} = "";
4446
4447
my %checkmark; {
4448
$checkmark{tag} = "input";
4449
$checkmark{type} = "checkbox";
4450
if ($attributes->{debug}) { $checkmark{checked} = 1; }
4451
$checkmark{class} = "no-transitions nodisabled";
4452
$checkmark{onclick} = "ret" . "urn false;";
4453
4454
$debug{innerHTML} .= tag(\%checkmark);
4455
}
4456
4457
$debug{onclick} = "document.location='";
4458
my $u = $url;
4459
my $p = "db";
4460
if (not $attributes->{debug}) {
4461
$u = Bc_misc::add_param($p, "1", $url);
4462
} else {
4463
$u = Bc_misc::remove_param("db", $url);
4464
$u = Bc_misc::remove_param("dba", $u);
4465
$u = Bc_misc::remove_param("dbb", $u);
4466
$u = Bc_misc::remove_param("dbc", $u);
4467
$u = Bc_misc::remove_param("dbg", $u);
4468
$u = Bc_misc::remove_param("dbi", $u);
4469
$u = Bc_misc::remove_param("dbo", $u);
4470
$u = Bc_misc::remove_param("dbt", $u);
4471
$u = Bc_misc::remove_param("dbu", $u);
4472
$u = Bc_misc::remove_param("dbs", $u);
4473
}
4474
$debug{onclick} .= $u;
4475
$debug{onclick} .= "#subtestsend';";
4476
4477
$debug{title} = "Click to Toggle Debug On/Off ($debug{onclick})";
4478
$debug{innerHTML} .= "Enable Debug";
4479
4480
$togglestitle{innerHTML} .= tag(\%debug);
4481
}
4482
4483
$togglesfs{innerHTML} .= tag(\%togglestitle);
4484
} # end toggles title
4485
4486
my %togglers; {
4487
$togglers{tag} = "div";
4488
$togglers{style} = "display: flex; column-gap: 15px;";
4489
if (not $attributes->{debug}) { $togglers{disabled} = 1; }
4490
if ($attributes->{debug} and $attributes->{debug_borders}) { $togglers{class} = "bordered"; }
4491
$togglers{innerHTML} = "";
4492
4493
my %displaytoggles; {
4494
$displaytoggles{tag} = "div";
4495
$displaytoggles{class} = "padded subnavbar_dark min-content";
4496
$displaytoggles{style} = "display: flex; " .
4497
"flex-direction: column; " .
4498
"row-gap: 5px; " .
4499
"justify-content: flex-start; " .
4500
"align-items: center; ";
4501
$displaytoggles{innerHTML} = "";
4502
4503
my %displaytitle; {
4504
$displaytitle{tag} = "div";
4505
$displaytitle{class} = "yellow-panel nowrap";
4506
$displaytitle{style} = "grid-area: title; margin-bottom: 5px;";
4507
$displaytitle{innerHTML} = "Toggles";
4508
4509
$displaytoggles{innerHTML} .= tag(\%displaytitle);
4510
}
4511
4512
my %showattr; {
4513
$showattr{tag} = "button";
4514
$showattr{type} = "button";
4515
$showattr{class} = "small min-content nowrap";
4516
if (not $attributes->{debug}) {
4517
$showattr{disabled} = 1;
4518
} else {
4519
$showattr{class} .= " green";
4520
}
4521
$showattr{innerHTML} = "";
4522
4523
my %checkmark; {
4524
$checkmark{tag} = "input";
4525
$checkmark{type} = "checkbox";
4526
if ($attributes->{debug} and $attributes->{debug_attributes}) { $checkmark{checked} = 1; }
4527
$checkmark{class} = "no-transitions nodisabled";
4528
$checkmark{onclick} = "ret" . "urn false;";
4529
4530
$showattr{innerHTML} .= tag(\%checkmark);
4531
}
4532
4533
$showattr{onclick} = "document.location='";
4534
my $u = $url;
4535
my $p = "dba";
4536
if (not $attributes->{debug_attributes}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4537
$showattr{onclick} .= $u;
4538
$showattr{onclick} .= "#subtestsend';";
4539
$showattr{title} = "Click to Toggle Displaying Attributes On/Off";
4540
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $showattr{title} .= " ($showattr{onclick})"; }
4541
4542
$showattr{innerHTML} .= "Attributes";
4543
4544
$displaytoggles{innerHTML} .= tag(\%showattr);
4545
}
4546
4547
my %showtdata; {
4548
$showtdata{tag} = "button";
4549
$showtdata{type} = "button";
4550
$showtdata{class} = "small min-content nowrap";
4551
if (not $attributes->{debug}) {
4552
$showtdata{disabled} = 1;
4553
} else {
4554
$showtdata{class} .= " green";
4555
}
4556
$showtdata{innerHTML} = "";
4557
4558
my %checkmark; {
4559
$checkmark{tag} = "input";
4560
$checkmark{type} = "checkbox";
4561
if ($attributes->{debug} and $attributes->{debug_themedata}) { $checkmark{checked} = 1; }
4562
$checkmark{class} = "no-transitions nodisabled";
4563
$checkmark{onclick} = "ret" . "urn false;";
4564
4565
$showtdata{innerHTML} .= tag(\%checkmark);
4566
}
4567
4568
$showtdata{onclick} = "document.location='";
4569
my $u = $url;
4570
my $p = "dbt";
4571
if (not $attributes->{debug_themedata}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4572
$showtdata{onclick} .= $u;
4573
$showtdata{onclick} .= "#subtestsend';";
4574
$showtdata{title} = "Click to Toggle Displaying Theme Data On/Off";
4575
if ($attributes->{debug}) { $showtdata{title} .= " ($showtdata{onclick})"; }
4576
4577
$showtdata{innerHTML} .= "Theme Data";
4578
4579
$displaytoggles{innerHTML} .= tag(\%showtdata);
4580
}
4581
4582
my %showstats; {
4583
$showstats{tag} = "button";
4584
$showstats{type} = "button";
4585
$showstats{class} = "small min-content nowrap";
4586
if (not $attributes->{debug}) {
4587
$showstats{disabled} = 1;
4588
} else {
4589
$showstats{class} .= " green";
4590
}
4591
$showstats{innerHTML} = "";
4592
4593
my %checkmark; {
4594
$checkmark{tag} = "input";
4595
$checkmark{type} = "checkbox";
4596
if ($attributes->{debug} and $attributes->{debug_stats}) { $checkmark{checked} = 1; }
4597
$checkmark{class} = "no-transitions nodisabled";
4598
$checkmark{onclick} = "ret" . "urn false;";
4599
4600
$showstats{innerHTML} .= tag(\%checkmark);
4601
}
4602
4603
$showstats{onclick} = "document.location='";
4604
my $u = $url;
4605
my $p = "dbs";
4606
if (not $attributes->{debug_stats}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4607
$showstats{onclick} .= $u;
4608
$showstats{onclick} .= "#subtestsend';";
4609
$showstats{title} = "Click to Toggle Displaying User Stats On/Off";
4610
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $showstats{title} .= " ($showstats{onclick})"; }
4611
4612
$showstats{innerHTML} .= "User Stats";
4613
4614
$displaytoggles{innerHTML} .= tag(\%showstats);
4615
}
4616
4617
my %showonclick; {
4618
$showonclick{tag} = "button";
4619
$showonclick{type} = "button";
4620
$showonclick{class} = "small min-content nowrap";
4621
if (not $attributes->{debug}) {
4622
$showonclick{disabled} = 1;
4623
} else {
4624
$showonclick{class} .= " green";
4625
}
4626
$showonclick{innerHTML} = "";
4627
4628
my %checkmark; {
4629
$checkmark{tag} = "input";
4630
$checkmark{type} = "checkbox";
4631
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $checkmark{checked} = 1; }
4632
$checkmark{class} = "no-transitions nodisabled";
4633
$checkmark{onclick} = "ret" . "urn false;";
4634
4635
$showonclick{innerHTML} .= tag(\%checkmark);
4636
}
4637
4638
$showonclick{onclick} = "document.location='";
4639
my $u = $url;
4640
my $p = "dbo";
4641
if (not $attributes->{debug_onclicks}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4642
$showonclick{onclick} .= $u;
4643
$showonclick{onclick} .= "#subtestsend';";
4644
$showonclick{title} = "Click to Toggle Adding OnClicks to Tooltips On/Off";
4645
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $showonclick{title} .= " ($showonclick{onclick})"; }
4646
4647
$showonclick{innerHTML} .= "OnClicks";
4648
4649
$displaytoggles{innerHTML} .= tag(\%showonclick);
4650
}
4651
4652
my %showborders; {
4653
$showborders{tag} = "button";
4654
$showborders{type} = "button";
4655
$showborders{class} = "small min-content nowrap";
4656
if (not $attributes->{debug}) {
4657
$showborders{disabled} = 1;
4658
} else {
4659
$showborders{class} .= " green";
4660
}
4661
$showborders{innerHTML} = "";
4662
4663
my %checkmark; {
4664
$checkmark{tag} = "input";
4665
$checkmark{type} = "checkbox";
4666
if ($attributes->{debug} and $attributes->{debug_borders}) { $checkmark{checked} = 1; }
4667
$checkmark{class} = "no-transitions nodisabled";
4668
$checkmark{onclick} = "ret" . "urn false;";
4669
4670
$showborders{innerHTML} .= tag(\%checkmark);
4671
}
4672
4673
$showborders{onclick} = "document.location='";
4674
my $u = $url;
4675
my $p = "dbb";
4676
if (not $attributes->{debug_borders}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4677
$showborders{onclick} .= $u;
4678
$showborders{onclick} .= "#subtestsend';";
4679
$showborders{title} = "Click to Toggle Displaying Borders On/Off";
4680
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $showborders{title} .= " ($showborders{onclick})"; }
4681
4682
$showborders{innerHTML} .= "Borders";
4683
4684
$displaytoggles{innerHTML} .= tag(\%showborders);
4685
}
4686
4687
my %jsconsole; {
4688
$jsconsole{tag} = "button";
4689
$jsconsole{type} = "button";
4690
$jsconsole{class} = "small min-content nowrap";
4691
if (not $attributes->{debug}) {
4692
$jsconsole{disabled} = 1;
4693
} else {
4694
$jsconsole{class} .= " green";
4695
}
4696
$jsconsole{innerHTML} = "";
4697
4698
my %checkmark; {
4699
$checkmark{tag} = "input";
4700
$checkmark{type} = "checkbox";
4701
if ($attributes->{debug} and $attributes->{debug_jsconsole}) { $checkmark{checked} = 1; }
4702
$checkmark{class} = "no-transitions nodisabled";
4703
$checkmark{onclick} = "ret" . "urn false;";
4704
4705
$jsconsole{innerHTML} .= tag(\%checkmark);
4706
}
4707
4708
$jsconsole{onclick} = "document.location='";
4709
my $u = $url;
4710
my $p = "dbc";
4711
if (not $attributes->{debug_jsconsole}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4712
$jsconsole{onclick} .= $u;
4713
$jsconsole{onclick} .= "#subtestsend';";
4714
$jsconsole{title} = "Click to Toggle JS Console Output On/Off";
4715
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $jsconsole{title} .= " ($jsconsole{onclick})"; }
4716
4717
$jsconsole{innerHTML} .= "JS Console";
4718
4719
$displaytoggles{innerHTML} .= tag(\%jsconsole);
4720
}
4721
4722
$togglers{innerHTML} .= tag(\%displaytoggles);
4723
} # end of display togglers
4724
4725
my %actasbuttons; {
4726
$actasbuttons{tag} = "div";
4727
$actasbuttons{class} = "padded subnavbar_dark min-content";
4728
$actasbuttons{style} = "display: flex; " .
4729
"flex-direction: column; " .
4730
"row-gap: 5px; " .
4731
"justify-content: flex-start; " .
4732
"align-items: center; ";
4733
$actasbuttons{innerHTML} = "";
4734
4735
my %actastitle; {
4736
$actastitle{tag} = "div";
4737
$actastitle{class} = "yellow-panel nowrap";
4738
$actastitle{style} = "margin-bottom: 5px;";
4739
$actastitle{innerHTML} = "Act As";
4740
4741
$actasbuttons{innerHTML} .= tag(\%actastitle);
4742
}
4743
4744
my %actuser; {
4745
$actuser{tag} = "button";
4746
$actuser{type} = "button";
4747
$actuser{class} = "small min-content nowrap";
4748
if (not $attributes->{debug}) {
4749
$actuser{disabled} = 1;
4750
} else {
4751
$actuser{class} .= " yellow";
4752
}
4753
$actuser{innerHTML} = "";
4754
4755
my %checkmark; {
4756
$checkmark{tag} = "input";
4757
$checkmark{type} = "checkbox";
4758
if ($attributes->{debug_actasuser}) { $checkmark{checked} = 1; }
4759
$checkmark{class} = "no-transitions nodisabled";
4760
$checkmark{onclick} = "ret" . "urn false;";
4761
4762
$actuser{innerHTML} .= tag(\%checkmark);
4763
}
4764
4765
$actuser{onclick} = "document.location='";
4766
my $u = $url;
4767
my $p = "dbu";
4768
if (not $attributes->{debug_actasuser}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4769
$u = Bc_misc::remove_param("dbg", $u);
4770
$actuser{onclick} .= $u;
4771
$actuser{onclick} .= "#subtestsend';";
4772
$actuser{title} = "Act as User";
4773
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $actuser{title} .= " ($actuser{onclick})"; }
4774
$actuser{innerHTML} .= "User";
4775
4776
$actasbuttons{innerHTML} .= tag(\%actuser);
4777
} # end act as user button
4778
4779
my %actguest; {
4780
$actguest{tag} = "button";
4781
$actguest{type} = "button";
4782
$actguest{class} = "small min-content nowrap";
4783
if (not $attributes->{debug}) {
4784
$actguest{disabled} = 1;
4785
} else {
4786
$actguest{class} .= " yellow";
4787
}
4788
$actguest{innerHTML} = "";
4789
4790
my %checkmark; {
4791
$checkmark{tag} = "input";
4792
$checkmark{type} = "checkbox";
4793
if ($attributes->{debug_actasguest}) { $checkmark{checked} = 1; }
4794
$checkmark{class} = "no-transitions nodisabled";
4795
$checkmark{onclick} = "ret" . "urn false;";
4796
4797
$actguest{innerHTML} .= tag(\%checkmark);
4798
}
4799
4800
$actguest{onclick} = "document.location='";
4801
my $u = $url;
4802
my $p = "dbg";
4803
if (not $attributes->{debug_actasguest}) { $u = Bc_misc::add_param($p, "1", $url); } else { $u = Bc_misc::remove_param($p, $url); }
4804
$u = Bc_misc::remove_param("dbu", $u);
4805
$actguest{onclick} .= $u;
4806
$actguest{onclick} .= "#subtestsend';";
4807
$actguest{title} = "Act as Guest User";
4808
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $actguest{title} .= " ($actguest{onclick})"; }
4809
$actguest{innerHTML} .= "Guest";
4810
4811
$actasbuttons{innerHTML} .= tag(\%actguest);
4812
} # end act as guest button
4813
4814
my %dropact; {
4815
$dropact{tag} = "button";
4816
$dropact{type} = "button";
4817
$dropact{class} .= "small min-content nowrap";
4818
if (not $attributes->{debug}) {
4819
$dropact{disabled} = 1;
4820
} else {
4821
if ($attributes->{debug_actasuser} or $attributes->{debug_actasguest}) {
4822
$dropact{class} .= " yellow";
4823
}
4824
}
4825
$dropact{innerHTML} = "";
4826
4827
my %checkmark; {
4828
$checkmark{tag} = "input";
4829
$checkmark{type} = "checkbox";
4830
if (not $attributes->{debug_actasuser} and not $attributes->{debug_actasguest}) {
4831
$checkmark{checked} = 1;
4832
$checkmark{disabled} = 1;
4833
$dropact{disabled} = 1;
4834
}
4835
$checkmark{class} = "no-transitions nodisabled";
4836
$checkmark{onclick} = "ret" . "urn false;";
4837
4838
$dropact{innerHTML} .= tag(\%checkmark);
4839
}
4840
4841
$dropact{onclick} = "document.location='";
4842
my $u = $url;
4843
$u = Bc_misc::remove_param("dbu", $u);
4844
$u = Bc_misc::remove_param("dbg", $u);
4845
$dropact{onclick} .= $u;
4846
$dropact{onclick} .= "#subtestsend';";
4847
$dropact{title} = "Drop the Act!";
4848
if ($attributes->{debug} and $attributes->{debug_onclicks}) { $dropact{title} .= " ($dropact{onclick})"; }
4849
$dropact{innerHTML} .= "Drop Act";
4850
4851
$actasbuttons{innerHTML} .= tag(\%dropact);
4852
} # end drop act button
4853
4854
$togglers{innerHTML} .= tag(\%actasbuttons);
4855
} # end of actas div
4856
4857
$togglesfs{innerHTML} .= tag(\%togglers);
4858
} # end of togglers
4859
4860
$adminfs{innerHTML} .= tag(\%togglesfs);
4861
} # end toggles div
4862
} # end if $debugpl
4863
4864
my %buttonsdiv; {
4865
$buttonsdiv{tag} = "div";
4866
$buttonsdiv{class} = "padded subnavbar center";
4867
$buttonsdiv{style} = "grid-area: buttons; " .
4868
"display: flex; " .
4869
"flex-direction: column; " .
4870
"width: min-content; ";
4871
4872
my %buttonstitle; {
4873
$buttonstitle{tag} = "div";
4874
$buttonstitle{class} = "yellow-panel nowrap center";
4875
$buttonstitle{style} = "grid-area: title; " .
4876
"margin-bottom: 15px; ";
4877
$buttonstitle{innerHTML} = "Button Panel";
4878
4879
$buttonsdiv{innerHTML} .= tag(\%buttonstitle);
4880
} # end buttons title
4881
4882
$adminfs{innerHTML} .= tag(\%buttonsdiv);
4883
} # end editorbutton
4884
4885
$rv .= tag(\%adminfs);
4886
} # end adminfs
4887
} # end if $admin
4888
} # end if not $attributes->{mini}
4889
} # end if user exists
4890
else {
4891
$rv = embolden("$attributes->{uid}") . " is not a valid user id";
4892
} # end else of if user exists
4893
} # end if (ref $attributes eq "HASH")
4894
else {
4895
$rv = embolden("\$attributes") . " must be a hash reference";
4896
} # end else of if (ref $attributes eq "HASH")
4897
4898
# now, as an after thought, let's containerize the whole thing
4899
my %motorhome; {
4900
$motorhome{tag} = "div";
4901
$motorhome{style} = "display: flex; " .
4902
"flex-direction: column; " .
4903
"align-items: center; ";
4904
$motorhome{class} = "motorhome";
4905
if ($attributes->{debug} and $attributes->{debug_borders}) { $motorhome{class} .= " bordered"; }
4906
$motorhome{innerHTML} = $rv;
4907
} # end motorhome
4908
4909
$rv = tag(\%motorhome);
4910
4911
return $rv; # a scalar
4912
#usage: $output .= display_user_profile(\%attributes);
4913
}
4914
4915
########################
4916
push @EXPORT_OK, "profile_link";
4917
sub profile_link(;$) {
4918
#*
4919
# returns a link to a given user's profile<br>
4920
4921
# <i>$uid</i> can be either a scalar, or a
4922
# a hash reference. if it's a hash:
4923
# $uid->{$Bc_sql::QUERY_UID} is required
4924
# you may include any other valid html attribute, too.
4925
# override what html tag is used, if you want!
4926
#*
4927
my ($uid) = @_; # a user ID or a hash reference! (optional, default=$Bc_sql::LOGGEDIN)
4928
my %rv;
4929
4930
if (ref $uid eq "HASH") {
4931
if (not $uid->{$Bc_sql::QUERY_UID}) { $uid->{$Bc_sql::QUERY_UID} = $Bc_sql::LOGGEDIN; }
4932
my $nn = User::get_user_stat($uid->{$Bc_sql::QUERY_UID}, "nickname");
4933
4934
my %link = %$uid;
4935
if ($nn) {
4936
if (not $uid->{tag}) { $link{tag} = "a"; }
4937
if (not $uid->{href}) { $link{href} = "/?$Bc_sql::QUERY_PAGE=" . Bc_sql::get_constant("PROFILE_PAGE") . "&$Bc_sql::QUERY_UID=$uid->{$Bc_sql::QUERY_UID}"; }
4938
if (not $uid->{title}) { $link{title} = Bc_misc::word_as_possessive($Bc_sql::USER_DATA->{nickname}) . " Profile"; }
4939
if (not $uid->{innerHTML}) { $link{innerHTML} = $nn; }
4940
} else {
4941
$link{tag} = "font";
4942
$link{class} = "red-panel";
4943
$link{innerHTML} = "Invalid Profile ID: $uid->{$Bc_sql::QUERY_UID}";
4944
}
4945
4946
%rv = %link;
4947
} # end if (ref $uid eq "HASH")
4948
else {
4949
if (not $uid) { $uid = $Bc_sql::LOGGEDIN; }
4950
my $nn = User::get_user_stat($uid, "nickname");
4951
4952
my %link;
4953
if ($nn) {
4954
$link{tag} = "a";
4955
$link{href} = "/?$Bc_sql::QUERY_PAGE=" . Bc_sql::get_constant("PROFILE_PAGE") . "&$Bc_sql::QUERY_UID=$uid";
4956
$link{title} = Bc_misc::word_as_possessive($Bc_sql::USER_DATA->{nickname}) . " Profile";
4957
$link{innerHTML} = $nn;
4958
} else {
4959
$link{tag} = "font";
4960
$link{class} = "red-panel";
4961
$link{innerHTML} = "Invalid Profile";
4962
}
4963
4964
%rv = %link;
4965
} # end else of if (ref $uid eq "HASH")
4966
4967
return tag(\%rv); # a scalar
4968
#usage: $output .= profile_link($uid); # or profile_link(\%attributes);
4969
}
4970
4971
########################
4972
push @EXPORT_OK, "facebook_share_properties";
4973
sub facebook_share_properties(;$$$) {
4974
#*
4975
# take this subroutine's output and pass it
4976
# to the head subroutine as $headextras
4977
#*
4978
my ($url, $title, $desc) = @_; # a url for the page being shared (optional, default=https://night-stand.ca/index.pl) && a title to show on fb's sharing interface && a description
4979
if (not $url) { $url = "https://night-stand.ca/index.pl"; }
4980
if (not $title) { $title = "(title missing)"; }
4981
if (not $desc) { $desc = "(not a synopsis)"; }
4982
4983
my $rv = <<END;
4984
<meta property="og:url" content="$url" />
4985
<meta property="og:type" content="website" />
4986
<meta property="og:title" content="$title - $Bc_sql::SITE_NAME" />
4987
<meta property="og:description" content="$desc" />
4988
<meta property="og:image" content="https://night-stand.ca/images/site/books.png" />
4989
<meta property="og:image:width" content="200" />
4990
<meta property="og:image:height" content="200" />
4991
END
4992
4993
return $rv; # a scalar
4994
#usage: my $meta = facebook_share_properties();
4995
}
4996
4997
########################
4998
push @EXPORT_OK, "facebook_share_sdk";
4999
sub facebook_share_sdk(;$) {
5000
#*
5001
# take this subroutine's output and insert
5002
# it into the body's html output as the
5003
# first children
5004
#*
5005
my ($something) = @_; # a variable
5006
my $rv = <<END;
5007
<div id="fb-root"></div>
5008
<script async defer crossorigin="anonymous" src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v10.0" nonce="wChsiGk2"></script>
5009
END
5010
5011
return $rv; # a scalar
5012
#usage: my $fb = facebook_share_sdk();
5013
}
5014
5015
########################
5016
push @EXPORT_OK, "facebook_share_button";
5017
sub facebook_share_button(;$) {
5018
#*
5019
# take this subroutine's output and insert
5020
# it into the body's html output as the
5021
# first children
5022
#*
5023
my ($url) = @_; # a url for the page being shared (optional, default=https://night-stand.ca/index.pl)
5024
if (not $url) { $url = "https://night-stand.ca/index.pl"; }
5025
my $rv = <<END;
5026
<div
5027
class="fb-share-button boxshadow"
5028
style=""
5029
data-href="$url"
5030
data-layout="button_count"><a
5031
target="_blank"
5032
href="https://www.facebook.com/sharer/sharer.php?u=$url"
5033
class="fb-xfbml-parse-ignore"></a>
5034
</div>
5035
END
5036
5037
{ my %container;
5038
$container{tag} = "div";
5039
$container{class} = "enlarge";
5040
$container{html} = $rv;
5041
5042
$rv = tag(\%container);
5043
}
5044
5045
return $rv; # a scalar
5046
#usage: my $fb = facebook_share_button();
5047
}
5048
5049
########################
5050
push @EXPORT_OK, "holy_grail";
5051
sub holy_grail(;$) {
5052
#*
5053
# the holy grail of grids!<br>
5054
5055
# $contents needs to be a hash reference, keys are:
5056
# any valid html attribute (used on grid)
5057
# {debug} toggles debugging on/off
5058
# {grid-header} will be a hash reference for use as contents of header grid area
5059
# {grid-aside-left} will be a hash reference for use as contents of left aside grid area
5060
# {grid-article} will be a hash reference for use as contents of article grid area
5061
# {grid-aside-right} will be a hash reference for use as contents of right aside grid area
5062
# {grid-footer} will be a hash reference for use as contents of footer grid area
5063
# {grid-gap} will be a valid value for CSS gap property
5064
# {grid-aside-left-width} will be a valid value for CSS aside left width property
5065
# {grid-aside-right-width} will be a valid value for CSS aside right width property
5066
# {grid-aside-left-height} will be a valid value for CSS aside left height property
5067
# {grid-aside-right-height} will be a valid value for CSS aside right height property
5068
#*
5069
my ($contents) = @_; # the contents (see description)
5070
my $rv = "";
5071
5072
if (ref $contents eq "HASH") {
5073
if (not $contents->{id}) {
5074
# generate a random id
5075
$contents->{id} = Bc_misc::new_id();
5076
}
5077
5078
{ my %css;
5079
$css{tag} = "style";
5080
my $alw = "123px";
5081
my $arw = "123px";
5082
my $alh = "50px";
5083
my $arh = "50px";
5084
if ($contents->{"grid-aside-left-width"}) { $alw = $contents->{"grid-aside-left-width"}; }
5085
if ($contents->{"grid-aside-right-width"}) { $alw = $contents->{"grid-aside-right-width"}; }
5086
if ($contents->{"grid-aside-left-height"}) { $alh = $contents->{"grid-aside-left-height"}; }
5087
if ($contents->{"grid-aside-right-height"}) { $alh = $contents->{"grid-aside-right-height"}; }
5088
$css{html} = <<END;
5089
.holy_grail_$contents->{id} {
5090
display: grid;
5091
5092
grid-template-columns: $alw 1fr $arw;
5093
grid-template-rows: min-content 1fr min-content;
5094
grid-template-areas: "grid-header-$contents->{id} grid-header-$contents->{id} grid-header-$contents->{id}"
5095
"grid-aside-left-$contents->{id} grid-article-$contents->{id} grid-aside-right-$contents->{id}"
5096
"grid-footer-$contents->{id} grid-footer-$contents->{id} grid-footer-$contents->{id}";
5097
END
5098
5099
$css{html} .= " gap: ";
5100
if ($contents->{"grid-gap"}) { $css{html} .= $contents->{"grid-gap"}; }
5101
$css{html} .= ";\n";
5102
5103
$css{html} .= <<END;
5104
}
5105
5106
\@media (max-width: 570px) {
5107
.holy_grail_$contents->{id} {
5108
grid-template-columns: 1fr;
5109
grid-template-rows: min-content $alh minmax(123px, 1fr) $arh min-content;
5110
grid-template-areas: "grid-header-$contents->{id}"
5111
"grid-aside-left-$contents->{id}"
5112
"grid-article-$contents->{id}"
5113
"grid-aside-right-$contents->{id}"
5114
"grid-footer-$contents->{id}";
5115
}
5116
}
5117
END
5118
5119
$rv .= tag(\%css);
5120
}
5121
5122
{ my %container;
5123
%container = %$contents;
5124
$container{tag} = "div";
5125
if ($container{class}) { $container{class} .= " "; }
5126
$container{class} .= "holy_grail_$contents->{id}";
5127
if ($contents->{debug}) {
5128
if ($container{style}) { $container{style} .= " "; }
5129
$container{style} .= "background-color: red;";
5130
}
5131
$container{html} = "";
5132
5133
{ my %header;
5134
$header{tag} = "header";
5135
if ($contents->{"grid-header"}->{class}) { $header{class} = $contents->{"grid-header"}->{class} . " "; }
5136
$header{class} .= "text-align-center";
5137
$header{style} = "grid-area: grid-header-$contents->{id};";
5138
if ($contents->{"grid-header"}->{html})
5139
{ $header{html} = $contents->{"grid-header"}->{html}; } else
5140
{ $header{html} = "(header)"; }
5141
if ($contents->{debug} or $contents->{"grid-header"}->{debug}) {
5142
if ($header{style}) { $header{style} .= " "; }
5143
$header{style} .= "background-color: green;";
5144
}
5145
5146
$container{html} .= tag(\%header);
5147
}
5148
5149
{ my %asideleft;
5150
$asideleft{tag} = "aside left";
5151
if ($contents->{"grid-aside-left"}->{class}) { $asideleft{class} = $contents->{"grid-aside-left"}->{class}; }
5152
$asideleft{style} = "grid-area: grid-aside-left-$contents->{id};";
5153
if ($contents->{"grid-aside-left"}->{html})
5154
{ $asideleft{html} = $contents->{"grid-aside-left"}->{html}; } else
5155
{ $asideleft{html} = "(aside left)"; }
5156
if ($contents->{debug} or $contents->{"grid-aside-left"}->{debug}) {
5157
if ($asideleft{style}) { $asideleft{style} .= " "; }
5158
$asideleft{style} .= "background-color: blue;";
5159
}
5160
5161
$container{html} .= tag(\%asideleft);
5162
}
5163
5164
{ my %article;
5165
$article{tag} = "article";
5166
if ($contents->{"grid-article"}->{class}) { $article{class} = $contents->{"grid-article"}->{class}; }
5167
if ($article{class}) { $article{class} .= " "; }
5168
$article{class} .= "scrolling_vertical-auto";
5169
$article{style} = "grid-area: grid-article-$contents->{id};";
5170
if ($contents->{"grid-article"}->{html})
5171
{ $article{html} = $contents->{"grid-article"}->{html}; } else
5172
{ $article{html} = "(article)"; }
5173
if ($contents->{debug} or $contents->{"grid-article"}->{debug}) {
5174
if ($article{style}) { $article{style} .= " "; }
5175
$article{style} .= "background-color: white; color: black;";
5176
}
5177
5178
$container{html} .= tag(\%article);
5179
}
5180
5181
{ my %asideright;
5182
$asideright{tag} = "aside right";
5183
if ($contents->{"grid-aside-right"}->{class}) { $asideright{class} = $contents->{"grid-aside-right"}->{class}; }
5184
$asideright{style} = "grid-area: grid-aside-right-$contents->{id};";
5185
if ($contents->{"grid-aside-right"}->{html})
5186
{ $asideright{html} = $contents->{"grid-aside-right"}->{html}; } else
5187
{ $asideright{html} = "(aside right)"; }
5188
if ($contents->{debug} or $contents->{"grid-aside-right"}->{debug}) {
5189
if ($asideright{style}) { $asideright{style} .= " "; }
5190
$asideright{style} .= "background-color: blue;";
5191
}
5192
5193
$container{html} .= tag(\%asideright);
5194
}
5195
5196
{ my %footer;
5197
$footer{tag} = "footer";
5198
if ($contents->{"grid-footer"}->{class}) { $footer{class} = $contents->{"grid-footer"}->{class} . " "; }
5199
$footer{class} .= "text-align-center";
5200
$footer{style} = "grid-area: grid-footer-$contents->{id};";
5201
if ($contents->{"grid-footer"}->{html})
5202
{ $footer{html} = $contents->{"grid-footer"}->{html}; } else
5203
{ $footer{html} = "(footer)"; }
5204
if ($contents->{debug} or $contents->{"grid-footer"}->{debug}) {
5205
if ($footer{style}) { $footer{style} .= " "; }
5206
$footer{style} .= "background-color: black;";
5207
if ($footer{html}) { $footer{html} .= br; }
5208
$footer{html} .= "Grid ID: " . $contents->{id};
5209
}
5210
5211
$container{html} .= tag(\%footer);
5212
}
5213
5214
$rv .= tag(\%container);
5215
}
5216
} else {
5217
$rv = "No content!?";
5218
}
5219
5220
return $rv; # a scalar
5221
#usage: print holy_grail(\%contents);
5222
}
5223
5224
########################
5225
push @EXPORT_OK, "holy_grail_alternate";
5226
sub holy_grail_alternate(;$) {
5227
#*
5228
# the alternate holy grail of grids!<br>
5229
5230
# $contents needs to be a hash reference, keys are:
5231
# any valid html attribute (used on grid)
5232
# {debug} toggles debugging on/off
5233
# {grid-header} will be a hash reference for use as contents of header grid area
5234
# {grid-aside} will be a hash reference for use as contents of aside grid area
5235
# {grid-nav} will be a hash reference for use as contents of nav grid area
5236
# {grid-article} will be a hash reference for use as contents of article grid area
5237
# {grid-footer} will be a hash reference for use as contents of footer grid area
5238
# {grid-gap} will be a valid value for CSS gap property
5239
# {grid-aside-width} will be the min/max width of the aside
5240
# {grid-aside-height} will be the min/max height of the aside
5241
#*
5242
my ($contents) = @_; # the contents (see description)
5243
my $rv = "";
5244
5245
if (ref $contents eq "HASH") {
5246
if (not $contents->{id}) {
5247
# generate a random id
5248
$contents->{id} = Bc_misc::new_id();
5249
}
5250
5251
{ my %css;
5252
$css{tag} = "style";
5253
my $aw = "123px";
5254
my $ah = "50px";
5255
if ($contents->{"grid-aside-width"}) { $aw = $contents->{"grid-aside-width"}; }
5256
if ($contents->{"grid-aside-height"}) { $ah = $contents->{"grid-aside-height"}; }
5257
$css{html} = <<END;
5258
.holy_grail_alternate_$contents->{id} {
5259
display: grid;
5260
5261
grid-template-columns: $aw 1fr;
5262
grid-template-rows: min-content min-content 1fr min-content;
5263
grid-template-areas: "grid-header-$contents->{id} grid-header-$contents->{id}"
5264
"grid-aside-$contents->{id} grid-nav-$contents->{id}"
5265
"grid-aside-$contents->{id} grid-article-$contents->{id}"
5266
"grid-footer-$contents->{id} grid-footer-$contents->{id}";
5267
END
5268
5269
$css{html} .= " gap: ";
5270
if ($contents->{"grid-gap"}) { $css{html} .= $contents->{"grid-gap"}; }
5271
$css{html} .= ";\n";
5272
5273
$css{html} .= <<END;
5274
}
5275
5276
5277
\@media (max-width: 570px) {
5278
.holy_grail_alternate_$contents->{id} {
5279
grid-template-columns: 1fr;
5280
grid-template-rows: min-content $ah min-content minmax(123px, 1fr) min-content;
5281
grid-template-areas: "grid-header-$contents->{id}"
5282
"grid-aside-$contents->{id}"
5283
"grid-nav-$contents->{id}"
5284
"grid-article-$contents->{id}"
5285
"grid-footer-$contents->{id}";
5286
}
5287
}
5288
END
5289
5290
$rv .= tag(\%css);
5291
}
5292
5293
{ my %container;
5294
%container = %$contents;
5295
$container{tag} = "div";
5296
if ($container{class}) { $container{class} .= " "; }
5297
$container{class} .= "holy_grail_alternate_$contents->{id}";
5298
if ($contents->{debug}) {
5299
if ($container{style}) { $container{style} .= " "; }
5300
$container{style} .= "background-color: red;";
5301
}
5302
$container{html} = "";
5303
5304
{ my %header;
5305
$header{tag} = "header";
5306
if ($contents->{"grid-header"}->{class}) { $header{class} = $contents->{"grid-header"}->{class} . " "; }
5307
$header{class} .= "text-align-center";
5308
$header{style} = "grid-area: grid-header-$contents->{id};";
5309
if ($contents->{"grid-header"}->{html})
5310
{ $header{html} = $contents->{"grid-header"}->{html}; } else
5311
{ $header{html} = "(header)"; }
5312
if ($contents->{debug} or $contents->{"grid-header"}->{debug}) {
5313
if ($header{style}) { $header{style} .= " "; }
5314
$header{style} .= "background-color: green;";
5315
}
5316
5317
$container{html} .= tag(\%header);
5318
}
5319
5320
{ my %aside;
5321
$aside{tag} = "aside";
5322
if ($contents->{"grid-aside"}->{class}) { $aside{class} = $contents->{"grid-aside"}->{class}; }
5323
$aside{style} = "grid-area: grid-aside-$contents->{id};";
5324
if ($contents->{"grid-aside"}->{html})
5325
{ $aside{html} = $contents->{"grid-aside"}->{html}; } else
5326
{ $aside{html} = "(aside)"; }
5327
if ($contents->{debug} or $contents->{"grid-aside"}->{debug}) {
5328
if ($aside{style}) { $aside{style} .= " "; }
5329
$aside{style} .= "background-color: blue;";
5330
}
5331
5332
$container{html} .= tag(\%aside);
5333
}
5334
5335
{ my %nav;
5336
$nav{tag} = "nav";
5337
if ($contents->{"grid-nav"}->{class}) { $nav{class} = $contents->{"grid-nav"}->{class} . " "; }
5338
$nav{class} .= "text-align-center";
5339
$nav{style} = "grid-area: grid-nav-$contents->{id};";
5340
if ($contents->{"grid-nav"}->{html})
5341
{ $nav{html} = $contents->{"grid-nav"}->{html}; } else
5342
{ $nav{html} = "(nav)"; }
5343
if ($contents->{debug} or $contents->{"grid-nav"}->{debug}) {
5344
if ($nav{style}) { $nav{style} .= " "; }
5345
$nav{style} .= "background-color: yellow; color: black;";
5346
}
5347
5348
$container{html} .= tag(\%nav);
5349
}
5350
5351
{ my %article;
5352
$article{tag} = "article";
5353
if ($contents->{"grid-article"}->{class}) { $article{class} = $contents->{"grid-article"}->{class}; }
5354
if ($article{class}) { $article{class} .= " "; }
5355
$article{class} .= "scrolling_vertical-auto";
5356
$article{style} = "grid-area: grid-article-$contents->{id};";
5357
if ($contents->{"grid-article"}->{html})
5358
{ $article{html} = $contents->{"grid-article"}->{html}; } else
5359
{ $article{html} = "(article)"; }
5360
if ($contents->{debug} or $contents->{"grid-article"}->{debug}) {
5361
if ($article{style}) { $article{style} .= " "; }
5362
$article{style} .= "background-color: white; color: black;";
5363
}
5364
5365
$container{html} .= tag(\%article);
5366
}
5367
5368
{ my %footer;
5369
$footer{tag} = "footer";
5370
if ($contents->{"grid-footer"}->{class}) { $footer{class} = $contents->{"grid-footer"}->{class} . " "; }
5371
$footer{class} .= "text-align-center";
5372
$footer{style} = "grid-area: grid-footer-$contents->{id};";
5373
if ($contents->{"grid-footer"}->{html})
5374
{ $footer{html} = $contents->{"grid-footer"}->{html}; } else
5375
{ $footer{html} = "(footer)"; }
5376
if ($contents->{debug} or $contents->{"grid-footer"}->{debug}) {
5377
if ($footer{style}) { $footer{style} .= " "; }
5378
$footer{style} .= "background-color: black;";
5379
if ($footer{html}) { $footer{html} .= br; }
5380
$footer{html} .= "Grid ID: " . $contents->{id};
5381
}
5382
5383
$container{html} .= tag(\%footer);
5384
}
5385
5386
$rv .= tag(\%container);
5387
}
5388
} # end if ref $contents eq "HASH"
5389
else {
5390
$rv = "no content!";
5391
}
5392
5393
return $rv; # a scalar
5394
#usage: print holy_grail_alternate(\%contents);
5395
}
5396
5397
########################
5398
push @EXPORT_OK, "modal";
5399
sub modal($$$$$$;$$) {
5400
#*
5401
# a fully customizable, hidden modal
5402
#
5403
# it's up to you to include the bits to
5404
# show the modal! it is??
5405
#
5406
# where $toggler format is: type,label[,src]
5407
# where type can be: button, link, or almost
5408
# any other HTML element (including images)
5409
#
5410
# $id must be unique
5411
#*
5412
my ($id, $toggler, $class, $title, $content, $footer, $spacing, $togglerclass) = @_; # an ID for the modal && that which opens the modal && css class && title && contents && footer contents && spacing for pretty printing HTML output && a class for the toggler (just in case)
5413
if (!$spacing) { $spacing = ""; }
5414
my $debug = 0;
5415
if ($spacing eq "debug") { $debug = 1; $spacing = ""; }
5416
5417
if (not $toggler) { $toggler = "button,show modal $id"; }
5418
if (not $title) { $title = "(no title)"; }
5419
if (not $footer) { $footer = "(no description)"; }
5420
5421
my ($toggleType, $toggleLabel, $toggleSrc, $toggleImgSize, $toggleImgBorders) = split(",", $toggler);
5422
$toggleType = lc($toggleType);
5423
5424
$content =~ s/\"/\\\"/g;
5425
5426
my $output = "";
5427
5428
if ($debug) {
5429
$output .= "debug info" . Html2::br();
5430
$output .= "id: " . $id . Html2::br();
5431
$output .= "toggler: " . $toggler . Html2::br();
5432
$output .= "class: " . $class . Html2::br();
5433
$output .= "title: " . $title . Html2::br();
5434
$output .= "content: " . Html2::italicize("not shown here!") . Html2::br();
5435
$output .= "footer: " . $footer . Html2::br();
5436
$output .= "toggler Class: " . $togglerclass . Html2::br();
5437
$output .= "toggle Type: " . $toggleType . Html2::br();
5438
}
5439
5440
my %script; {
5441
$script{tag} = "script";
5442
$script{innerHTML} = "var $id" . "_modal;\n";
5443
$script{innerHTML} .= "var $id" . "_modal_toggler;\n";
5444
$script{innerHTML} .= "var $id" . "_closer;\n";
5445
$script{innerHTML} .= "\n";
5446
$script{innerHTML} .= "const $id" . "_modalClose = _ => { $id" . "_modal.classList.remove('is-open'); $id" . "_modal.removeEventListener('animationend', $id" . "_modalClose); }\n";
5447
$script{innerHTML} .= "\n";
5448
$script{innerHTML} .= "function initModal$id(m) {\n";
5449
$script{innerHTML} .= " $id" . "_modal = document.getElementById(m + '_modal');\n";
5450
$script{innerHTML} .= " $id" . "_modal_toggler = document.getElementById(m + '_modal_toggler');\n";
5451
$script{innerHTML} .= " $id" . "_closer = document.getElementById(m + '_closer');\n";
5452
$script{innerHTML} .= " $id" . "_modal_toggler.addEventListener('click', e => {\n";
5453
$script{innerHTML} .= " //console.log('open modal ' + m);\n";
5454
$script{innerHTML} .= " e.preventDefault()\n";
5455
$script{innerHTML} .= " showModal$id(m);\n";
5456
$script{innerHTML} .= " document.getElementsByTagName('html')[0].style.overflowY = 'hidden';\n";
5457
$script{innerHTML} .= " });\n";
5458
$script{innerHTML} .= "\n";
5459
$script{innerHTML} .= " $id" . "_closer.addEventListener('keydown', e => {\n";
5460
$script{innerHTML} .= " if (e.keyCode == 27 && $id" . "_modal.classList.contains('is-open')) {\n";
5461
$script{innerHTML} .= " //console.log('esc pressed');\n";
5462
$script{innerHTML} .= " $id" . "_modal.style.animation = 'modalFadeOut 500ms forwards';\n";
5463
$script{innerHTML} .= " $id" . "_modal.addEventListener('animationend', $id" . "_modalClose);\n";
5464
$script{innerHTML} .= " $id" . "_modal.focus();\n";
5465
$script{innerHTML} .= " document.getElementsByTagName('html')[0].style.overflowY = 'scroll';\n";
5466
$script{innerHTML} .= " }\n";
5467
$script{innerHTML} .= " });\n";
5468
5469
$script{innerHTML} .= " $id" . "_closer.addEventListener('click', e => {\n";
5470
$script{innerHTML} .= " //console.log('close modal ' + m);\n";
5471
$script{innerHTML} .= " e.preventDefault()\n";
5472
$script{innerHTML} .= " showModal$id(m);\n";
5473
$script{innerHTML} .= " document.getElementsByTagName('html')[0].style.overflowY = 'scroll';\n";
5474
$script{innerHTML} .= " });\n";
5475
$script{innerHTML} .= "}\n";
5476
$script{innerHTML} .= "\n";
5477
$script{innerHTML} .= "function showModal$id(m) {\n";
5478
$script{innerHTML} .= " $id" . "_modal_toggler = document.getElementById(m + '_modal_toggler');\n";
5479
$script{innerHTML} .= " $id" . "_modal_content = document.getElementById(m + '_modal_content');\n";
5480
$script{innerHTML} .= " $id" . "_modal = document.getElementById(m + '_modal');\n";
5481
$script{innerHTML} .= " $id" . "_closer = document.getElementById(m + '_closer');\n";
5482
$script{innerHTML} .= "\n";
5483
$script{innerHTML} .= " if ($id" . "_modal.classList.contains('is-open')) {\n";
5484
$script{innerHTML} .= " $id" . "_modal.style.animation = 'modalFadeOut 500ms forwards';\n";
5485
$script{innerHTML} .= " $id" . "_modal.addEventListener('animationend', $id" . "_modalClose);\n";
5486
$script{innerHTML} .= " } else { \n";
5487
$script{innerHTML} .= " $id" . "_modal.classList.add('is-open');\n";
5488
$script{innerHTML} .= " $id" . "_modal.style.animation = 'modalFadeIn 500ms forwards';\n";
5489
$script{innerHTML} .= " $id" . "_modal_content.innerHTML = \"$content\";\n";
5490
$script{innerHTML} .= " }\n";
5491
$script{innerHTML} .= "}\n";
5492
5493
$output .= Html2::tag(\%script);
5494
}
5495
5496
5497
my %toggler; {
5498
$toggler{id} = $id . "_modal_toggler";
5499
$toggler{innerHTML} = $toggleLabel;
5500
if ($togglerclass) { $toggler{class} = $togglerclass; }
5501
5502
if ($toggleType eq "link") {
5503
$toggler{tag} = "a";
5504
} elsif ($toggleType eq "button") {
5505
$toggler{tag} = "button";
5506
$toggler{type} = "button";
5507
} elsif ($toggleType eq "img") {
5508
$toggler{tag} = "img";
5509
$toggler{alt} = $toggleLabel;
5510
$toggler{title} = $toggleLabel;
5511
$toggler{src} = $toggleSrc;
5512
if ($toggleSrc !~ /getimage\.pl/ and $toggleSrc !~ /img.\pl/ and $toggleImgSize ne "0") {
5513
$toggler{height} = $toggleImgSize;
5514
}
5515
$toggler{class} .= " enlarge clickable";
5516
delete $toggler{innerHTML};
5517
} else {
5518
$toggler{tag} = $toggleType;
5519
$toggler{class} .= " enlarge clickable";
5520
}
5521
5522
$output .= Html2::tag(\%toggler);
5523
}
5524
5525
{ my %modal;
5526
$modal{tag} = "div";
5527
$modal{id} = $id . "_modal";
5528
$modal{class} = "modal";
5529
if ($class) { $modal{class} .= " $class"; }
5530
$modal{innerHTML} = "";
5531
5532
{ my %closebutton;
5533
$closebutton{tag} = "button";
5534
$closebutton{type} = "button";
5535
$closebutton{innerHTML} = "x";
5536
$closebutton{id} = $id . "_closer";
5537
$closebutton{class} = "modal-close";
5538
5539
$modal{innerHTML} .= Html2::tag(\%closebutton);
5540
}
5541
5542
{ my %modalcontainer;
5543
$modalcontainer{tag} = "div";
5544
$modalcontainer{class} = "modal-container";
5545
$modalcontainer{innerHTML} = "";
5546
5547
{ my %modalheader;
5548
$modalheader{tag} = "div";
5549
$modalheader{class} = "modal-header";
5550
$modalheader{innerHTML} = "";
5551
5552
{ my %title;
5553
$title{tag} = "div";
5554
$title{class} = "subnavbar";
5555
$title{innerHTML} = $title;
5556
5557
$modalheader{innerHTML} .= Html2::tag(\%title);
5558
} # end title
5559
5560
$modalcontainer{innerHTML} .= Html2::tag(\%modalheader);
5561
} # end modal header
5562
5563
{ my %modalcontent;
5564
$modalcontent{tag} = "div";
5565
$modalcontent{id} = $id . "_modal_content";
5566
$modalcontent{class} = "modal-content";
5567
if ($toggleImgBorders) { $modalcontent{class} .= " bordered"; }
5568
5569
$modalcontainer{innerHTML} .= Html2::tag(\%modalcontent);
5570
}
5571
5572
{ my %modalfooter;
5573
$modalfooter{tag} = "div";
5574
$modalfooter{class} = "modal-footer";
5575
$modalfooter{innerHTML} = $footer;
5576
5577
$modalcontainer{innerHTML} .= Html2::tag(\%modalfooter);
5578
}
5579
5580
$modal{innerHTML} .= Html2::tag(\%modalcontainer);
5581
} # end modal inner container
5582
5583
{ my %initjs;
5584
$initjs{tag} = "script";
5585
$initjs{innerHTML} = "initModal$id(\"$id\");";
5586
5587
$modal{innerHTML} .= Html2::tag(\%initjs);
5588
}
5589
5590
$output .= Html2::tag(\%modal);
5591
} # end modal container (dunno why 2 containers, but hey, here we are)
5592
5593
return $output; # a scalar
5594
#usage: my $modal = modal("test_modal", "subnavbar");
5595
}
5596
5597
########################
5598
########################
5599
########################
5600
########################
5601
########################
5602
########################
5603
########################
5604
########################
5605
########################
5606
########################
5607
########################
5608
########################
5609
########################
5610
########################
5611
########################
5612
########################
5613
########################
5614
########################
5615
########################
5616
########################
5617
########################
5618
########################
5619
########################
5620
########################
5621
########################
5622
########################
5623
5624
########################
5625
sub _tests(;$) {
5626
#*
5627
# to test <i>Pm::Html2</i> subroutines
5628
#*
5629
my ($extended) = @_; # to display extended info
5630
my $output = "";
5631
5632
my $width = 800;
5633
my $height = 400;
5634
my $test = "";
5635
my $test2 = "";
5636
my $test3 = "";
5637
my @atest;
5638
my %htest;
5639
5640
$output .= holy_grail({
5641
cdebug=>1,
5642
class=>"normal-panel margins-none padding-none",
5643
style=>"width: " . $width . "px; height: " . $height . "px; max-width: " . $width . "px; max-height: " . $height . "px; min-width: " . $width . "px; min-height: " . $height . "px;",
5644
"grid-header"=>{
5645
html=>"This is the Header for Holy Grail #1",
5646
class=>"rounded red-panel title"
5647
},
5648
"grid-aside-left"=>{
5649
html=>"Aside Left",
5650
class=>"rounded green-panel"
5651
},
5652
"grid-article"=>{
5653
html=>"test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>",
5654
class=>"rounded blue-panel"
5655
},
5656
"grid-aside-right"=>{
5657
html=>"Aside Right",
5658
class=>"rounded purple-panel"
5659
},
5660
"grid-footer"=>{
5661
html=>"This is the Footer",
5662
class=>"rounded cyan-panel"
5663
}
5664
});
5665
5666
$output .= br . hr . br;
5667
5668
$output .= holy_grail_alternate({
5669
cdebug=>1,
5670
class=>"normal-panel margins-none padding-none",
5671
style=>"width: " . $width . "px; height: " . $height . "px; max-width: " . $width . "px; max-height: " . $height . "px; min-width: " . $width . "px; min-height: " . $height . "px;",
5672
"grid-header"=>{
5673
html=>"This is the Header for Holy Grail #2",
5674
class=>"rounded red-panel title"
5675
},
5676
"grid-aside"=>{
5677
html=>"An Aside",
5678
class=>"rounded green-panel"
5679
},
5680
"grid-nav"=>{
5681
html=>"Navigator",
5682
class=>"rounded blue-panel"
5683
},
5684
"grid-article"=>{
5685
html=>"test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>",
5686
class=>"rounded purple-panel"
5687
},
5688
"grid-footer"=>{
5689
html=>"This is the Footer",
5690
class=>"rounded cyan-panel"
5691
}
5692
});
5693
5694
return $output; # a scalar
5695
#usage: print _tests();
5696
#test=(none);
5697
}
5698
5699
1;