| 
					
				 | 
			
			
				@@ -3,17 +3,24 @@ package staticmd 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"bufio" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"html/template" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	"io" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"io/ioutil" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"os" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"path/filepath" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"runtime" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"strings" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"sync" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	"github.com/russross/blackfriday" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-var MaxParallelism = runtime.NumCPU() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var readfile = ioutil.ReadFile 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var create = os.Create 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var mkdirall = os.MkdirAll 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var parseFiles = template.ParseFiles 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+var walk = filepath.Walk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+type ht interface { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	Execute(io.Writer, interface{}) error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 type Generator struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	Input        string 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -25,11 +32,12 @@ type Generator struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	version  string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	pages    []string 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	template *template.Template 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	template ht 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // convert markdown input path to html output path 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// there is no reverse (because we support `.md`, `.mkd`, and `.markdown`) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// @note: we cannot reverse since we do not track the extension 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// we support `.md`, `.mkd`, and `.markdown` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (self *Generator) ior(path string) string { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return strings.TrimSuffix(strings.Replace(path, self.Input, self.Output, 1), filepath.Ext(path)) + ".html" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -44,26 +52,19 @@ func (self *Generator) depth(path string) string { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// walk the directories and build a list of pages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// walk the directories and build a list of markdown files with content 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (self *Generator) walk(path string, file os.FileInfo, err error) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// only pay attention to files with a size greater than 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if file == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} else if file.Mode().IsRegular() && file.Size() > 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		// only add markdown files to our pages array (.md, .mkd, .markdown) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if strings.HasSuffix(path, ".md") || strings.HasSuffix(path, ".mkd") || strings.HasSuffix(path, ".markdown") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			self.pages = append(self.pages, path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if file != nil && file.Mode().IsRegular() && file.Size() > 0 && isMarkdown(path) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		self.pages = append(self.pages, path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // build output concurrently to many pages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (self *Generator) multi() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (self *Generator) multi() (err error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// prepare navigation storage 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	navigation := make(map[string][]Navigation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	navi := make(map[string][]navigation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// loop pages to build table of contents 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	for i, _ := range self.pages { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -73,13 +74,13 @@ func (self *Generator) multi() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		dir := filepath.Dir(self.ior(out)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// create navigation object 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		nav := Navigation{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		nav := navigation{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// sub-index condition changes name, dir, and link 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if filepath.Dir(out) != self.Output && strings.ToLower(basename(out)) == "index" { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			// set name to containing folder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			nav.Name = basename(dir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			nav.Title = basename(dir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			// set relative or absolute link 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			if self.Relative { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -93,7 +94,7 @@ func (self *Generator) multi() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			// set name to files name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			nav.Name = basename(out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			nav.Title = basename(out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			// set relative or absolute link 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			if self.Relative { 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -104,110 +105,82 @@ func (self *Generator) multi() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// build indexes first-match 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if _, ok := navigation[dir]; !ok { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			navigation[dir] = make([]Navigation, 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if _, ok := navi[dir]; !ok { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			navi[dir] = make([]navigation, 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			// create output directory for when we create files 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			if ok, _ := exists(dir); !ok { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if err := os.MkdirAll(dir, 0770); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				if err = mkdirall(dir, 0770); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 					self.Logger.Error("Failed to create path: %s, %s", dir, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// append to navigational list 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		navigation[dir] = append(navigation[dir], nav) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		navi[dir] = append(navi[dir], nav) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// debug navigation output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	self.Logger.Debug("navigation: %+v", navigation) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// prepare waitgroup, bufferer channel, and add number of async handlers to wg 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	var wg sync.WaitGroup 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	pages := make(chan string, MaxParallelism) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	wg.Add(MaxParallelism) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// prepare workers 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for i := 0; i < MaxParallelism; i++ { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		go func() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			defer wg.Done() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			// iterate supplied pages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			for p := range pages { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				// acquire output filepath 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				out := self.ior(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				dir := filepath.Dir(out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				// prepare a new page object for our template to render 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				page := Page{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					Name:    basename(p), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					Version: self.version, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					Nav:     navigation[self.Output], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					Depth:   self.depth(out), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// process all pages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	for _, p := range self.pages { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				// read in page text 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if markdown, err := ioutil.ReadFile(p); err == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// attempt to read entire document 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		var markdown []byte 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if markdown, err = readfile(p); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			self.Logger.Error("failed to read file: %s, %s", p, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					// if this page happens to be a sub-index we can generate the table of contents 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if dir != self.Output && strings.ToLower(basename(p)) == "index" { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// acquire output filepath 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		out := self.ior(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		dir := filepath.Dir(out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						// iterate and build table of contents as basic markdown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						toc := "\n## Table of Contents:\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						for i, _ := range navigation[dir] { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-							toc = toc + "- [" + navigation[dir][i].Name + "](" + navigation[dir][i].Link + ")\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// prepare a new page object for our template to render 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		page := page{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Name:    basename(p), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Version: self.version, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Nav:     navi[self.Output], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			Depth:   self.depth(out), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						// debug table of contents output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						self.Logger.Debug("table of contents for %s, %s", out, toc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// if this page happens to be a sub-index we can generate the table of contents 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if dir != self.Output && strings.ToLower(basename(p)) == "index" { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						// prepend table of contents 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						markdown = append([]byte(toc), markdown...) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			// iterate and build table of contents as basic markdown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			toc := "\n## Table of Contents:\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			for i, _ := range navi[dir] { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+				toc = toc + "- [" + navi[dir][i].Title + "](" + navi[dir][i].Link + ")\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					// convert to html, and accept as part of the template 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					page.Content = template.HTML(blackfriday.MarkdownCommon(markdown)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					self.Logger.Error("failed to read file: %s, %s", p, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			// debug: table of contents output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			self.Logger.Debug("table of contents for %s, %s", out, toc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				// debug page output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				self.Logger.Debug("page: %+v", page) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			// prepend table of contents 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			markdown = append([]byte(toc), markdown...) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				// translate input path to output path & create a write context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if f, err := os.Create(out); err == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defer f.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// convert to html, and accept as part of the template 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		page.Content = template.HTML(blackfriday.MarkdownCommon(markdown)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					// prepare a writer /w buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					fb := bufio.NewWriter(f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					defer fb.Flush() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// attempt to open file for output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		var f *os.File 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if f, err = create(out); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+			return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defer f.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					// attempt to use template to write output with page context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if e := self.template.Execute(fb, page); e != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						self.Logger.Error("Failed to write template: %s, %s", out, e) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					self.Logger.Error("failed to create new file: %s, %s", out, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		}() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// prepare a writer /w buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		fb := bufio.NewWriter(f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		defer fb.Flush() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// send pages to workers for async rendering 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for i, _ := range self.pages { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		pages <- self.pages[i] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		// attempt to use template to write output with page context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		err = self.template.Execute(fb, page) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// close channel and wait for async to finish before continuing 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	close(pages) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	wg.Wait() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // build output synchronously to a single page 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (self *Generator) single() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+func (self *Generator) single() (err error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// prepare []byte array to store all files markdown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	content := make([]byte, 0) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -238,7 +211,8 @@ func (self *Generator) single() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		toc = toc + strings.Repeat("\t", depth) + "- [" + basename(p) + "](#" + anchor + ")\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		// read markdown from file or skip to next file 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		markdown, err := ioutil.ReadFile(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		var markdown []byte 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		markdown, err = readfile(p) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			self.Logger.Error("failed to read file: %s (%s)", p, err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			continue 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -260,26 +234,25 @@ func (self *Generator) single() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// prepend toc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	content = append([]byte(toc), content...) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// create page object with version & content 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	page := Page{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		Version: self.version, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		Content: template.HTML(blackfriday.MarkdownCommon(content)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// prepare output directory 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if ok, _ := exists(self.Output); !ok { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if err := os.MkdirAll(self.Output, 0770); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		if err = mkdirall(self.Output, 0770); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 			return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// create page object with version & content 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	page := page{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Version: self.version, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+		Content: template.HTML(blackfriday.MarkdownCommon(content)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// prepare output file path 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	out := filepath.Join(self.Output, "index.html") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// attempt to open file for output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	var f *os.File 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	var err error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if f, err = os.Create(out); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if f, err = create(out); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	defer f.Close() 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -289,14 +262,15 @@ func (self *Generator) single() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	defer fb.Flush() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// attempt to use template to write output with page context 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return self.template.Execute(fb, page) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	err = self.template.Execute(fb, page) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 func (self *Generator) Generate() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// process template 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	var err error 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if self.template, err = template.ParseFiles(self.TemplateFile); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if self.template, err = parseFiles(self.TemplateFile); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -311,16 +285,13 @@ func (self *Generator) Generate() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	self.Input = filepath.Clean(self.Input) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	self.Output = filepath.Clean(self.Output) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// debug: print state 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	self.Logger.Debug("generator state: %+v", self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// walk the file system 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if err := filepath.Walk(self.Input, self.walk); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	if err := walk(self.Input, self.walk); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 		return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// debug: print pages 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	self.Logger.Debug("pages: %+v", self.pages) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	// debug: print state 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+	self.Logger.Debug("generator state: %+v", self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	// determine assembly method 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 	if self.Book { 
			 |