Adding the ability to click on the images in the history and have that load the code...
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 22 Apr 2014 19:32:06 +0000 (19:32 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 22 Apr 2014 19:32:06 +0000 (19:32 +0000)
BUG=skia:
R=mtklein@google.com

Author: jcgregorio@google.com

Review URL: https://codereview.chromium.org/246393002

git-svn-id: http://skia.googlecode.com/svn/trunk@14310 2bbb7eff-a529-9590-31e7-b0007b416f81

experimental/webtry/js/run.js
experimental/webtry/templates/index.html
experimental/webtry/templates/workspace.html
experimental/webtry/webtry.go

index 5cd43ac..c69e2bc 100644 (file)
     var tryTemplate = document.getElementById('tryTemplate');
 
 
-    function addToHistory(hash, imgUrl) {
-      var clone = tryTemplate.content.cloneNode(true);
-      clone.querySelector('a').href = '/c/' + hash;
-      clone.querySelector('img').src = imgUrl;
-      tryHistory.insertBefore(clone, tryHistory.firstChild);
-    }
-
-
     function beginWait() {
       document.body.classList.add('waiting');
       run.disabled = true;
 
 
     /**
+     * Callback when there's an XHR error.
+     * @param e The callback event.
+     */
+    function xhrError(e) {
+      endWait();
+      alert('Something bad happened: ' + e);
+    }
+
+    /**
+     * Called when an image in the workspace history is clicked.
+     */
+    function historyClick() {
+      beginWait();
+      var req = new XMLHttpRequest();
+      req.addEventListener('load', historyComplete);
+      req.addEventListener('error', xhrError);
+      req.overrideMimeType('application/json');
+      req.open('GET', this.getAttribute('data-try'), true);
+      req.send();
+    }
+
+
+    /**
+     * Callback for when the XHR kicked off in historyClick() returns.
+     */
+    function historyComplete(e) {
+      // The response is JSON of the form:
+      // {
+      //   "hash": "unique id for a try",
+      //   "code": "source code for try"
+      // }
+      endWait();
+      body = JSON.parse(e.target.response);
+      code.value = body.code;
+      img.src = '/i/'+body.hash+'.png';
+      if (permalink) {
+        permalink.href = '/c/' + body.hash;
+      }
+    }
+
+
+    /**
+     * Add the given try image to the history of a workspace.
+     */
+    function addToHistory(hash, imgUrl) {
+      var clone = tryTemplate.content.cloneNode(true);
+      clone.querySelector('img').src = imgUrl;
+      clone.querySelector('.tries').setAttribute('data-try', '/json/' + hash);
+      tryHistory.insertBefore(clone, tryHistory.firstChild);
+      tryHistory.querySelector('.tries').addEventListener('click', historyClick, true);
+    }
+
+
+    /**
      * Callback for when the XHR returns after attempting to run the code.
      * @param e The callback event.
      */
       if (tryHistory) {
         addToHistory(body.hash, 'data:image/png;base64,' + body.img);
       } else {
-        window.history.pushState(null, null, "./" + body.hash);
+        window.history.pushState(null, null, './' + body.hash);
       }
       if (permalink) {
-        permalink.href = "/c/" + body.hash;
+        permalink.href = '/c/' + body.hash;
       }
       if (embed) {
         var url = document.URL;
     }
 
 
-    /**
-     * Callback where there's an XHR error.
-     * @param e The callback event.
-     */
-    function codeError(e) {
-      endWait();
-      alert('Something bad happened: ' + e);
-    }
-
-
     function onSubmitCode() {
       beginWait();
       var req = new XMLHttpRequest();
       req.addEventListener('load', codeComplete);
-      req.addEventListener('error', codeError);
+      req.addEventListener('error', xhrError);
       req.overrideMimeType('application/json');
       req.open('POST', '/', true);
       req.setRequestHeader('content-type', 'application/json');
-      req.send(JSON.stringify({"code": code.value, "name": workspaceName}));
+      req.send(JSON.stringify({'code': code.value, 'name': workspaceName}));
     }
     run.addEventListener('click', onSubmitCode);
 
index b3f6b8f..a29bfd1 100644 (file)
@@ -21,7 +21,7 @@ void draw(SkCanvas* canvas) {
   <input type="text" value="" id="embed" readonly style="display:none;">
 
   <p>
-  <img id='img' src=''/>
+  <img id='img' src='{{if .Hash}}/i/{{.Hash}}.png{{end}}'/>
   </p>
 
   <pre><code id='output'></code></pre>
index 3a461d6..a802ea7 100644 (file)
@@ -6,13 +6,10 @@
     <link rel="stylesheet" href="/css/" type="text/css" media="screen">
 </head>
 <body>
-
   <template id=tryTemplate>
-      <div class=tries>
-        <a href="">
-          <img width=64 height=64 src="">
-        </a>
-      </div>
+    <div class=tries data-try="">
+      <img width=64 height=64 src="">
+    </div>
   </template>
   {{template "titlebar.html"}}
   <section id=content>
@@ -25,9 +22,10 @@ void draw(SkCanvas* canvas) {
 </code></pre>
 
   <input type='button' value='Run' id='run'>
+  <a href='{{if .Hash}}/c/{{.Hash}}{{end}}' target=_blank id=permalink>Share</a>
 
   <p>Image appears here:</p>
-  <img id='img' src=''/>
+  <img id='img' src='{{if .Hash}}/i/{{.Hash}}.png{{end}}'/>
 
   <pre><code id='output'></code></pre>
   </section>
index a895b01..73e9fda 100644 (file)
@@ -67,6 +67,9 @@ var (
        // imageLink is the regex that matches URLs paths that are direct links to PNGs.
        imageLink = regexp.MustCompile("^/i/([a-f0-9]+.png)$")
 
+       // tryInfoLink is the regex that matches URLs paths that are direct links to data about a single try.
+       tryInfoLink = regexp.MustCompile("^/json/([a-f0-9]+)$")
+
        // workspaceLink is the regex that matches URLs paths for workspaces.
        workspaceLink = regexp.MustCompile("^/w/([a-z0-9-]+)$")
 
@@ -382,6 +385,7 @@ func recentHandler(w http.ResponseWriter, r *http.Request) {
 type Workspace struct {
        Name  string
        Code  string
+       Hash  string
        Tries []Try
 }
 
@@ -402,12 +406,13 @@ func newWorkspace() (string, error) {
 }
 
 // getCode returns the code for a given hash, or the empty string if not found.
-func getCode(hash string) string {
+func getCode(hash string) (string, error) {
        code := ""
        if err := db.QueryRow("SELECT code FROM webtry WHERE hash=?", hash).Scan(&code); err != nil {
                log.Printf("ERROR: Code for hash is missing: %q\n", err)
+               return code, err
        }
-       return code
+       return code, nil
 }
 
 func workspaceHandler(w http.ResponseWriter, r *http.Request) {
@@ -434,12 +439,14 @@ func workspaceHandler(w http.ResponseWriter, r *http.Request) {
                        }
                }
                var code string
+               var hash string
                if len(tries) == 0 {
                        code = DEFAULT_SAMPLE
                } else {
-                       code = getCode(tries[len(tries)-1].Hash)
+                       hash = tries[len(tries)-1].Hash
+                       code, _ = getCode(hash)
                }
-               if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name}); err != nil {
+               if err := workspaceTemplate.Execute(w, Workspace{Tries: tries, Code: code, Name: name, Hash: hash}); err != nil {
                        log.Printf("ERROR: Failed to expand template: %q\n", err)
                }
        } else if r.Method == "POST" {
@@ -465,7 +472,7 @@ func hasPreProcessor(code string) bool {
 
 type TryRequest struct {
        Code string `json:"code"`
-       Name string `json:"name"`
+       Name string `json:"name"` // Optional name of the workspace the code is in.
 }
 
 // iframeHandler handles the GET and POST of the main page.
@@ -486,8 +493,8 @@ func iframeHandler(w http.ResponseWriter, r *http.Request) {
                return
        }
        var code string
-       // Load 'code' with the code found in the database.
-       if err := db.QueryRow("SELECT code FROM webtry WHERE hash=?", hash).Scan(&code); err != nil {
+       code, err := getCode(hash)
+       if err != nil {
                http.NotFound(w, r)
                return
        }
@@ -497,14 +504,51 @@ func iframeHandler(w http.ResponseWriter, r *http.Request) {
        }
 }
 
+type TryInfo struct {
+       Hash string `json:"hash"`
+       Code string `json:"code"`
+}
+
+// tryInfoHandler returns information about a specific try.
+func tryInfoHandler(w http.ResponseWriter, r *http.Request) {
+       log.Printf("Try Info Handler: %q\n", r.URL.Path)
+       if r.Method != "GET" {
+               http.NotFound(w, r)
+               return
+       }
+       match := tryInfoLink.FindStringSubmatch(r.URL.Path)
+       if len(match) != 2 {
+               http.NotFound(w, r)
+               return
+       }
+       hash := match[1]
+       code, err := getCode(hash)
+       if err != nil {
+               http.NotFound(w, r)
+               return
+       }
+       m := TryInfo{
+               Hash: hash,
+               Code: code,
+       }
+       resp, err := json.Marshal(m)
+       if err != nil {
+               reportError(w, r, err, "Failed to serialize a response.")
+               return
+       }
+       w.Header().Set("Content-Type", "application/json")
+       w.Write(resp)
+}
+
 // mainHandler handles the GET and POST of the main page.
 func mainHandler(w http.ResponseWriter, r *http.Request) {
        log.Printf("Main Handler: %q\n", r.URL.Path)
        if r.Method == "GET" {
                code := DEFAULT_SAMPLE
                match := directLink.FindStringSubmatch(r.URL.Path)
+               var hash string
                if len(match) == 2 && r.URL.Path != "/" {
-                       hash := match[1]
+                       hash = match[1]
                        if db == nil {
                                http.NotFound(w, r)
                                return
@@ -516,7 +560,7 @@ func mainHandler(w http.ResponseWriter, r *http.Request) {
                        }
                }
                // Expand the template.
-               if err := indexTemplate.Execute(w, userCode{UserCode: code}); err != nil {
+               if err := indexTemplate.Execute(w, userCode{UserCode: code, Hash: hash}); err != nil {
                        log.Printf("ERROR: Failed to expand template: %q\n", err)
                }
        } else if r.Method == "POST" {
@@ -602,8 +646,10 @@ func main() {
        http.HandleFunc("/w/", workspaceHandler)
        http.HandleFunc("/recent/", recentHandler)
        http.HandleFunc("/iframe/", iframeHandler)
+       http.HandleFunc("/json/", tryInfoHandler)
        http.HandleFunc("/css/", cssHandler)
        http.HandleFunc("/js/", jsHandler)
+       // TODO Break out /c/ as it's own handler.
        http.HandleFunc("/", mainHandler)
        log.Fatal(http.ListenAndServe(*port, nil))
 }