Fossil

Check-in [064c1c95]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Reworked how style.css is emitted: all default rules first, then page-specific rules (if passed name=PAGENAME and if builtin file style.PAGENAME.css exists), then the skin. This increases the default style.css output by only approx. 800 bytes before compression (approx. 300 after compression). Has no visual impact on existing built-in skins, and none is expected on 3rd-party skin edits. See /forumpost/d63ff33063 for discussion.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 064c1c95889feef29f83dba07735ed8a3ec8938658ac3d3e76656d6d36729eba
User & Date: stephan 2020-05-28 09:18:56
Original Comment: Reworked how style.css is emitted: all default rules first, then page-specific rules (if passed name=PAGENAME and if builtin file style.PAGENAME.css exists), then the skin. This increases the default style.css output by only approx. 800 bytes before compression (approx. 300 after compression). Has no visual impact on existing built-in skins, and none is expected on 3rd-party skin edits. See /forumpost/c659c44685 for discussion.
Context
2020-05-28
09:40
Introducing the /fileedit page. ... (check-in: 1243bf39 user: stephan tags: trunk)
09:18
Reworked how style.css is emitted: all default rules first, then page-specific rules (if passed name=PAGENAME and if builtin file style.PAGENAME.css exists), then the skin. This increases the default style.css output by only approx. 800 bytes before compression (approx. 300 after compression). Has no visual impact on existing built-in skins, and none is expected on 3rd-party skin edits. See /forumpost/d63ff33063 for discussion. ... (check-in: 064c1c95 user: stephan tags: trunk)
08:55
Editing forum posts now applies delta compression to the edits. ... (check-in: 5b7888c9 user: stephan tags: trunk)
2020-05-26
06:47
merged in trunk. ... (Closed-Leaf check-in: fe018289 user: stephan tags: style-css-revamp)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to skins/bootstrap/header.txt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html lang="en">
<head>
  <meta charset="utf-8">
  <base href="$baseurl/$current_page" />
  <title>$<project_name>: $<title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Content-Security-Policy" content="$default_csp"/>
    <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
    <link rel="stylesheet" href="$home/style.css?default" type="text/css" media="screen" />
    <script nonce="$<nonce>">
    function gebi(x){
      if(/^#/.test(x)) x = x.substr(1);
      var e = document.getElementById(x);
      if(!e) throw new Error("Expecting element with ID "+x);
      else return e;
    }








|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html lang="en">
<head>
  <meta charset="utf-8">
  <base href="$baseurl/$current_page" />
  <title>$<project_name>: $<title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Content-Security-Policy" content="$default_csp"/>
    <link rel="alternate" type="application/rss+xml" title="RSS Feed" href="$home/timeline.rss" />
    <link rel="stylesheet" href="$stylesheet_url" type="text/css" media="screen" />
    <script nonce="$<nonce>">
    function gebi(x){
      if(/^#/.test(x)) x = x.substr(1);
      var e = document.getElementById(x);
      if(!e) throw new Error("Expecting element with ID "+x);
      else return e;
    }

Changes to src/main.mk.

241
242
243
244
245
246
247

248
249
250
251
252
253
254
  $(SRCDIR)/sounds/9.wav \
  $(SRCDIR)/sounds/a.wav \
  $(SRCDIR)/sounds/b.wav \
  $(SRCDIR)/sounds/c.wav \
  $(SRCDIR)/sounds/d.wav \
  $(SRCDIR)/sounds/e.wav \
  $(SRCDIR)/sounds/f.wav \

  $(SRCDIR)/tree.js \
  $(SRCDIR)/useredit.js \
  $(SRCDIR)/wiki.wiki

TRANS_SRC = \
  $(OBJDIR)/add_.c \
  $(OBJDIR)/alerts_.c \







>







241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
  $(SRCDIR)/sounds/9.wav \
  $(SRCDIR)/sounds/a.wav \
  $(SRCDIR)/sounds/b.wav \
  $(SRCDIR)/sounds/c.wav \
  $(SRCDIR)/sounds/d.wav \
  $(SRCDIR)/sounds/e.wav \
  $(SRCDIR)/sounds/f.wav \
  $(SRCDIR)/style.admin_log.css \
  $(SRCDIR)/tree.js \
  $(SRCDIR)/useredit.js \
  $(SRCDIR)/wiki.wiki

TRANS_SRC = \
  $(OBJDIR)/add_.c \
  $(OBJDIR)/alerts_.c \

Changes to src/makemake.tcl.

173
174
175
176
177
178
179

180
181
182
183
184
185
186
# Additional resource files that get built into the executable.
#
set extra_files {
  diff.tcl
  markdown.md
  wiki.wiki
  *.js

  ../skins/*/*.txt
  sounds/*.wav
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {







>







173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Additional resource files that get built into the executable.
#
set extra_files {
  diff.tcl
  markdown.md
  wiki.wiki
  *.js
  style.*.css
  ../skins/*/*.txt
  sounds/*.wav
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {

Changes to src/skins.c.

706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
/*
** Emits the list of built-in default CSS selectors. Intended
** for use only from the /setup_skinedit page.
*/
static void skin_emit_css_defaults(){
  struct strctCssDefaults const * pCss;
  fossil_print("<h1>CSS Defaults</h1>");
  fossil_print("If a skin defines any of the following CSS selectors, "
               "that definition replaces the default, as opposed to "
               "cascading from it. ");
  fossil_print("See <a href=\"https://fossil-scm.org/fossil/"
               "doc/trunk/www/css-tricks.md\">this "
               "document</a> for more details.");
  /* To discuss: do we want to list only the default selectors or
  ** also their default values? The latter increases the size of the
  ** page considerably, but is arguably more useful. We could, of
  ** course, offer a URL param to toggle the view, but that currently







|
<
<







706
707
708
709
710
711
712
713


714
715
716
717
718
719
720
/*
** Emits the list of built-in default CSS selectors. Intended
** for use only from the /setup_skinedit page.
*/
static void skin_emit_css_defaults(){
  struct strctCssDefaults const * pCss;
  fossil_print("<h1>CSS Defaults</h1>");
  fossil_print("Fossil's list of its own CSS classes follows. ");


  fossil_print("See <a href=\"https://fossil-scm.org/fossil/"
               "doc/trunk/www/css-tricks.md\">this "
               "document</a> for more details.");
  /* To discuss: do we want to list only the default selectors or
  ** also their default values? The latter increases the size of the
  ** page considerably, but is arguably more useful. We could, of
  ** course, offer a URL param to toggle the view, but that currently

Added src/style.admin_log.css.













>
>
>
>
>
>
1
2
3
4
5
6
/* This file is just to demonstrate/test page-specific CSS. Using
   the browser dev tools, select any link, "inspect" it, and edit
   this style. */
a{
  font-size: inherit;
}

Changes to src/style.c.

338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

368
369
370
371
372
373
374













375

376
377
378
379
380
381
382
383
384
385
386
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)b;
  return fossil_strcmp(A->zLabel, B->zLabel);
}

/* Use this for the $current_page variable if it is not NULL.  If it is
** NULL then use g.zPath.
*/
static char *local_zCurrentPage = 0;

/*
** Set the desired $current_page to something other than g.zPath
*/
void style_set_current_page(const char *zFormat, ...){
  fossil_free(local_zCurrentPage);
  if( zFormat==0 ){
    local_zCurrentPage = 0;
  }else{
    va_list ap;
    va_start(ap, zFormat);
    local_zCurrentPage = vmprintf(zFormat, ap);
    va_end(ap);
  }
}

/*
** Create a TH1 variable containing the URL for the specified config resource.
** The resulting variable name will be of the form $[zVarPrefix]_url.

*/
static void url_var(
  const char *zVarPrefix,
  const char *zConfigName,
  const char *zPageName
){
  char *zVarName = mprintf("%s_url", zVarPrefix);













  char *zUrl = mprintf("%R/%s?id=%x", zPageName,

                       skin_id(zConfigName));
  Th_Store(zVarName, zUrl);
  free(zUrl);
  free(zVarName);
}

/*
** Create a TH1 variable containing the URL for the specified config image.
** The resulting variable name will be of the form $[zImageName]_image_url.
*/
static void image_url_var(const char *zImageName){







|
|



















|
|
>







>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
|

|
|







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
*/
static int submenuCompare(const void *a, const void *b){
  const struct Submenu *A = (const struct Submenu*)a;
  const struct Submenu *B = (const struct Submenu*)b;
  return fossil_strcmp(A->zLabel, B->zLabel);
}

/* Use this for the $current_page variable if it is not NULL.  If it
** is NULL then use g.zPath.
*/
static char *local_zCurrentPage = 0;

/*
** Set the desired $current_page to something other than g.zPath
*/
void style_set_current_page(const char *zFormat, ...){
  fossil_free(local_zCurrentPage);
  if( zFormat==0 ){
    local_zCurrentPage = 0;
  }else{
    va_list ap;
    va_start(ap, zFormat);
    local_zCurrentPage = vmprintf(zFormat, ap);
    va_end(ap);
  }
}

/*
** Create a TH1 variable containing the URL for the specified config
** resource. The resulting variable name will be of the form
** $[zVarPrefix]_url.
*/
static void url_var(
  const char *zVarPrefix,
  const char *zConfigName,
  const char *zPageName
){
  char *zVarName = mprintf("%s_url", zVarPrefix);
  char *zUrl = 0;              /* stylesheet URL */
  int hasBuiltin = 0;          /* true for built-in page-specific CSS */

  if(0==strcmp("css",zConfigName)){
    /* Account for page-specific CSS, appending a /{{g.zPath}} to the
    ** url only if we have a corresponding built-in page-specific CSS
    ** file. Do not append it to all pages because we would
    ** effectively cache-bust all pages which do not have
    ** page-specific CSS. */
    char * zBuiltin = mprintf("style.%s.css", g.zPath);
    hasBuiltin = builtin_file(zBuiltin,0)!=0;
    fossil_free(zBuiltin);
  }
  zUrl = mprintf("%R/%s%s%s?id=%x", zPageName,
                 hasBuiltin ? "/" : "", hasBuiltin ? g.zPath : "",
                 skin_id(zConfigName));
  Th_Store(zVarName, zUrl);
  fossil_free(zUrl);
  fossil_free(zVarName);
}

/*
** Create a TH1 variable containing the URL for the specified config image.
** The resulting variable name will be of the form $[zImageName]_image_url.
*/
static void image_url_var(const char *zImageName){
1022
1023
1024
1025
1026
1027
1028





1029
1030
1031
1032
1033
1034
1035

/*
** COMMAND: test-contains-selector
**
** Usage: %fossil test-contains-selector FILENAME SELECTOR
**
** Determine if the CSS stylesheet FILENAME contains SELECTOR.





*/
void contains_selector_cmd(void){
  int found;
  char *zSelector;
  Blob css;
  if( g.argc!=4 ) usage("FILENAME SELECTOR");
  blob_read_from_file(&css, g.argv[2], ExtFILE);







>
>
>
>
>







1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055

/*
** COMMAND: test-contains-selector
**
** Usage: %fossil test-contains-selector FILENAME SELECTOR
**
** Determine if the CSS stylesheet FILENAME contains SELECTOR.
**
** Note that as of 2020-05-28, the default rules are always emitted,
** so the containsSelector() logic is no longer applied when emitting
** style.css. It is unclear whether this test command is now obsolete
** or whether it may still serve a purpose.
*/
void contains_selector_cmd(void){
  int found;
  char *zSelector;
  Blob css;
  if( g.argc!=4 ) usage("FILENAME SELECTOR");
  blob_read_from_file(&css, g.argv[2], ExtFILE);
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080

1081
1082
1083
1084
1085

1086
1087
1088
1089





1090





1091



1092
1093

1094






1095
1096
1097
1098
1099
1100
1101
    /* Default behavior is to return javascript */
    cgi_set_content_type("application/javascript");
  }
  style_init_th1_vars(0);
  Th_Render(zScript?zScript:"");
}


/*
** WEBPAGE: style.css
**
** Return the style sheet.
*/
void page_style_css(void){
  Blob css;
  int i;
  int isInit = 0;

  cgi_set_content_type("text/css");
  blob_init(&css,skin_get("css"),-1);

  /* add special missing definitions */
  for(i=1; cssDefaultList[i].elementClass; i++){
    char *z = blob_str(&css);
    if( !containsSelector(z, cssDefaultList[i].elementClass) ){
      if( !isInit ){
        isInit = 1;

        blob_append(&css,
          "\n/***********************************************************\n"
          "** All CSS above is supplied by the repository \"skin\".\n"
          "** That which follows is generated automatically by Fossil\n"
          "** to fill in needed selectors that are missing from the\n"

          "** \"skin\" CSS.\n"
          "***********************************************************/\n",
          -1);
      }





      blob_appendf(&css, "%s {\n%s}\n",





          cssDefaultList[i].elementClass,



          cssDefaultList[i].value);
    }

  }







  /* Process through TH1 in order to give an opportunity to substitute
  ** variables such as $baseurl.
  */
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
  Th_Store("home", g.zTop);







<






|

|


<
|
<

|
|
<
|
>
|
|
<
|
<
>
|
|
|
<
>
>
>
>
>
|
>
>
>
>
>
|
>
>
>
|

>

>
>
>
>
>
>







1074
1075
1076
1077
1078
1079
1080

1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091

1092

1093
1094
1095

1096
1097
1098
1099

1100

1101
1102
1103
1104

1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
    /* Default behavior is to return javascript */
    cgi_set_content_type("application/javascript");
  }
  style_init_th1_vars(0);
  Th_Render(zScript?zScript:"");
}


/*
** WEBPAGE: style.css
**
** Return the style sheet.
*/
void page_style_css(void){
  Blob css = empty_blob;
  int i;
  const char *zPage = PD("name",P("page"));

  cgi_set_content_type("text/css");

  /* Emit all default rules... */

  for(i=1; cssDefaultList[i].elementClass; i++){
    blob_appendf(&css, "%s {\n%s}\n",
                 cssDefaultList[i].elementClass,

                 cssDefaultList[i].value);
  }
  blob_append(&css,
    "\n/***********************************************************\n"

    "** All CSS above is generated automatically by Fossil to\n"

    "** provide default rule implementations which the \"skin\"\n"
    "** may cascade.\n"
    "***********************************************************/\n",
    -1);

  if(zPage!=0 && zPage[0]!=0){
    char * zFile = mprintf("style.%s.css", zPage);
    int nFile = 0;
    const char *zBuiltin = (const char *)builtin_file(zFile, &nFile);
    if(nFile>0){
      blob_appendf(&css,
        "\n/***********************************************************\n"
        "** Start of page-specific CSS for page %s...\n"
        "***********************************************************/\n",
        zPage);
      blob_append(&css, zBuiltin, nFile);
      blob_appendf(&css,
        "\n/***********************************************************\n"
        "** End of page-specific CSS for page %s.\n"
        "***********************************************************/\n",
        zPage);
    }
    fossil_free(zFile);
  }
  blob_append(&css,
     "\n/***********************************************************\n"
     "** All CSS which follows is supplied by the repository \"skin\".\n"
     "***********************************************************/\n",
     -1);
  blob_append(&css,skin_get("css"),-1);

  /* Process through TH1 in order to give an opportunity to substitute
  ** variables such as $baseurl.
  */
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
  Th_Store("home", g.zTop);

Changes to src/url.c.

47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
*/
struct UrlData {
  int isFile;      /* True if a "file:" url */
  int isHttps;     /* True if a "https:" url */
  int isSsh;       /* True if an "ssh:" url */
  char *name;      /* Hostname for http: or filename for file: */
  char *hostname;  /* The HOST: parameter on http headers */
  char *protocol;  /* "http" or "https" */
  int port;        /* TCP port number for http: or https: */
  int dfltPort;    /* The default port for the given protocol */
  char *path;      /* Pathname for http: */
  char *user;      /* User id for http: */
  char *passwd;    /* Password for http: */
  char *canonical; /* Canonical representation of the URL */
  char *proxyAuth; /* Proxy-Authorizer: string */







|







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
*/
struct UrlData {
  int isFile;      /* True if a "file:" url */
  int isHttps;     /* True if a "https:" url */
  int isSsh;       /* True if an "ssh:" url */
  char *name;      /* Hostname for http: or filename for file: */
  char *hostname;  /* The HOST: parameter on http headers */
  const char *protocol; /* "http" or "https" or "ssh" */
  int port;        /* TCP port number for http: or https: */
  int dfltPort;    /* The default port for the given protocol */
  char *path;      /* Pathname for http: */
  char *user;      /* User id for http: */
  char *passwd;    /* Password for http: */
  char *canonical; /* Canonical representation of the URL */
  char *proxyAuth; /* Proxy-Authorizer: string */

Changes to win/Makefile.mingw.

663
664
665
666
667
668
669

670
671
672
673
674
675
676
  $(SRCDIR)/sounds/9.wav \
  $(SRCDIR)/sounds/a.wav \
  $(SRCDIR)/sounds/b.wav \
  $(SRCDIR)/sounds/c.wav \
  $(SRCDIR)/sounds/d.wav \
  $(SRCDIR)/sounds/e.wav \
  $(SRCDIR)/sounds/f.wav \

  $(SRCDIR)/tree.js \
  $(SRCDIR)/useredit.js \
  $(SRCDIR)/wiki.wiki

TRANS_SRC = \
  $(OBJDIR)/add_.c \
  $(OBJDIR)/alerts_.c \







>







663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
  $(SRCDIR)/sounds/9.wav \
  $(SRCDIR)/sounds/a.wav \
  $(SRCDIR)/sounds/b.wav \
  $(SRCDIR)/sounds/c.wav \
  $(SRCDIR)/sounds/d.wav \
  $(SRCDIR)/sounds/e.wav \
  $(SRCDIR)/sounds/f.wav \
  $(SRCDIR)/style.admin_log.css \
  $(SRCDIR)/tree.js \
  $(SRCDIR)/useredit.js \
  $(SRCDIR)/wiki.wiki

TRANS_SRC = \
  $(OBJDIR)/add_.c \
  $(OBJDIR)/alerts_.c \

Changes to win/Makefile.msc.

570
571
572
573
574
575
576

577
578
579
580
581
582
583
        $(SRCDIR)\sounds\9.wav \
        $(SRCDIR)\sounds\a.wav \
        $(SRCDIR)\sounds\b.wav \
        $(SRCDIR)\sounds\c.wav \
        $(SRCDIR)\sounds\d.wav \
        $(SRCDIR)\sounds\e.wav \
        $(SRCDIR)\sounds\f.wav \

        $(SRCDIR)\tree.js \
        $(SRCDIR)\useredit.js \
        $(SRCDIR)\wiki.wiki

OBJ   = $(OX)\add$O \
        $(OX)\alerts$O \
        $(OX)\allrepo$O \







>







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
        $(SRCDIR)\sounds\9.wav \
        $(SRCDIR)\sounds\a.wav \
        $(SRCDIR)\sounds\b.wav \
        $(SRCDIR)\sounds\c.wav \
        $(SRCDIR)\sounds\d.wav \
        $(SRCDIR)\sounds\e.wav \
        $(SRCDIR)\sounds\f.wav \
        $(SRCDIR)\style.admin_log.css \
        $(SRCDIR)\tree.js \
        $(SRCDIR)\useredit.js \
        $(SRCDIR)\wiki.wiki

OBJ   = $(OX)\add$O \
        $(OX)\alerts$O \
        $(OX)\allrepo$O \

Changes to www/css-tricks.md.

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
This is a "living document" - please feel free to suggest
additions via [the Fossil forum](https://fossil-scm.org/forum/).

This document is *not* an introduction to CSS - the web is
full of tutorials on that topic. It covers only the specifics
of customizing certain CSS-based behaviors in a Fossil UI. That said...

# Overriding Default Rules

One behavior of the skinning system works considerably differently
from the cascading nature of CSS: if a skin applies a CSS selector for
which Fossil has a built-in default value, Fossil elides the entire
default definition for that rule. i.e., the skin's definition is the
only one which is applied, rather than cascading the definition from
the default value.

For example, if Fossil has a default CSS rule which looks like:

```css
div.foo {
  font-size: 120%;
  margin-left: 1em;
}
```

And a skin has:

```css
div.foo {}
```

Then Fossil will *not* emit its default rule and the user's copy will
become the only definition of that CSS rule. This is different from
normal CSS cascading rules, in which the above sequence would result
in, effectively, the top set of rules being applied because the second
(empty) one does not override anything from the first.

If a skin applies a given selector more than once, or imports external
style sheets which do, those cascade following CSS's normal rules.

## Is it Really `!important`?

By and large, CSS's `!important` qualifier is not needed when
customzing Fossil's CSS. On occasion, however, particular styles may
be set directly on DOM elements when Fossil generates its HTML, and
such cases require the use of `!important` to override them.








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







8
9
10
11
12
13
14

































15
16
17
18
19
20
21
This is a "living document" - please feel free to suggest
additions via [the Fossil forum](https://fossil-scm.org/forum/).

This document is *not* an introduction to CSS - the web is
full of tutorials on that topic. It covers only the specifics
of customizing certain CSS-based behaviors in a Fossil UI. That said...


































## Is it Really `!important`?

By and large, CSS's `!important` qualifier is not needed when
customzing Fossil's CSS. On occasion, however, particular styles may
be set directly on DOM elements when Fossil generates its HTML, and
such cases require the use of `!important` to override them.