# The "skin" configuration exported from
# repository "/home/mario/fossil.d/canonic_autoloader.fossil"
# on 2014-03-17 15:00:05
config /config 7895
1395068394 'css' value '/* General settings for the entire page */
body {
margin: 0ex 1ex;
padding: 0px;
background-color: white;
font-family: sans-serif;
}
/* The project logo in the upper left-hand corner of each page */
#logo {
text-align: center;
vertical-align: bottom;
font-weight: bold;
color: #f90;
padding: 5px 20px 0px 0px;
}
/* The page title centered at the top of each page */
.title {
font-size: 2em;
font-weight: 200;
color: #333;
vertical-align: bottom;
width: 100% ;
position: relative; top:30px;
}
.title b { font-weight: 800; }
/* The login status message in the top right-hand corner */
.status {
display: table-cell;
text-align: right;
vertical-align: bottom;
color: #ff9900;
font-size: 0.8em;
font-weight: bold;
min-width: 200px;
white-space: nowrap;
}
/* The header across the top of the page */
header, .header {
display: table;
width: 100% ;
}
/* The main menu bar that appears at the top of the page beneath
** the header */
nav, .mainmenu {
padding: 5px 10px 5px 10px;
font-size: 0.9em;
font-weight: bold;
text-align: center;
letter-spacing: 1px;
}
/* The submenu bar that *sometimes* appears below the main menu */
.submenu, .sectionmenu {
padding: 3px 10px 3px 0px;
font-size: 0.9em;
text-align: center;
background-color: #ff9900;
color: white;
}
.mainmenu a, .mainmenu a:visited, .submenu a, .submenu a:visited,
.sectionmenu>a.button:link, .sectionmenu>a.button:visited {
padding: 3px 10px 3px 10px;
color: white;
text-decoration: none;
}
.mainmenu a:hover, .submenu a:hover, .sectionmenu>a.button:hover {
color: #ff9900;
background-color: white;
}
/* All page content from the bottom of the menu or submenu down to
** the footer */
.content {
padding: 0ex 1ex 0ex 2ex;
}
/* Some pages have section dividers */
div.section {
margin-bottom: 0px;
margin-top: 1em;
padding: 1px 1px 1px 1px;
font-size: 1.2em;
font-weight: bold;
background-color: #ff9900;
color: white;
white-space: nowrap;
}
/* The "Date" that occurs on the left hand side of timelines */
.divider {
border-radius: 4px;
color: #750;
background: #ffeecc;
border: 2px #f90 solid;
font-size: 1em; font-weight: normal;
padding: .25em;
margin: .2em 0 .2em 0;
float: left;
clear: left;
white-space: nowrap;
}
/* The footer at the very bottom of the page */
footer, .footer {
clear: both;
}
/* verbatim blocks */
pre.verbatim {
background-color: #f5f5f5;
padding: 0.5em;
}
/* The label/value pairs on (for example) the ci page */
table.label-value th {
vertical-align: top;
text-align: right;
padding: 0.2ex 2ex;
}
/* Side-by-side diff */
table.sbsdiff {
background-color: white;
font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
font-size: 8pt;
border-collapse:collapse;
white-space: pre;
width: 98%;
border: 1px #000 dashed;
margin-left: auto;
margin-right: auto;
}
table.sbsdiff th.diffhdr {
border-bottom: dotted;
border-width: 1px;
}
table.sbsdiff tr td {
white-space: pre;
padding-left: 3px;
padding-right: 3px;
margin: 0px;
vertical-align: top;
}
table.sbsdiff tr td.lineno {
text-align: right;
}
table.sbsdiff tr td.srcline {
}
table.sbsdiff tr td.meta {
background-color: rgb(170, 160, 255);
text-align: center;
}
table.sbsdiff tr td.added {
background-color: rgb(180, 250, 180);
}
table.sbsdiff tr td.addedvoid {
background-color: rgb(190, 190, 180);
}
table.sbsdiff tr td.removed {
background-color: rgb(250, 130, 130);
}
table.sbsdiff tr td.removedvoid {
background-color: rgb(190, 190, 180);
}
table.sbsdiff tr td.changed {
background-color: rgb(210, 210, 200);
}
table.sbsdiff tr td.changedvoid {
background-color: rgb(190, 190, 180);
}
/*********************************
*
* SO lookalike.
*
*/
html {
}
body {
padding: 0;
margin: 0;
}
body, p, article, td {
font-family: Arial;
font-size: 14px;
color: #111;
}
p, article {
line-height: 175%;
}
li {
margin-bottom: 5pt;
}
.container {
margin-left: auto;
margin-right: auto;
width: 960px;
clear: both;
}
a {
text-decoration: none;
font-weight: 700;
color: #07c;
}
a:visited {
color: #468;
}
.header {
background: #eee;
color: #999;
padding: 2px 0;
}
#title {
float: left;
color: #666;
font-weight: 700;
margin: 3px;
}
.badge1 { color: #fd5; }
.badge2 { color: #ccc; }
.badge3 { color: #c96; }
#logo {
height: 100px;
}
#navlist {
padding-top: 55px;
}
#navlist a {
color: #ffffff;
font-weight: 900;
font-size: 130%;
padding: 4px 15px;
background: #777;
}
#navlist a.current, #navlist a:hover {
background: #f90;
}
#navlist a.last {
margin-left: 50px;;
}
.mainbar {
width: 730px;
float: left;
padding-top: 10px;
}
.sidebar {
width: 220px;
float: right;
}
.topic {
padding: 22pt 0 15pt 0;
border-bottom: 1px solid #999;
}
.topic tt, .topic kbd {
font-size: 80%;
color: #555;
background: #f7f7f7;
margin-left: 30px;
display: block;
}
.topic-options a, .topic-options {
color: #aaa;
font-weight: 100;
padding-top: 30px;
}
ul.topic-list {
margin-top: 20pt;
padding: 0;
}
#helpbox {
background: #fec;
border-radius: 5px;
padding: 10px;
margin-top: 15px;
color: #750;
}
#helpbox h4 {
color: #a00;
font-size: 140%;
margin: 0;
}
.tag {
padding:2px 5px;
background:#e1e9f1;
color:#58a;
border:1px outset #cde;
}
input[type=submit] {
border: 1px solid black;
padding: 3px 10px;
font: normal normal bold 140% "Trebuchet MS", sans-serif;
}
textarea {
border: 1px solid #999;
border-bottom: 7px solid #ddd;
}
#footer-spacer {
clear: both;
padding-top: 130px;
}
footer {
clear: both;
background: #555;
padding: 65px 275px;
border-top: 9px solid black;
color: #eee;
}
footer p {
color: #999;
}
footer a, footer a:visited {
color: #fec;
}
footer ul {
list-style-type: square;
padding: 0;
}
footer li {
display: inline;
padding-right: 10pt;
}
footer li:before {
color: #f73;
content: "⬛ ";
}
footer li:nth-child(even):before {
color: #fc9;
}
footer li:nth-child(3):before {
color: #57f;
}
footer li:nth-child(4):before {
color: #6f7;
}
#main {
padding-top: 30px;
min-height: 700px;
}
#main .content {
padding-top: 15pt;
padding-bottom: 50pt;
}
h2.subtitle {
font-size: 24px;
font-weight: 700;
display: inline;
border-bottom: 1px solid #999;
}
.submenu {
display: inline;
background: white;
border: 1px #fff solid;
border-bottom: 1px #999 solid;
}
.submenu a, .submenu a:visited {
font-size: 18px;
font-weight: 200;
color: black;
}
.submenu a:hover {
border: 1px #666 solid;
border-bottom: white;
}
code {
background: #eee;
}
pre {
background: #f3f3f3;
padding: 3pt;
}
ul {
margin-top: 3pt;
}
.search-result {
padding: 5pt;
}
.search-result a {
font-size: 140%;
font-weight: 300;
}
.search-link a {
font-weight: 100;
font-size: 60%;
color: #337733;
}
.search-excerpt {
color: #333;
}
.search-headline {
font-size: 300%;
color: #999;
letter-spacing: 5pt;
}
.search-excerpt mark {
text-decoration: none;
font-style: italic;
font-weight: 700;
color: #aa3322;
}
#language-bar {
clear: both;
position: relative;
top: -2px;
width: 100%;
height: 5px;
box-sizing: border-box;
background: #eef;
}
'
config /config 2990
1395068013 'header' value '<html>
<head>
<base href="$baseurl/$current_page" />
<title>$<project_name>: $<title></title>
<link rel=alternate type="application/rss+xml" title=Timeline href="$home/timeline.rss">
<link rel=stylesheet href="$home/style.css?default" type="text/css" media="screen">
<link rel="stylesheet" href="https://google-code-prettify.googlecode.com/svn/loader/prettify.css" type="text/css" media="screen">
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="//google-code-prettify.googlecode.com/svn/loader/run_prettify.js?autoload=true"></script>
</head>
<body onLoad="$(''code,pre'').addClass(''prettyprint'');">
<header class=header>
<div class=container>
<a href="." id="title">𝌔 Fossil Hub</a>
<div style="float:right">
<th1>
if {[info exists login]} {
html "✉ <a href=''$home/login'' class=''profile-link''>$login</a> 751"
html "<span class=badge1>✹</span>1 <span class=badge3>✹</span>5 | "
html "<a href=''$home/login''>Logout</a> | "
} else {
html "<a href=''$home/login''>Login</a> | "
}
if {[hascap s]} {
html "<a href=''$home/setup''>Admin</a> | "
} elseif {[hascap a]} {
html "<a href=''$home/setup_ulist''>Users</a> | "
}
</th1>
<a href="//fossil-scm.org">Fossil manual</a> |
<form id=google style="display:inline" action="wiki">
<input name="name" size="28" value="" placeholder="search wiki+files">
</form>
<!--form id=google style="display:inline" action="http://google.com/search">
<input name="q" size="28" value="" placeholder="search">
<input type="hidden" name="as_sitesearch" value="$baseurl">
</form-->
</div>
</div>
</header>
<div id=language-bar><th1>catch { ui::lang_stats } </th1></div>
<div class="container logo-title-nav">
<a href="$home"><img id=logo src="$home/logo" alt="$<title>" align=left></a>
<span class=title>$<project_name></span>
<nav id=navlist class=mainmenu>
<th1>
if {[anycap jor]} {
html "<a href=''$home/timeline''>Timeline</a>\n"
}
if {[hascap oh]} {
html "<a class=current href=''$home/tree?ci=tip''>Files</a>\n"
}
if {[hascap o]} {
html "<a href=''$home/brlist''>Branches</a>\n"
html "<a href=''$home/taglist''>Tags</a>\n"
}
if {[hascap r]} {
html "<a href=''$home/rptview?rn=1''>Tickets</a>\n"
}
if {[hascap j]} {
html "<a class=last href=''$home/wiki''>Wiki</a>\n"
html "<a href=''$home/timeline?y=w''>RC</a>\n"
}
</th1>
</nav>
</div>
<div class=container id=main>
<h2 class=subtitle><b>$<title></b></h2>
<th1>
catch {ui::search_on_wiki}
</th1>'
config /config 909
1392538406 'footer' value '</div><!--container-->
<footer class=footer>
<p><a href=http://fossil-scm.org/>Fossil</a> version <tt>$release_version</tt> | Manifest: $manifest_version | Manifest date: $manifest_date</p>
<p>
<a href=wiki>wiki</a>
| <a href=login>account</a>
| <a href=setup>admin</a>
| <a href=home>home</a>
</p>
<ul>
<li><a href=tree?ci=tip>Files</a>
<li><a href=zip/trunk.zip?uuid=trunk>ZIP</a>
<li><a href=tarball/trunk.zip?uuid=trunk>TGZ</a>
<li><a href=timeline>Timeline</a>
<li><a href=brlist>Branches</a>
<li><a href=taglist>Tags</a>
<li><a href=reportlist>Tickets</a>
<li><a href=reports>Reports</a>
</ul>
<p>
Skin inspired by <a href=//s.tk/so>Stackoverflow</a>
</p>
</footer>
</body>
</html>
'
config /config 9097
1395068051 'th1-setup' value '
# -------------------------------------------------------------------
# Control structures
#
# ++ varname
# ?: [if] {then} {else}
# isset varname
# eq str1 str2
#
# while {cond} {code}
# foreach varname {list} {code}
# switch value { val1 {code1} val2 {code2} ... }
#
#-- Pre-increment [++ varname]
proc ++ {varname} {
upvar 1 $varname i
return [uplevel 1 "set {$varname} [expr 1+$i]"]
}
#-- ternary / if-shorthand (cond/then/else may be literals, or {[expressions]} themselves)
proc ?: {cond then else} {
uplevel 1 "if {$cond} { return $then; } else { return $else; }"
}
#-- info exists shorthand
proc isset {varname} {
return [uplevel 1 "info exists {$varname}"]
}
#-- string equality shorthand
proc eq {str1 str2} {
return [expr {$str1 eq $str2}]
}
#-- while loop
proc while {condition code} {
return [uplevel 1 "for {} {$condition} {} {$code}"]
}
#-- foreach list
#
# foreach VAR "abc xyz 123" { puts "($VAR) " }
#
proc foreach {varname list code} {
upvar 1 $varname val
for {set i 0} {$i < [llength $list]} {++ i} {
set val [lindex $list $i]
uplevel 1 "$code"
}
}
#-- A switch statement.
#
# switch "val" {
# "cmp1" {code1}
# "cmp2" {code2}
# "cmp3" {code3}
# {{default}} {codeN}
# }
#
proc switch {compare_value val_code_pairs} {
set len [llength $val_code_pairs]
# loop over compare values + code pairs
for {set n 0} {$n < $len} {++ n} {
set cmp [lindex $val_code_pairs $n];
if {[expr $cmp eq $compare_value || $cmp eq {{default}} ]} {
return [uplevel 1 [lindex $val_code_pairs [++ n]]];
}
}
}
# -------------------------------------------------------------------
# String functions
#
# str::contains needle haystack
# str::next needle haystack startindex
# str::wrap haystack needle addbefore addafter
#
#-- returns true if string contained in another string
proc str::contains {needle haystack} {
return [expr {-1 != [string first $needle $haystack]}]
}
#-- wrapper for [string first ...] to support startindex
proc str::next {search content start} {
# cut out $content at $start before searching
set p [string first $search [string range $content $start [string length $content]]]
if [expr $p>=0] {
set p [expr $start+$p]
}
return $p
}
#-- enclose string in e.g. html tags
proc str::wrap {content search before after} {
set len [string length $search]
set p 0
while {[expr [set p [str::next $search $content $p]]>=0]} {
set content "[string range $content 0 [expr $p-1]]$before$search$after[string range $content [expr $p+$len] 2000]";
set p [expr $p+[string length "$before+$search+$after"]]; # skip a little further
}
return $content
}
#-- Split string into list on delimiter character
# (basically just turns delimiter into space)
#
proc str::explode {delim str} {
set r ""
set len [string length $str]
while {-1 != [set p [string first $delim $str]]} {
set r "$r [string range $str 0 [expr $p-1]]"
set str [string range $str [++ p] $len]
}
return [list [string trim "$r $str"]]
}
# -------------------------------------------------------------------
# User Interface utility code
#
# sql::allowed safe_string
# ui::page_exists WikiPage
# ui::file_exists file.name
#
# ui::search terms baseurl
# ui::search_on_wiki
# ui::stats
# ui::lang_stats
#
#-- Whitelist permissible characters for SQL context
# * A workaround for the lack of SQL escaping here (or the new query API branch)
# * Used in LIKE context, so ? and % are not allowed
# * And '' and \ or " not included in the whitelist for obvious reasons.
proc sql::allowed {str} {
return [regexp {^[a-zA-Z0-9 !$&/(){}=<>,.;:-_+#*@]+$} $str]
}
#-- Search function
# * Requires fossil-search.php to build the according table
# (reading from the raw blobs is impossible) as cronjob
# * And a patched `fossil` binary src/report.c to allow
# SELECTs on the `search` table.
proc ui::search {terms baseurl} {
# cleanup $terms
if [sql::allowed $terms] {
# prepare search query
set WHERE "1"
foreach search $terms {
set WHERE "$WHERE AND content LIKE ''%$search%''"
}
# perform search
query "SELECT path, type, name, content FROM fx_search WHERE $WHERE" {
# conent excerpt and highlighting
set p [string first $terms $content]
set excerpt [string range $content [expr $p-50] [expr $p+450]]
foreach search $terms {
set excerpt [str::wrap "$excerpt" $search <mark> </mark>]
}
# format result list
html "
<div class=search-result>
<b>[htmlize $type]</b>
<a href=[htmlize $path]>[htmlize $name]</a> <br>
<span class=search-link><a href=[htmlize $path]>$baseurl/[htmlize $path]</a></span> <br>
<small class=search-excerpt>$excerpt</small>
</div>\n";
}
}
}
#-- Check for existence of wiki page
proc ui::page_exists {name} {
if [sql::allowed $name] {
query "SELECT 1 FROM tag WHERE tagname = ''wiki-$name''" {
return 1
}
}
return 0
}
#-- Check if file exists in repository
proc ui::file_exists {name} {
if [sql::allowed $name] {
query "SELECT 1 FROM filename WHERE name = ''$name''" {
return 1
}
}
return 0
}
#-- Call ui::search on non-existant wiki pages
# * We can get $<title> as search terms
# (no way to access query string parameters otherwise)
# * But this is also convenient, as it doubles as wiki page search
proc ui::search_on_wiki {} {
upvar 1 title title baseurl baseurl current_page current_page
if [expr {[regexp {^wiki[?]name=} $current_page] && ! [ui::page_exists $title]}] {
html "<h2 class=search-headline>Search</h2>";
ui::search $title $baseurl
html "<br><br><br><br>"
}
}
#-- Ordered list of project statistics (will populate global $stats() array)
proc ui::stats {} {
uplevel 1 { query {SELECT
(SELECT count(objid) FROM event WHERE type=''ci'' LIMIT 1) AS `stats_checkins`,
(SELECT count(name) FROM filename LIMIT 1) AS `stats_files`,
(SELECT count(status) FROM ticket LIMIT 1) AS `stats_tickets`,
(SELECT count(DISTINCT user) FROM event LIMIT 1) AS `stats_developers`,
(SELECT count(DISTINCT value) FROM tagxref WHERE tagid=8) AS `stats_branches`,
(SELECT count(tagname) FROM tag WHERE tagname LIKE ''sym-%'') AS `stats_tags`,
(SELECT count(tagname) FROM tag WHERE tagname REGEXP ''^sym[\\-\\w_.]+\\d+\\.\\d+'') AS `stats_releases`
} {} }
}
#-- Language/Content statistics (outputs colored bar graph)
proc ui::lang_stats {} {
# fetch $lang(js/...), $lang_color(js), $lang_list, $total_size
query {SELECT name, value FROM fx_stats ORDER by VALUE DESC} {
set $name $value
}
# output color bar for language proportions
#html "<div class=language-bar style=''width:100%; height:3pt; box-sizing:border-box;''>"
foreach name $lang_list {
set percent "[expr $lang($name)*100]%"
html "<span class=code-rate-$name style=''height:100%; width:$percent; display:inline-block; background-color:#$lang_color($name)'' title=''$percent $name''></span>";
}
#html "</div>";
}
#-- print two table rows for last commit
proc ui::last_commit {} {
query {SELECT *, CAST(julianday(''now'')-mtime AS INT) AS age, substr(comment,0,199) AS msg, substr(uuid, 0, 10) AS short_uuid
FROM event JOIN blob ON blob.rid=event.objid
WHERE type=''ci'' ORDER BY mtime DESC LIMIT 1
} {
html " <tr><th colspan=3>$msg</th></tr>";
html " <tr><th colspan=3 style=background:#fff><a href=''timeline?u=$user'' class=user>$user</a> authored $age days ago
<span style=float:right>last checkin <a href=''ci/$uuid''>$short_uuid</a></span></th></tr>";
}
}
#-- outputs table rows containing top-level filenames and recent checkin comments
proc ui::recent_files {} {
set seen ""
# files
query {
SELECT DISTINCT
instr(name, ''/'') as dir,
(CASE instr(name,''/'') WHEN 0 THEN name
ELSE substr(name,0,instr(name, ''/'')) END) AS name,
substr(comment, 0, 70) AS comment,
uuid,
CAST(julianday(''now'')-mtime AS INT) AS age
FROM filename
JOIN mlink ON filename.fnid=mlink.fnid
JOIN event ON mlink.mid=event.objid
JOIN blob ON blob.rid=event.objid
GROUP BY name ORDER BY dir DESC, mtime DESC
} {
if {$dir && [str::contains $name $seen]} { continue } else { set seen "$name,$seen" }
html " <tr><td>";
if {$dir > 0} {
html "<a class=dir href=''tree?name=[htmlize $name]''><b class=glyph>📂</b> [htmlize $name]</a>";
} else {
html "<a class=file href=''ci/$uuid?sbs=0''><b class=glyph>📄</b> [htmlize $name]</a>";
}
html "</td> <td>[htmlize $comment]</td> <td>[htmlize $age] days ago</td></tr>\n";
}
}'