# Fossil skin configuration "github" for simple `fossil import skin.txt` # 2016-09-04T12-28-29Z # config /config 10652 1472992109 'css' value '/* fonts */ @import url(http://fonts.googleapis.com/css?family=Viga); /* no body spacing */ html, body { border: 0; padding: 0; margin: 0; background: #fefefe; font: normal normal 400 10pt/16pt Arial,sans-serif; max-width: 1600px; } html, * { box-sizing: border-box; } /* general text settings */ body, article, p { color: #333; line-height: 145%; } /* layout features */ .width-container { padding-left: 15% !important; padding-right: 15% !important; } .text-shadow { text-shadow: 0px 1px 0px #888; } .glyph { font-family: "Symbola", "Arial Unicode MS", "Quivira", "Code200", sans-serif; } /* menu layout */ #menu-header { background: #f7f7f7; } #menu-header, #submenu-header, #project-header, #fossil-footer { width: 100%; height: 50pt; padding: 10pt; padding-top: 14pt; border-bottom: 1px solid #dfdfdf; display: block; clear: both; vertical-align: middle; } #menu-header h1 { font-family: Viga; font-weight: 700; display: inline; font-size: 23pt; color: #333; margin: 3pt 18pt 0 0; float: left; } /* search bar */ #search_form { display: inline; } #search_form { display: inline-box; padding: 0; height: 30px; line-height: 30px; } #search_form input, #search_form select { padding: 3px; border: 1px solid #ccc; } #search_form select, #search_form option { background: #eee; } #search_form input:focus, #search_form select:focus { box-shadow: 0 0 5px rgba(81, 203, 238, 1); border: 1px solid rgba(81, 203, 238, 1); } /* menu links */ #menu-header a { color: #444; font-size: 11pt; font-weight: 600; padding: 7pt; } /* buttons */ #menu-header a.button { top: 2pt; border-radius: 5px; font-weight: 700; padding: 6pt 10pt; position: relative; } .button.green { border: 1px solid #493; background: #7e5 linear-gradient(180deg,#8e7,#593); color: #fff !important; } .button.green:hover { background: #5c3 linear-gradient(180deg,#6d5,#482); } .button.white { border: 1px solid #ddd; background: #eee; background: linear-gradient(180deg,#fff,#ddd); color: #333; } .button.white:hover, .submenu .label:hover { background: #ccc linear-gradient(180deg,#eee,#ccc); } .button.red { border: 1px solid #d97; background: #fdc; background: linear-gradient(180deg,#fdc,#eba); color: #333; } .button.red:hover { background: #eba linear-gradient(180deg,#ecb,#c97); } .download.button { display: block; padding: 4pt 10pt; border-radius: 4px; margin-top: 10pt; } .branch.button { font-family: sans-serif; padding: 2pt 6pt; border-radius: 4px; } .branch.button select { border: 0 !important; background: inherit; font-size: 103%; font-weight: 700; -webkit-appearance: none; -moz-appearance: none; appearance: none; } .branch.button option { font-weight: 700; background: #eee; } /* fossil action buttons */ .submenu a.label, .submenu select, .submenu input { padding: 2px 3pt; border-radius: 3px; border: 1px solid #d7d7d7; background: #fff linear-gradient(0deg, #eee 0%, #fff 20%); color: #333; font-size: 12pt; } .submenu input { width: 40px; } .submenu select { padding: 1px 3pt; } .submenu select option { background: linear-gradient(0deg, #ddd 0%, #fff 20%); font-size: 11pt; } main .submenu { float: right; position: relative; top: -40px; } /* links */ a { text-decoration: none; color: #57c; } /* project info */ #project-header a { font-size: 16pt; color: #48c; font-weight: 100; } #public-prefix { font-size: 13pt; font-weight: 100; color: #ccc; position: relative; left: -53pt; top: 5pt; } #project-header .share-button { padding: 3pt 6pt; font-weight: 600; border: 1px solid #ddd; border-radius: 4px 0 0 4px; background: #eee; background:linear-gradient(180deg,#fff,#e7e7e7); color: #222; } #project-header .share-button a { font-size: 12pt; } #project-header .share-button-number { padding: 3pt 6pt; border: 1px solid #ddd; border-left: 0; border-radius: 0 4px 4px 0; background: #fcfcfc; color: #444; } /* project main pane */ #project-content { font-size: 12.25pt; padding-top: 16pt; color: #666; width: 82%; /************** layout *************/ } /* code statistics box */ #project-stats { margin-top: 10pt; border: 1px solid #ccc; border-radius: 4pt; height: 48px; padding: 0; overflow: hidden; background: #fcfcfc; font-family: sans-serif; font-size: 90%; } #project-stats-alternate { height: 34px; text-align: center; overflow: hidden; padding: 1pt 20pt; } #project-stats-alternate div { height: 34px; padding: 8px; } #project-stats-alternate a { padding: 1pt 25pt; color: #999; } #project-stats-alternate a b { color: #111; } /* language color graph */ #language-bar { overflow: hidden; width: 101%; height: 16px; border-radius: 0 0 4pt 4pt; background: #999; padding: 0; margin: 0; } #language-bar span:first-child { border-radius: 0 0 0 8pt; } #language-bar span:last-child { border-radius: 0 0 8pt 0; } /* default user pic in filebox */ .user { font-weight: 700; color: #557; padding-left: 26px; background: url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAHAAcAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAWABYDASIAAhEBAxEB/8QAGQABAQEAAwAAAAAAAAAAAAAAAAYFBAgJ/8QAJhAAAQMEAgEEAwEAAAAAAAAAAQIDBAAFBhESIQcTMTJBM2Fxkf/EABcBAQADAAAAAAAAAAAAAAAAAAUAAQP/xAAnEQABAwMCBAcAAAAAAAAAAAABAgMEABExEiEFBgdBEyIyQlGBwf/aAAwDAQACEQMRAD8A7g5RKy61YvcAw4I819CIsKBwBEl9Z4oSVAnQClAqOx1vuoZrDfOeLXaLkUfIYuTWZKUKucdxlDKwB+RLaQOR0O0lKtk9EH7o8j8v4hbrnGZvGbWZlxO3lCRJbZSOK/iN6IAI13s1Yz8ptljx6TIvN2Zs8EBIdnS1hDUfmdJKle2+RAGj7kf2j+bZU1M+IhD5bCuzarDVqA8wGbgj1X2xk0pwQRnYryikKKck77W7fGDiuRdGp9vW6pMy2ej6iQ27IlcCsKSFHrX0ev2KVHQsrwPIUyZFoya3XKNHU00FiWl0pXwPPfySOwCNH2pW0rpvyhOfXJfaBWskk6sk7n3UEnibiQEhY2ryX8izvIj/AJPyyMZsAOIvEvluS4oAeoeKQSjZAToDf0KzrvnfmmRBj4xdcyfl2+MlJYhuXOQ5HaA2lPFChxTrR1odClKtIBQm4xioytSGgEmwtatTxNkGeTblPSLgwlhLIC0CQpJUsKHE/A9AKV/tKUoqZEZfd1uJuaDnQY8l7xHU3P3+V//Z") no-repeat; } /* sidebar */ #function-sidebar { width: 15%; float: right; /************** layout *************/ overflow: hidden; } #function-sidebar ul { padding: 0; margin: 20pt 0 0 0; } #function-sidebar li { border-left: 1px solid #ececec; box-shadow: #f4f4f4 4px 0 0 0 inset; display: block; border-bottom: 1px solid #fcfcfc; list-style-type: none; margin: 0; } #function-sidebar li a { display: block; white-space: nowrap; padding: 8pt 20pt; } #function-sidebar li.current, #function-sidebar li a[href="current_page"] { border: 0; border-top: 1px solid #eee; border-bottom: 1px solid #eee; border-right: 3px solid #b53; box-shadow: #f7f7f7 0 3px 0 0 inset; } #function-sidebar li:hover { background: linear-gradient(90deg, #bbb 0%, #eee 2%, #f7f7f7 15%, #fff 50%); } #function-sidebar li a b { color: #333; font-weight: 500; } /* file list box */ #recent-files { border: 1px solid #edf; border-radius: 3pt; box-shadow: #f0f4f8 0 0 6px 4px; padding: 0; } #recent-files table { background: #f7f8f9; width: 100%; margin: 0; padding: 0; border-spacing: 0; border-collapse: collapse; } #recent-files tr { width: 100%; border-bottom: 1px solid #e4e8ea; font-size: 89%; padding: 0 !important; margin: 0 !important; } #recent-files th { padding: 5pt; text-align: left; font-weight: 300; background: #e0e7f7; border-bottom: 1px solid #f3f5f7; } #recent-files td { padding: 5pt; text-align: left; font-size: 89%; } #recent-files a b { font-weight: 600; font-size: 115%; } #recent-files a.dir b { color: #33c; } #recent-files a.file b { color: #dcb; } /* actual fossil content (wiki/timeline/files/etc.) */ main { display: block; min-height: 500pt; border: 1px solid #eee; box-shadow: #f7f7f7 0 0 5px 3px; } main h2.page-title { display: block; margin: 0; padding: 9pt; background: #eee; background: linear-gradient(180deg, #fff 0%, #fcfcfc 33%, #f7f7f7 75%, #ebebeb 100%); } /* end */ #fossil-footer { margin-top: 30pt; border-top: 1px solid #eee; height: 175pt; } #fossil-footer .button { padding: 3pt 6pt; border-radius: 2pt; } /* have different layout on frontpage than elsewhere */ .optional { display: none; } body.page-index .optional { display: inherit; } body.page-index span.optional { display: inline; } #function-sidebar { width: 6%; } body.page-index #function-sidebar { width: 15%; } #project-content { width: 91%; } body.page-index #project-content { width: 82%; } /* ui::search decoration */ .searchResult li { } .searchResult li a { font-size: 120%; font-weight: 300; } .searchResult li a.search-link { display: block; font-weight: 100; font-size: 70%; color: #337733; line-height: 125%; } .searchResult li .snippet { color: #333; } .searchResult li .snippet mark { text-decoration: none; font-style: italic; font-weight: 700; color: #aa3322; background: #f7f3cc; } /* actual HTML content decoration */ kbd { border: 1px dotted #bbb; border-radius: 3px; padding: 1px 3px; background: #eee linear-gradient(#fafcff,#e7e9ec); } code { background: #f5f6f7; } pre.prettyprint { border: 1px dashed #eee !important; background: #f7f7f7; } main table { border: 1px solid #edf; box-shadow: #f0f4f8 0 0 6px 4px; background: #ddd linear-gradient(180deg,#fff,#f7f7f7); } main table th { background: #eee linear-gradient(180deg,#fff,#ddd); border-collapse: collapse; padding: 5pt; text-align: left; } main table tr { border: 1px solid #f7f7f7; } #timelineTable tr { border: 0; } main table td { padding: 3pt; } main code, main pre { /*font-family: "Source Sans Pro", sans-serif; font-size: 110%;*/ } main h2, main h3, main h4, main h5, main h6 { margin-top: 22pt; } main li { margin-top: 5.5pt; } main em { color: #522; } main .content p, main .content tr, main .content li { line-height: 155%; } main td.tktDspValue { background: #f3f3f3 linear-gradient(45deg,#ebebf5,#fff); } main td.tktDspLabel { background: linear-gradient(115deg,#fff 70%,#fff9f7 100%); text-align: right; font-size: 90%; color: #666; } /* ------------------------ Fossil internal styles ------------------------ */ ' config /config 8232 1528091111 'header' value ' #-- Determine current page type set pagename "" if {[regexp {^(index|home)[?]?} $current_page]} { set pagecat "index" } else { if {[regexp {^wiki\?name=} $current_page]} { set pagecat "wiki" set pagename [string range $current_page 10 2048] } else { set pagecat $current_page } } #-- For outputting class=current in #sidebar proc current {name} { upvar 1 pagecat pagecat if [regexp "^($name)" $pagecat] { puts { class=current} } } #-- Split domain name from baseurl set basedomain [string range $baseurl [expr 7+[expr {[string range $baseurl 4 4] eq "s"}]] [expr 7+[string first "/" [string range "$baseurl/" 8 50]]]] #-- Project stats set stats_description [setting project-description] set stats_social 0 set stats_forks 1 catch { query { SELECT name,value FROM fx_stats WHERE name GLOB ''stats_*'' } { set "$name" "$value" } } #-- end setup $<project_name>: $<title>
$stats_description
if {$pagecat eq "index"} { set stats_checkins [set stats_branches [set stats_releases [set stats_developers 0]]] ; catch { ui::stats } html " " html {
} catch { ui::lang_stats } html {
} }

