Browse Source

init with flexbox

Chuan Miao 9 years ago
commit
fc73dbe951
10 changed files with 452 additions and 0 deletions
  1. 3 0
      .gitignore
  2. 102 0
      gulpfile.js
  3. 36 0
      package.json
  4. 62 0
      src/css/index.css
  5. 29 0
      src/htdocs/index.html
  6. BIN
      src/images/arrow_down.png
  7. BIN
      src/images/arrow_up.png
  8. 202 0
      src/javascript/app.js
  9. 1 0
      src/javascript/global.js
  10. 17 0
      src/javascript/page.js

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+node_modules/
+bower_components/
+build/

+ 102 - 0
gulpfile.js

@@ -0,0 +1,102 @@
+var gulp = require("gulp");
+var uglify = require("gulp-uglify");
+var rename = require("gulp-rename");
+var reactify = require("reactify");
+var browserify = require("browserify");
+var source = require("vinyl-source-stream");
+var buffer = require("vinyl-buffer");
+var debug  =  require("gulp-debug");
+
+var config = {
+    src: './src',
+    dest: './build'
+}
+
+/**** gulp tasks ***************************************************/
+gulp.task('global', function() {
+    var entry = config.src + '/javascript/global.js';
+    var dest = config.dest;
+    var outputName = 'global.js';
+    var files = [
+        'jquery',
+        'backbone',
+        'react',
+        {file: 'backbone/node_modules/underscore', expose: 'underscore'}
+    ];
+
+    var b = browserify(entry)
+            .require(files);
+
+    return b.bundle()
+            .pipe(source(outputName))
+            .pipe(gulp.dest(dest));
+});
+
+gulp.task('app', function() {
+    var entry = config.src + '/javascript/app.js';
+    var dest = config.dest;
+    var outputName = 'app.js';
+    var modules = ['jquery', 'backbone', 'underscore', 'react'];
+
+    var b = browserify(entry)
+            .external(modules)
+            .transform(reactify);
+
+    return b.bundle()
+        .pipe(source(outputName))
+        .pipe(gulp.dest(dest));
+});
+
+gulp.task('htdocs', function() {
+    var src = config.src + '/htdocs/**.html';
+    var dest = config.dest;
+    return gulp.src(src)
+        //.pipe(debug({title: 'unicorn:'}))
+        .pipe(gulp.dest(dest));
+});
+
+gulp.task('css', function() {
+    var src = config.src + '/css/**';
+    var dest = config.dest + '/css';
+    return gulp.src(src)
+        //.pipe(debug({title: 'unicorn:'}))
+        .pipe(gulp.dest(dest));
+});
+
+gulp.task('images', function() {
+    var src = config.src + '/images/**';
+    var dest = config.dest + '/images';
+    return gulp.src(src)
+        // .pipe(debug({title: 'unicorn:'}))
+        .pipe(gulp.dest(dest));
+});
+
+gulp.task('icons', function() {
+    var src = './node_modules/font-awesome/**/*';
+    var dest = config.dest + '/vendor';
+    return gulp.src(src, {base: './node_modules'})
+        // .pipe(debug())
+        .pipe(gulp.dest(dest));
+});
+
+gulp.task('vendor', function() {
+    var src = ['./node_modules/bootstrap/dist/css/bootstrap.css'];
+    var dest = config.dest + '/vendor';
+    return gulp.src(src)
+        // .pipe(debug({title: 'unicorn:'}))
+        .pipe(gulp.dest(dest));
+});
+
+
+/**** gulp watch ***************************************************/
+gulp.task('watch', function() {
+    gulp.watch( './gulpfile.js', ['build']);
+    gulp.watch( config.src + "/javascript/*.js", [ 'app' ]);
+    gulp.watch( config.src + "/htdocs/**.html", [ 'htdocs' ]);
+    gulp.watch( config.src + "/images/**", [ 'images' ]);
+    gulp.watch( config.src + "/css/**", [ 'css' ]);
+});
+
+/***** gulp main tasks *********************************************/
+gulp.task('build', ['global', 'images', 'css', 'htdocs', 'app']);
+gulp.task('default', ['vendor', 'icons', 'build']);

+ 36 - 0
package.json

@@ -0,0 +1,36 @@
+{
+  "browser": {
+    "underscore": "./node_modules/backbone/node_modules/underscore",
+    "bootstrap": "./node_modules/bootstrap/dist/js/bootstrap.js"
+  },
+  "browserify": {
+    "transform": [
+      "browserify-shim"
+    ]
+  },
+  "browserify-shim": {
+    "bootstrap": [
+      "jquery:$"
+    ]
+  },
+  "devDependencies": {
+    "backbone-react-component": "~0.7.0",
+    "backbone-relational": "~0.9.0",
+    "browserify": "~8.0.0",
+    "browserify-shim": "^3.8.2",
+    "font-awesome": "^4.3.0",
+    "gulp": "~3.8.5",
+    "gulp-debug": "^2.0.0",
+    "gulp-rename": "~1.2.0",
+    "gulp-uglify": "~1.0.0",
+    "react": "~0.12.2",
+    "reactify": "~0.17.0",
+    "vinyl-buffer": "~1.0.0",
+    "vinyl-source-stream": "~1.0.0"
+  },
+  "dependencies": {
+    "backbone": "^1.1.2",
+    "bootstrap": "~3.1.0",
+    "jquery": "^2.1.3"
+  }
+}

