Bladeren bron

Refactor index rendering

Benoît Hubert 8 jaren geleden
bovenliggende
commit
2c01d92232
10 gewijzigde bestanden met toevoegingen van 192 en 71 verwijderingen
  1. 14 5
      css/styles.css
  2. 22 7
      html/index.mustache.html
  3. 0 4
      js/editor.js
  4. 8 0
      js/menu.js
  5. 5 0
      languages/en-US.json
  6. 5 0
      languages/fr-FR.json
  7. 116 53
      lib/indexHandlers.js
  8. 18 0
      lib/translator.js
  9. 2 1
      package.json
  10. 2 1
      sandboxApp.js

+ 14 - 5
css/styles.css

@@ -146,22 +146,25 @@ html, body {
   display: inline-block;
 }*/
 #notification {
-  color: black;
+  color: #333;
   position: absolute;
   top: -100px;
   right: 10px;
   transition: top 1s;
+}
+.alert-box {
+  text-align: center;
   border-radius: 3px;
-  padding: 3px;
+  padding: 5px 10px;
 }
 #notification.active {
   top: 5px;
 }
-#notification.error {
+.alert-box.error {
   border: 1px solid #d42;
   background: #ffe8e0;
 }
-#notification.success {
+.alert-box.success {
   border: 1px solid #2d4;
   background: #e0ffe8;
 }
@@ -221,6 +224,10 @@ button:hover {
   border-bottom: 1px solid #ddd;
   font-size: 26px;
 }
+#navbar a {
+  color: #555;
+}
+#navbar a:hover,
 #navbar button:hover {
   color: #777;
 }
@@ -256,7 +263,9 @@ button:hover {
   padding-left: 0;
   margin-top: 5px;
 }
-
+#menu-btn:focus {
+  outline: none;
+}
 /* Menus de nav */
 #nav-menus {
 }

+ 22 - 7
html/index.mustache.html

@@ -1,7 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-<title>Web sandbox</title>
+<title>Web Sandbox</title>
     <link rel="stylesheet" href="/css/normalize.css">
     <link rel="stylesheet" href="/css/main.css">
 <!--     <link rel="stylesheet" href="css/qunit-2.4.1.css"> -->
@@ -19,10 +19,15 @@
 
     <div class="panel-left">
 
+        <div id="navbar">
+            <button id="menu-btn" class="icon-menu"></button>
+            <a href="/">Web Sandbox</a>
+        </div>
+
         <div id="nav-menus">
             <div id="menu-repo" class="pure-g">
               <div class="pure-u-1">