โŒˆโŒ‹ โŽ‡ branch:  $project_name

catch { if {$pagecat eq "index" || [string length $pagename] && [sql::dir_exists $pagename]} { html {
} ui::last_commit ; ui::recent_files [?: {$pagecat eq "index"} "" "$pagename"] html {
} } }

$title

' config /config 1998 1472992109 'footer' value '


' config /config 41 1394330578 'timeline-plaintext' value '1' config /config 11144 1472992109 'th1-setup' value ' #-- 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 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]]]; } } } #-- 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[-a-z0-9_.]+\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 "
" foreach name $lang_list { set percent "[expr $lang($name)*100]%" html ""; } #html "
"; } #-- 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 " $msg"; html " $user authored $age days ago last checkin $short_uuid "; } } #-- outputs table rows containing top-level filenames and recent checkin comments proc ui::recent_files {dirname} { set seen "(.gitignore)" # search files using directory as base path set branch "trunk" set cutname 0 set AND_DIR "" if {[string length $dirname]} { set dirname "$dirname/" set cutname [string length $dirname] set AND_DIR " AND substr(name, 0, \$cutname+1) = \$dirname " } # files // vcache.rid=mlink.fid would be easier to skip deleted files, but access is prohibited(?) query " SELECT DISTINCT m.fnid, INSTR(SUBSTR(name,\$cutname+1),''/'')>0 AS is_dir, name AS pathname, bf.rid AS fn_rid, bf.uuid AS fn_uuid, bm.rid AS ci_rid, bm.uuid AS ci_uuid, SUBSTR(comment, 0, 70) AS comment, CAST(JULIANDAY(''now'')-e.mtime AS INT) AS age FROM filename LEFT JOIN mlink m ON m.fnid = filename.fnid LEFT JOIN tagxref ON m.mid = tagxref.rid LEFT JOIN blob bf ON bf.rid = m.fid LEFT JOIN blob bm ON bm.rid = m.mid LEFT JOIN event e ON e.objid = m.mid WHERE --tagxref.value = \$branch AND m.fnid NOT IN (SELECT fnid FROM mlink m LEFT JOIN tagxref x ON m.mid=x.rid WHERE fid=0 AND x.value=\$branch) $AND_DIR GROUP BY name ORDER BY is_dir DESC, name ASC, e.mtime DESC " { # separate directories and files set name [string range $pathname $cutname 2048] set dir [string first "/" $name] if {$dir>0} { set name [string range $name 0 [expr $dir-1]] } # skip seen files if [str::contains "($name)" $seen] { continue } else { set seen "($name),$seen" } # output table entries html " "; if {$dir>0} { # if there is an equivalent wiki page for a directory, then we mix filebox + wiki set display "wiki" #set display [?: [sql::page_exists "$dirname$name"] "wiki" "tree"] html "๐Ÿ“‚ [htmlize $name]"; } else { html "๐Ÿ“„ [htmlize $name]"; } html " [htmlize $comment]โ€นโ€บ [htmlize $age] days ago\n"; } } #-- social media share links proc ui::social_links {baseurl} { html " g+ · fb · tw · rd · in · su · dl "; } # Outputs a textual /changelog proc webpage_changelog {} { html "
\n\n";
  set version "trunk"
  puts "$version (unreleased)\n";
  query {
     SELECT event.mtime, tag.tagname, MAX(tag.tagid), DATE(event.mtime) AS d,
            REPLACE(TRIM(REPLACE(event.comment, char(10,10), char(10)), char(8,10,13,32)), char(10), char(10,32,32,32)) AS comment
     FROM event
      LEFT JOIN tagxref ON event.objid=tagxref.rid
      LEFT JOIN tag ON tagxref.tagid=tag.tagid
     WHERE type=''ci''
     GROUP BY objid
     ORDER BY event.mtime DESC
     LIMIT 750
  } {
     if {[regexp {^sym-.*\d+\.\d+} $tagname]} {
        for {} {[string length $tagname] >= 3 && [regexp {^\d+\.} $tagname] == 0} {} {
          set tagname [string range $tagname 1 100]
        }
        puts "\n$tagname ($d)\n";
     }
     puts " * $comment\n";
  }
  puts "\n\n";
}