+ 62 - 0
src/css/index.css

@@ -0,0 +1,62 @@
+
+.HolyGrail {
+  display: flex;
+  min-height: 100vh;
+  flex-direction: column;
+}
+
+.HolyGrail-header {
+    display: flex;
+    flex-direction: row;
+    order: -1;
+}
+
+.HolyGrail-body {
+    display: flex;
+    flex: 1;
+}
+
+.HolyGrail-content {
+    flex: 1 0 auto;
+}
+
+.HolyGrail-sidebar {
+    flex: 0 0 210px;
+    order: 0;
+}
+
+.HolyGrail-header {
+    background: #333;
+    border-bottom: 1px solid #d8d8d8;
+}
+
+.HolyGrail-sidebar {
+  border-radius: 3px;
+  background: #eeeeee;
+}
+
+.HolyGrail-header-brand {
+    flex: 0 0 10em;
+    text-align: center;
+    font-size: 1.5em;
+    padding: 0.5em 0;
+    /*color: #d8412f;*/
+    /*background-color: #d8d8d8;*/
+}
+
+body {
+    margin: 0;
+    background: #f5f5f5;
+}
+
+.toggle-sidebar {
+    font-size: 1.5em;
+    background-color: #e8e8e8;
+    padding: 2px;
+}
+
+.toggle-wrapper {
+    padding: 1em;
+    width: auto;
+    overflow: hidden;
+}

+ 29 - 0
src/htdocs/index.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  <link rel="stylesheet" href="./vendor/bootstrap.css">
+  <link rel="stylesheet" href="./vendor/font-awesome/css/font-awesome.css">
+  <link rel="stylesheet" href="css/index.css">
+</head>
+<body class="HolyGrail">
+
+  <header class="HolyGrail-header" role="banner">
+      <a href="../" class="HolyGrail-header-brand">Data Display</a>
+  </header>
+
+  <div class="HolyGrail-body">
+    <nav class="HolyGrail-sidebar">
+    ABC title
+    </nav>
+    <main class="HolyGrail-content">
+    <i class="fa fa-bars toggle-sidebar"></i>
+    Content
+    </main>
+  </div>
+
+  <!-- Include scripts at the end of document for faster loading -->
+  <script src="global.js"></script>
+  <script src="app.js"></script>
+</body>
+</html>

BIN
src/images/arrow_down.png


BIN
src/images/arrow_up.png


+ 202 - 0
src/javascript/app.js