-                <span class="cat-title">Collections d'examples</span>
+                <span class="cat-title">Collections d'exemples</span>
                 <ul class="nav-menu">{{#menuRepo}}
                   <li><a href="/{{slug}}">{{title}}</a></li>{{/menuRepo}}
                 </ul>
@@ -37,14 +42,15 @@
             </div>{{/menuExample}}</div>
         </div>
 
-        <div id="navbar">
-            <button id="menu-btn" class="icon-menu"></button>
-        </div>
-
         <div id="notification"></div>
         <div class="panel-inner">
 
+        {{#errorMessage}}
+            <div class="alert-box error">{{errorMessage}}</div>
+        {{/errorMessage}}
+
 
+        {{#showControls}}
             <button id="add-example-btn" class="icon-plus rounded blue"></button>
             <form id="add-example-form" style="display: none;">
                 <input type="text" name="title" class="input-sm" value="" />
@@ -52,18 +58,24 @@
                 --><button type="submit" id="add-example-save" class="icon-checkmark rounded green"></button>
             </form>
             <button id="save-changes" class="icon-cloud-upload green"></button>
+        {{/showControls}}
 
-
+        {{#showEditor}}
             <ul id="tabs">{{#files}}
                 <li class="tab-{{type}}" data-type="{{type}}">{{name}}</li>
             {{/files}}
             <li>+</li>
             </ul>
+        {{/showEditor}}
         </div>
+
+        {{#showEditor}}
         <div id="editor"></div>
+
         <script type="text/html" id="editor-javascript"></script>
         <script type="text/html" id="editor-html"></script>
         <script type="text/html" id="editor-css"></script>
+        {{/showEditor}}
         <!-- <button id="revert-editor">!</button> -->
     </div>
 
@@ -81,6 +93,8 @@
 <script src="/js/vendor/loadJS.js" ></script>
 <script src="/js/plugins.js"></script>
 <script src="/js/main.js"></script>
+<script src="/js/menu.js"></script>
+{{#showEditor}}
 <script src="/js/vendor/ace/ace.js" type="text/javascript" charset="utf-8"></script>
 <script src="/js/req-promise.js"></script>
 <script src="/js/editor-local-storage.js"></script>
@@ -88,5 +102,6 @@
 window._webSandboxFiles = {{{filesJSON}}};
 </script>
 <script src="/js/editor.js"></script>
+{{/showEditor}}
 </body>
 </html>

+ 0 - 4
js/editor.js

@@ -32,10 +32,6 @@ $(document).ready(function() {
     html: 'html', js: 'javascript', css: 'css'
   }
 
-  $('#menu-btn').click(() => {
-    $('#nav-menus').toggleClass('in');
-  });
-
   if(_webSandboxFiles.length === 0) {
     return;
   }

+ 8 - 0
js/menu.js

@@ -0,0 +1,8 @@
+"use strict";
+$(document).ready(function() {
+
+  $('#menu-btn').click(() => {
+    $('#nav-menus').toggleClass('in');
+  });
+
+});

+ 5 - 0
languages/en-US.json

@@ -0,0 +1,5 @@
+{
+  "home": "Home",
+  "repoNotFound": "Example collection `%s` not found",
+  "exampleNotFound": "Example `%s/%s` not found"
+}

+ 5 - 0
languages/fr-FR.json

@@ -0,0 +1,5 @@
+{
+  "home": "Accueil",
+  "repoNotFound": "Collection d'exemples `%s` introuvable",
+  "exampleNotFound": "Exemple `%s/%s` introuvable"
+}

+ 116 - 53
lib/indexHandlers.js

@@ -10,6 +10,7 @@ var indexTpml;
 var {
   readFilesAsync
 }                 = require('../lib/fsio');
+var translator    = require('../lib/translator');
 
 
 // One-liner for current directory, ignores .dotfiles
@@ -31,30 +32,88 @@ module.exports = function(exStore, exDir) {
     const { html, js, css } = config; // libsCss, libsJs 
 
     const files = [].concat(html, js, css);
-          // console.log('#2', exampleDir, html, js, css, files);
     return readFilesAsync(exampleDir, files);
-    // .then(files => {
-    //   console.log('#### files', files);
-    //   return files;
-    // });
   }
 
+  function renderIndex(req, withRepo, withExample) {
 
+    // Extract repoSlug and exampleSlug from req.params
+    const { locale, params: { repoSlug, exampleSlug } } = req;
+    let repo;
+    let example;
+    let menuExample;
+    let statusCode;
+
+    // Initialize view data
+    let data = {
+      menuRepo: exStore.getRepoMenu()
+    }
+
+    // Fetch example repository if needed
+    if(withRepo) {
+      repo = exStore.getRepo(repoSlug);
+      if(! repo) {
+        // return res.status(404).send('Repo ' + req.params.repoSlug + ' not found');
+        data.errorMessage = translator.get(locale, "repoNotFound", [repoSlug]); //'Repo ' + params.repoSlug + ' not found';
+        statusCode = 404;
+      }
+      else {
+        data.menuExample = exStore.getExampleMenu(repo.path);
+        data.showControls = true;
+      }
+    }
+
+    // Fetch example if needed
+    if(withExample && repo) {
+      example = _.find(repo.examples, { slug: exampleSlug });
+      if(! example) {
+        // return res.status(404).send('Example ' + req.params.repoSlug + '/' + req.params.exampleSlug + ' not found');
+        data.errorMessage = translator.get(locale, "exampleNotFound", [repoSlug, exampleSlug]);
+        statusCode = 404;
+      }
+      else {
+        data.showEditor = true;
+      }
+    }
+
+    // Mustache.render(indexTpml, data);
+    return (
+      exampleSlug && example ?
+        readExampleFiles(repoSlug, exampleSlug, example) : Promise.resolve([])
+    ).then(files =>
+      ({ files, filesJSON: JSON.stringify(files) })
+    )
+    .then(({ files, filesJSON }) => Object.assign(data, { files, filesJSON }))
+    .then(c => { console.log(c); return c; })
+    .then(data => ({
+      html: Mustache.render(indexTpml, data),
+      code: statusCode ? statusCode : 200
+    }));
+  }
 
   return {
 
+
+    /**
+     * Extract language header from req
+     */
+    getAcceptLanguage: function (req, res, next) {
+      const acceptLanguageHdr = req.headers["accept-language"];
+      const re = /[a-z]{2}\-[A-Z]{2}/;
+      const matches = re.exec(acceptLanguageHdr);
+      if(matches) {
+        req.locale = matches[0];
+      }
+      next();
+    },
+
+
     /**
      * Get bare index without repo or examples
      */
     getIndexBare: function(req, res) {
-      const menuRepo = exStore.getRepoMenu();
-      // const title    = 'Home';
-      console.log(menuRepo);
-      res.send(Mustache.render(indexTpml, {
-        // title,
-        menuRepo,
-        filesJSON: '[]'
-      }));
+      renderIndex(req)
+      .then(({ html, code }) => res.send(html));
     },
 
 
@@ -62,20 +121,22 @@ module.exports = function(exStore, exDir) {
      * Get index with repo selected only
      */
     getIndexRepo: function getIndexRepo(req, res) {
-      const repo = exStore.getRepo(req.params.repoSlug);
-      if(! repo) {
-        return res.status(404).send('Repo ' + req.params.repoSlug + ' not found');
-      }
-      const menuRepo = exStore.getRepoMenu();
-      const menuExample = exStore.getExampleMenu(repo.path);
-      // const title    = 'Home';
-      // console.log(menuExample);
-      res.send(Mustache.render(indexTpml, {
-        // title,
-        menuRepo,
-        menuExample,
-        filesJSON: '[]'
-      }));
+      renderIndex(req, true)
+      .then(({ html, code }) => res.status(code).send(html));
+      // const repo = exStore.getRepo(req.params.repoSlug);
+      // if(! repo) {
+      //   return res.status(404).send('Repo ' + req.params.repoSlug + ' not found');
+      // }
+      // const menuRepo = exStore.getRepoMenu();
+      // const menuExample = exStore.getExampleMenu(repo.path);
+      // // const title    = 'Home';
+      // // console.log(menuExample);
+      // res.send(Mustache.render(indexTpml, {
+      //   // title,
+      //   menuRepo,
+      //   menuExample,
+      //   filesJSON: '[]'
+      // }));
     },
 
 
@@ -83,32 +144,34 @@ module.exports = function(exStore, exDir) {
      * Get index with selected repo&example
      */
     getIndexExample: function(req, res) {
-      console.log('getIndexExample', req.params);
-      const repo = exStore.getRepo(req.params.repoSlug);
-      if(! repo) {
-        return res.status(404).send('Repo ' + req.params.repoSlug + ' not found');
-      }
-      const menuRepo = exStore.getRepoMenu();
-      const menuExample = exStore.getExampleMenu(repo.path);
-      const example = _.find(repo.examples, { slug: req.params.exampleSlug });
-      if(! example) {
-        return res.status(404).send('Example ' + req.params.repoSlug + '/' + req.params.exampleSlug + ' not found');
-      }
-      // console.log('#1');
-      // const title    = 'Home';
-      // console.log(menuExample);
-      const { repoSlug, exampleSlug } = req.params;
-      readExampleFiles(repoSlug, exampleSlug, example)
-      .then(files => {
-        console.log('example files', files);
-        res.send(Mustache.render(indexTpml, {
-          // title,
-          menuRepo,
-          menuExample,
-          files,
-          filesJSON: JSON.stringify(files)
-        }));
-      });
+      renderIndex(req, true, true)
+      .then(({ html, code }) => res.status(code).send(html));
+      // console.log('getIndexExample', req.params);
+      // const repo = exStore.getRepo(req.params.repoSlug);
+      // if(! repo) {
+      //   return res.status(404).send('Repo ' + req.params.repoSlug + ' not found');
+      // }
+      // const menuRepo = exStore.getRepoMenu();
+      // const menuExample = exStore.getExampleMenu(repo.path);
+      // const example = _.find(repo.examples, { slug: req.params.exampleSlug });
+      // if(! example) {
+      //   return res.status(404).send('Example ' + req.params.repoSlug + '/' + req.params.exampleSlug + ' not found');
+      // }
+      // // console.log('#1');
+      // // const title    = 'Home';
+      // // console.log(menuExample);
+      // const { repoSlug, exampleSlug } = req.params;
+      // readExampleFiles(repoSlug, exampleSlug, example)
+      // .then(files => {
+      //   console.log('example files', files);
+      //   res.send(Mustache.render(indexTpml, {
+      //     // title,
+      //     menuRepo,
+      //     menuExample,
+      //     files,
+      //     filesJSON: JSON.stringify(files)
+      //   }));
+      // });
     }
 
   };

+ 18 - 0
lib/translator.js

@@ -0,0 +1,18 @@
+var fs       = require('fs');
+var path     = require('path');
+var langDir  = path.normalize(__dirname + '/../languages');
+var locales  = fs.readdirSync(langDir)
+               .map(locale => path.basename(locale, '.json'));
+var vsprintf = require('sprintf-js').vsprintf;
+var langs    = locales.reduce((carry, locale) =>
+  Object.assign(carry, { [locale]: require(langDir + '/' + locale + '.json') }),
+  {}
+);
+
+module.exports = {
+  get: function(locale, string, args) {
+    locale = locales.indexOf(locale) > -1 ? locale : 'en-US';
+    const translation = langs[locale][string];
+    return translation ? vsprintf(translation, args) : 'N/A';
+  }
+};

+ 2 - 1
package.json

@@ -9,7 +9,8 @@
     "json-beautify": "^1.0.1",
     "lodash": "^4.17.4",
     "mustache": "^2.3.0",
-    "slug": "^0.9.1"
+    "slug": "^0.9.1",
+    "sprintf-js": "^1.1.1"
   },
   "devDependencies": {
     "chai": "^4.1.2",

+ 2 - 1
sandboxApp.js

@@ -22,6 +22,7 @@ var {
   readFilesAsync
 }                 = require('./lib/fsio');
 var {
+  getAcceptLanguage,
   getIndexBare,
   getIndexRepo,
   getIndexExample
@@ -41,7 +42,7 @@ exStore.init();
 app.use(express.static(__dirname));
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: true }));
-
+app.use(getAcceptLanguage);
 
 function addExample(slug, title) {
   examples.push({ slug: slug, title: title });