# Alternative to /raw trunk file access without ?name=uuid,
# Doesn''t work with CONTENT() yet.
proc webpage_cat {} {
  set name [getParameter name ""]
  if {![string length $name]} { puts "No filename given."; break; }
  query {
     SELECT uuid
     FROM blob LEFT JOIN mlink ON blob.rid=mlink.fid
               LEFT JOIN filename ON mlink.fnid=filename.fnid
     WHERE name = $name
     ORDER BY rid DESC LIMIT 1
  } { html [artifact "$uuid"]; }
}

# Generate a text/uri-list for available files
proc webpage_uri-list {} {
  query {
     SELECT filename.name, uuid
     FROM blob LEFT JOIN mlink ON blob.rid=mlink.fid LEFT JOIN filename ON mlink.fnid=filename.fnid
     GROUP BY filename.name  ORDER BY rid DESC
  } { puts "$name?name=$uuid\n" }
}

# Invokes web request page procs
proc webpage_hook {} {
  #if {! [anycap ro]} { break }
  catch { "webpage_$::web_name"; return -code 2 found; } rc
  if {"$rc" eq "found"} { break continue }
}


#-- Whitelist for SQL params
# Just realized this is redundant; because query {} accepts
# uninterpolated \$varnames as parameter placeholders.
proc sql::allowed {str} {
   return [regexp {^[-a-zA-Z0-9 !$&/(){}=<>,.;:_+#*@]+$} $str]
}
#-- Also prohibit regex special chars
proc sql::allowed_regexp {str} {
   return [regexp {^[-a-zA-Z0-9 !$&/    =<>,.;:_ # @]+$} $str]
}


#-- Check for existence of wiki page
proc sql::page_exists {name} {
   query {SELECT 1 FROM tag WHERE tagname = (''wiki-'' || $name)} { return 1 }
   return 0
}


#-- Check if exact file name (including path) exists in repository
proc sql::file_exists {name} {
   query {SELECT 1 FROM filename WHERE name = $name} { return 1 }
   return 0
}


#-- Find file by basename
proc sql::find_file {path} {
   if {![sql::allowed_regexp $path]} { return 0 }
   query {SELECT name FROM filename WHERE name REGEXP (''(^|/)'' || $path || ''\$'')} { return $name }
   return ""
}


#-- Check if directory exists
proc sql::dir_exists {path} {
   if {![sql::allowed_regexp $path]} { return 0 }
   query {SELECT name FROM filename WHERE name REGEXP (''^'' || $path || ''/.+'')} { return 1 }
   return 0
}

   
#-- 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"]]
}   

#-- Extract dirname from path/file/name
proc str::dirname {path} {
   return [string range $path 0 [expr [string last "/" $path]-1]]
}

'