@@ -0,0 +1,202 @@
+var React = require('react');
+var Backbone = require('backbone');
+var $ = jQuery = require('jquery');
+var _ = require('underscore');
+require('backbone-react-component');
+require('backbone-relational');
+Backbone.$ = $;
+
+var page = require('./page.js');
+
+var bootstrap = require('bootstrap');
+
+config = {
+    url: "http://katrin.kit.edu/newstatus/katrin/api/sql/",
+    server: "orca",
+};
+
+var ToggleContent = React.createClass({
+    render: function() {
+        var rightAlign = {
+            marginRight: '10px',
+            float: 'right'
+        }
+        var leftAlign = {
+            marginLeft: '10px',
+            float: 'left'
+        };
+        return (
+            <div className={'toggle-content'}>
+                <div><b style={leftAlign}>ID</b> <span style={rightAlign}>{this.props.id}</span></div><br/>
+                <div><b style={leftAlign}>IP</b> <span style={rightAlign}>{this.props.ip_address}</span></div><br/>
+                <div><b style={leftAlign}>Time to go</b> <span style={rightAlign}>{this.props.timeToGo}</span></div><br/>
+            </div>
+            );
+    }
+});
+
+var MyComponent = React.createClass({
+    mixins: [Backbone.React.Component.mixin],
+    getInitialSate: function() {
+        return {showMore: false}
+    },
+    onClick: function () {
+        this.setState({showMore: !this.state.showMore})
+    },
+    millisecondsToStr: function (milliseconds) {
+        // TIP: to find current time in milliseconds, use:
+        // var  current_time_milliseconds = new Date().getTime();
+
+        function numberEnding (number) {
+            return (number > 1) ? 's' : '';
+        }
+
+        var temp = Math.floor(milliseconds / 1000);
+        var years = Math.floor(temp / 31536000);
+        if (years) {
+            return years + ' year' + numberEnding(years);
+        }
+        //TODO: Months! Maybe weeks? 
+        var days = Math.floor((temp %= 31536000) / 86400);
+        if (days) {
+            return days + ' day' + numberEnding(days);
+        }
+        var hours = Math.floor((temp %= 86400) / 3600);
+        if (hours) {
+            return hours + ' hour' + numberEnding(hours);
+        }
+        var minutes = Math.floor((temp %= 3600) / 60);
+        if (minutes) {
+            return minutes + ' minute' + numberEnding(minutes);
+        }
+        var seconds = temp % 60;
+        if (seconds) {
+            return seconds + ' second' + numberEnding(seconds);
+        }
+        return 'less than a second'; //'just now' //or other string you like;
+    },
+    render: function () { 
+        var arrowStyle = {
+            display: 'block',
+            marginLeft: 'auto',
+            marginRight: 'auto',
+            width: '20px'
+        };
+        var rightAlign = {
+            marginRight: '10px',
+            float: 'right'
+        }
+        var leftAlign = {
+            marginLeft: '10px',
+            float: 'left'
+        };
+        return (
+            <div>
+            <h4>{this.props.name}</h4>
+            <div><b style={leftAlign}>Experiment</b> <span style={rightAlign}>{this.props.experiment}</span></div><br/>
+            <div><b style={leftAlign}>State</b> <span style={rightAlign}>{this.props.state ? 'Running' : 'Stopped'}</span></div><br/>
+            <div><b style={leftAlign}>Run time</b> <span style={rightAlign}>{this.millisecondsToStr(this.props.uptime*1000)}</span></div><br/>
+            { this.state.showMore 
+                ? <div><ToggleContent {...this.props} /> <img src="./images/arrow_up.png" alt="toggle" style={arrowStyle} onClick={this.onClick} /></div>
+                : <img src="./images/arrow_down.png" alt="toggle" style={arrowStyle} onClick={this.onClick} /> }
+            </div>);
+    }
+});
+
+var Run = Backbone.RelationalModel.extend({
+    parse: function(resp) {
+        if (typeof resp.data === 'undefined')
+            return resp;
+        else
+            return resp.data[0];
+    }
+});
+
+var Runs = Backbone.Collection.extend({
+    model: Run,
+    parse: function(resp) {
+        return resp.data;
+    }
+});
+
+var Machine = Backbone.Model.extend({
+    parse: function(resp) {
+        if (typeof resp.data === 'undefined')
+            return resp;
+        else
+            return resp.data[0];
+    }
+});
+
+var Machines = Backbone.Collection.extend({
+    model: Machine,
+    parse: function(resp) {
+        return resp.data;
+    },
+
+});
+
+
+function setup0() {
+    machines.fetch({
+        error: function(c) {
+            console.log('oops');
+        },
+        success: function(c) {
+            _.each(c.models, function(model, key) {
+                var _id = 'block' + (++key);
+                React.render(<MyComponent model={model} />, document.getElementById(_id));    
+            });
+        }
+    });
+}
+
+function setup() {
+    $.when( machines.fetch(), runs.fetch()).then( function(){
+        var run_info = {}
+        _.each(runs.models, function(run) {
+            var run = run.attributes;
+            run_info[run.machine_id] = run;
+        });
+        _.each(machines.models, function(machine, key) {
+            var run = run_info[machine.attributes.machine_id];
+            _.extend(machines.models[key].attributes, run);
+            React.render(<MyComponent model={machines.models[key]} />, document.getElementById('block'+(key+1)));
+        });
+    });
+}
+
+function update() {
+    $.when( machines.fetch(), runs.fetch()).then( function(){
+        var run_info = {}
+        _.each(runs.models, function(run) {
+            run_info[run.machine_id] = run.attributes;
+        });
+        _.each(machines.models, function(machine, key) {
+            var run = run_info[machine.attributes.machine_id];
+            _.extend(machines.models[key].attributes, run);
+        });
+
+    });
+}
+
+var url = config.url + config.server + "/";
+
+machines = new Machines({});
+machines.url = url+'machines';
+
+runs = new Runs({});
+runs.url = url + 'runs';
+
+//setup0();
+setup();
+
+
+setInterval(function() {
+    update();
+}, 10000);
+
+$(document).ready(function() {
+    console.log('ready'); 
+    page.toggleSidebar();
+});

+ 1 - 0
src/javascript/global.js

@@ -0,0 +1 @@
+console.log('global.js loaded!');

+ 17 - 0
src/javascript/page.js

@@ -0,0 +1,17 @@
+var $ = require('jquery');
+
+module.exports = {
+	toggleSidebar: function() {
+		$('.toggle-sidebar').click(function() {
+			var v = $('.HolyGrail-sidebar').css('visibility');
+			if (v === 'visible') {
+				$('.HolyGrail-sidebar').css('visibility', 'collapse');
+				//$('.toggle-wrapper').css('width', '0');
+			} else {
+				$('.HolyGrail-sidebar').css('visibility', 'visible');
+				//$('.toggle-wrapper').css('width', '200');
+			}
+			console.log(v);
+		});		
+	}
+};