Pm/Html2.pm
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>&quot;</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;