From 85aea63307f55abe319b1a9bd66890959fba8136 Mon Sep 17 00:00:00 2001
From: Henrique Dias <hacdias@gmail.com>
Date: Tue, 15 Sep 2015 11:59:48 +0100
Subject: [PATCH] update

---
 edit/edit.go               |   5 +-
 frontmatter/frontmatter.go |  46 ++++++++++--
 static/css/main.css        | 121 ++++++++++++++++++++++++++++--
 static/css/scrollbar.css   | 146 +++++++++++++++++++++++++++++++++++++
 static/js/app.js           |  23 ++++--
 templates/base_full.tmpl   |   4 +-
 templates/edit.tmpl        |  28 ++++---
 templates/frontmatter.tmpl |   9 ++-
 utils/utils.go             |   8 ++
 9 files changed, 354 insertions(+), 36 deletions(-)
 create mode 100644 static/css/scrollbar.css

diff --git a/edit/edit.go b/edit/edit.go
index a1823dd5..022fa4dc 100644
--- a/edit/edit.go
+++ b/edit/edit.go
@@ -66,7 +66,10 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
 
 		w.Header().Set("Content-Type", "application/json")
 		w.Write([]byte("{}"))
-		go commands.Execute()
+
+		if r.Header.Get("X-Save-Mode") == "Publish" {
+			go commands.Execute()
+		}
 	} else {
 		if _, err := os.Stat(filename); os.IsNotExist(err) {
 			log.Print(err)
diff --git a/frontmatter/frontmatter.go b/frontmatter/frontmatter.go
index 1601a2b7..98990c21 100644
--- a/frontmatter/frontmatter.go
+++ b/frontmatter/frontmatter.go
@@ -16,16 +16,42 @@ func Pretty(content []byte) (interface{}, error) {
 		return []string{}, err
 	}
 
-	return rawToPretty(front, ""), nil
+	return rawToPretty(front, "", ""), nil
 }
 
 type frontmatter struct {
-	Name       string
-	Content    interface{}
-	SubContent bool
+	Name    string
+	Content interface{}
+	Parent  string
+	Type    string
 }
 
-func rawToPretty(config interface{}, master string) interface{} {
+func rawToPretty(config interface{}, master string, parent string) interface{} {
+	if utils.IsSlice(config) {
+		settings := make([]interface{}, len(config.([]interface{})))
+
+		for index, element := range config.([]interface{}) {
+			c := new(frontmatter)
+			c.Name = master
+			c.Parent = parent
+
+			if utils.IsMap(element) {
+				c.Type = "object"
+				c.Content = rawToPretty(element, c.Name, "object")
+			} else if utils.IsSlice(element) {
+				c.Type = "array"
+				c.Content = rawToPretty(element, c.Name, "array")
+			} else {
+				c.Type = "text"
+				c.Content = element
+			}
+
+			settings[index] = c
+		}
+
+		return settings
+	}
+
 	var mapsNames []string
 	var stringsNames []string
 
@@ -46,14 +72,18 @@ func rawToPretty(config interface{}, master string) interface{} {
 	for index := range names {
 		c := new(frontmatter)
 		c.Name = names[index]
-		c.SubContent = false
+		c.Parent = parent
 
 		i := config.(map[string]interface{})[names[index]]
 
 		if utils.IsMap(i) {
-			c.Content = rawToPretty(i, c.Name)
-			c.SubContent = true
+			c.Type = "object"
+			c.Content = rawToPretty(i, c.Name, "object")
+		} else if utils.IsSlice(i) {
+			c.Type = "array"
+			c.Content = rawToPretty(i, c.Name, "array")
 		} else {
+			c.Type = "text"
 			c.Content = i
 		}
 
diff --git a/static/css/main.css b/static/css/main.css
index 83e03b9a..061d9329 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -11,15 +11,13 @@ header {
   left: 0;
   height: 3em;
   width: 100%;
-  background-color: #EEE;
+  background-color: #263238;
   padding: 0 2em;
   box-sizing: border-box;
   z-index: 999;
-  color: #555;
+  color: #eee;
 }
 
-header nav {}
-
 header nav ul {
   margin: 0;
   padding: 0;
@@ -63,7 +61,49 @@ header nav ul li a:hover {
 
 main {
   top: 3em;
-  position: relative;
+  left: 0;
+  position: fixed;
+  width: 100%;
+  height: 100%;
+  overflow: inherit;
+}
+
+.container {
+  box-sizing: border-box;
+  top: 3em;
+  height: auto;
+  max-height: 100%;
+  bottom: 0;
+}
+
+.container.left {
+  position: fixed;
+  left: 0;
+  width: 25%;
+  background-color: #37474F;
+  overflow: auto;
+  color: #eee;
+  padding: 1.5em 1.5em;
+}
+
+.container.main {
+  position: fixed;
+  right: 0;
+  width: 75%;
+  overflow: auto;
+  padding: 0;
+  margin-bottom: 3em;
+}
+
+.container *:first-child {
+  margin-top: 0;
+}
+
+.container.main textarea {
+  height: 100%;
+  width: 100%;
+  padding: 2em 5em;
+  box-sizing: border-box;
 }
 
 .content {
@@ -75,16 +115,15 @@ main {
 textarea {
   width: 100%;
   min-height: 50em;
-  resize: vertical;
   border: 0;
+  resize: vertical;
+  box-sizing: content-box;
   font-family: inherit;
 }
 
 
 /* FORMS */
 
-form {}
-
 form input {
   color: rgba(0, 0, 0, 0.41);
   width: 15em;
@@ -92,6 +131,7 @@ form input {
   margin: .5em 0;
   border: 1px solid #fff;
   transition: .5s ease-out all;
+  background-color: transparent;
 }
 
 form input:focus {
@@ -103,4 +143,69 @@ form input:focus {
 form label {
   width: 10.5em;
   display: inline-block;
+}
+
+form fieldset {
+  border: 0;
+  margin: 0;
+  padding: 0;
+}
+
+form legend {
+  font-size: 1.5em;
+}
+
+form .container.left input {
+  width: 100%;
+  background-color: #fff;
+  border-radius: 5px;
+  padding: .5em 1em;
+  box-sizing: border-box;
+}
+
+form .container.left input:focus {
+  border: 1px solid #ddd;
+}
+
+form .container.left label {}
+
+form .container.left legend {
+  font-size: 1em;
+  font-weight: bold;
+}
+
+.action-bar {
+  position: fixed;
+  bottom: 0;
+  right: 0;
+  width: 75%;
+  background-color: #455A64;
+  height: 3em;
+  display: flex;
+  padding: 0.5em 1em;
+  box-sizing: border-box;
+}
+
+.action-bar .left {
+  margin-right: auto;
+}
+
+.action-bar *:last-child {
+  margin-left: 1em;
+}
+
+button, input[type="submit"] {
+  border: 0;
+  color: #fff;
+  margin: 0;
+  padding: .5em 1em;
+  border-radius: 10px;
+  font-size: .9em;
+  width: auto;
+  line-height: 1em;
+  background-color: #BBB;
+}
+
+button.default, input[type="submit"].default {
+  background-color: #2196F3;
 }
\ No newline at end of file
diff --git a/static/css/scrollbar.css b/static/css/scrollbar.css
new file mode 100644
index 00000000..cc60ba5a
--- /dev/null
+++ b/static/css/scrollbar.css
@@ -0,0 +1,146 @@
+/* perfect-scrollbar v0.6.5 */
+
+.ps-container {
+  -ms-touch-action: none;
+  overflow: hidden !important;
+}
+
+.ps-container.ps-active-x > .ps-scrollbar-x-rail, .ps-container.ps-active-y > .ps-scrollbar-y-rail {
+  display: block;
+}
+
+.ps-container.ps-in-scrolling {
+  pointer-events: none;
+}
+
+.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x {
+  background-color: #999;
+}
+
+.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y {
+  background-color: #999;
+}
+
+.ps-container > .ps-scrollbar-x-rail {
+  display: none;
+  position: absolute;
+  /* please don't change 'position' */
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  border-radius: 4px;
+  opacity: 0;
+  -webkit-transition: background-color .2s linear, opacity .2s linear;
+  -moz-transition: background-color .2s linear, opacity .2s linear;
+  -o-transition: background-color .2s linear, opacity .2s linear;
+  transition: background-color .2s linear, opacity .2s linear;
+  bottom: 3px;
+  /* there must be 'bottom' for ps-scrollbar-x-rail */
+  height: 8px;
+}
+
+.ps-container > .ps-scrollbar-x-rail > .ps-scrollbar-x {
+  position: absolute;
+  /* please don't change 'position' */
+  background-color: #aaa;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  border-radius: 4px;
+  -webkit-transition: background-color .2s linear;
+  -moz-transition: background-color .2s linear;
+  -o-transition: background-color .2s linear;
+  transition: background-color .2s linear;
+  bottom: 0;
+  /* there must be 'bottom' for ps-scrollbar-x */
+  height: 8px;
+}
+
+.ps-container > .ps-scrollbar-y-rail {
+  display: none;
+  position: absolute;
+  /* please don't change 'position' */
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  border-radius: 4px;
+  opacity: 0;
+  -webkit-transition: background-color .2s linear, opacity .2s linear;
+  -moz-transition: background-color .2s linear, opacity .2s linear;
+  -o-transition: background-color .2s linear, opacity .2s linear;
+  transition: background-color .2s linear, opacity .2s linear;
+  right: 3px;
+  /* there must be 'right' for ps-scrollbar-y-rail */
+  width: 8px;
+}
+
+.ps-container > .ps-scrollbar-y-rail > .ps-scrollbar-y {
+  position: absolute;
+  /* please don't change 'position' */
+  background-color: #aaa;
+  -webkit-border-radius: 4px;
+  -moz-border-radius: 4px;
+  -ms-border-radius: 4px;
+  border-radius: 4px;
+  -webkit-transition: background-color .2s linear;
+  -moz-transition: background-color .2s linear;
+  -o-transition: background-color .2s linear;
+  transition: background-color .2s linear;
+  right: 0;
+  /* there must be 'right' for ps-scrollbar-y */
+  width: 8px;
+}
+
+.ps-container:hover.ps-in-scrolling {
+  pointer-events: none;
+}
+
+.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container:hover.ps-in-scrolling.ps-x > .ps-scrollbar-x-rail > .ps-scrollbar-x {
+  background-color: #999;
+}
+
+.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container:hover.ps-in-scrolling.ps-y > .ps-scrollbar-y-rail > .ps-scrollbar-y {
+  background-color: #999;
+}
+
+.ps-container:hover > .ps-scrollbar-x-rail, .ps-container:hover > .ps-scrollbar-y-rail {
+  opacity: 0.6;
+}
+
+.ps-container:hover > .ps-scrollbar-x-rail:hover {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container:hover > .ps-scrollbar-x-rail:hover > .ps-scrollbar-x {
+  background-color: #999;
+}
+
+.ps-container:hover > .ps-scrollbar-y-rail:hover {
+  background-color: #eee;
+  opacity: 0.9;
+}
+
+.ps-container:hover > .ps-scrollbar-y-rail:hover > .ps-scrollbar-y {
+  background-color: #999;
+}
\ No newline at end of file
diff --git a/static/js/app.js b/static/js/app.js
index d15814be..cb4ab6f7 100644
--- a/static/js/app.js
+++ b/static/js/app.js
@@ -1,18 +1,21 @@
 $(document).ready(function() {
+  $('.scroll').perfectScrollbar();
+
+
   $('form').submit(function(event) {
     var data = JSON.stringify($(this).serializeField())
     var url = $(this).attr('action')
+    var action = $(this).find("input[type=submit]:focus").val();
 
-    console.log(data)
+    console.log(data);
 
     $.ajax({
       type: 'POST',
       url: url,
       data: data,
       beforeSend: function(xhr) {
-        // add publish and save
-        xhr.setRequestHeader('X-Save-Mode', 'publish');
-      }
+        xhr.setRequestHeader('X-Save-Mode', action);
+      },
       dataType: 'json',
       encode: true,
     }).done(function(data) {
@@ -47,12 +50,20 @@ $(document).ready(function() {
 $.fn.serializeField = function() {
   var result = {};
   this.each(function() {
-    $(this).find("> *").each(function() {
+    $(this).find(".container > *").each(function() {
       var $this = $(this);
       var name = $this.attr("name");
 
       if ($this.is("fieldset") && name) {
-        result[name] = $this.serializeField();
+        if ($this.attr("type") == "array") {
+          result[this.name] = [];
+
+          $.each($this.serializeArray(), function() {
+            result[this.name].push(this.value);
+          });
+        } else {
+          result[name] = $this.serializeField();
+        }
       } else {
         $.each($this.serializeArray(), function() {
           result[this.name] = this.value;
diff --git a/templates/base_full.tmpl b/templates/base_full.tmpl
index c7231659..2b021090 100644
--- a/templates/base_full.tmpl
+++ b/templates/base_full.tmpl
@@ -12,9 +12,11 @@
 
   <link rel="stylesheet" href="/admin/static/css/normalize.css">
   <link rel="stylesheet" href="/admin/static/css/main.css">
+  <link rel="stylesheet" href="/admin/static/css/scrollbar.css">
   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
 
   <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
+  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.perfect-scrollbar/0.6.5/js/min/perfect-scrollbar.jquery.min.js"></script>
   <script src="/admin/static/js/app.js"></script>
 </head>
 
@@ -38,4 +40,4 @@
   </footer>
 </body>
 
-</html>
\ No newline at end of file
+</html>
diff --git a/templates/edit.tmpl b/templates/edit.tmpl
index fea9a3ac..7c869e5d 100644
--- a/templates/edit.tmpl
+++ b/templates/edit.tmpl
@@ -1,14 +1,24 @@
 {{ define "content" }} {{ with .Body }}
 
-<div class="content">
-  <h1>Editing {{ .Name }}</h1>
-
-  <form method="POST" action="">
-    {{ template "frontmatter" .FrontMatter }}
-    <textarea name="content">{{ .Content }}</textarea>
-    <input type="submit" value="Save">
-  </form>
+<form method="POST" action="">
 
+<div class="container left scroll">
+  <h2>Metadata</h2>
+  {{ template "frontmatter" .FrontMatter }}
 </div>
 
-{{ end }} {{ end }}
\ No newline at end of file
+<div class="container main">
+  <textarea name="content" class="scroll">
+    {{ .Content }}
+  </textarea>
+</div>
+
+<div class="action-bar">
+  <button id="preview" class="left">Preview</button>
+  <input type="submit" value="Save">
+  <input type="submit" class="default" value="Publish">
+</div>
+
+</form>
+
+{{ end }} {{ end }}
diff --git a/templates/frontmatter.tmpl b/templates/frontmatter.tmpl
index f705aa39..154bc3ac 100644
--- a/templates/frontmatter.tmpl
+++ b/templates/frontmatter.tmpl
@@ -1,12 +1,15 @@
 {{ define "frontmatter" }}
   {{ range $key, $value := . }}
-    {{ if $value.SubContent }}
-      <fieldset name="{{ $value.Name }}">
+    {{ if or (eq $value.Type "object") (eq $value.Type "array") }}
+      <fieldset name="{{ $value.Name }}" type="{{ $value.Type }}">
         <legend>{{ splitCapitalize $value.Name }}</legend>
         {{ template "frontmatter" $value.Content }}
+        <button><i class="fa fa-plus"></i> Add field</button>
       </fieldset>
-    {{ else}}
+    {{ else }}
+    {{ if not (eq $value.Parent "array") }}
     <label for="{{ $value.Name }}">{{ splitCapitalize $value.Name }}</label>
+    {{ end }}
     <input name="{{ $value.Name }}" id="{{ $value.Name }}" value="{{ $value.Content }}"></input><br>
     {{ end }}
   {{ end }}
diff --git a/utils/utils.go b/utils/utils.go
index 534c9c04..1f5433ff 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -27,6 +27,14 @@ func IsMap(sth interface{}) bool {
 	return reflect.ValueOf(sth).Kind() == reflect.Map
 }
 
+func IsSlice(sth interface{}) bool {
+	return reflect.ValueOf(sth).Kind() == reflect.Slice
+}
+
+func IsArray(sth interface{}) bool {
+	return reflect.ValueOf(sth).Kind() == reflect.Array
+}
+
 func SplitCapitalize(name string) string {
 	var words []string
 	l := 0