Choose color scheme

About the Author

478 Posts By ben

  • Shelving

    We were in such dire need of storage I didn’t even get to finish the shelves before they were filled. Having custom made shelves is amazingly efficient. I even cut around the 2x4s of the wall behind to gain 3.5″ which is a lot of room in a small house. The finish work is small and will likely happen in the next week to 30 years.

    IMG_1980

  • Looks like there was an attempt at replacing me with a robot

    Following the robot son, an attempt was made to replace me, Dad, with a robot.IMG_1926

  • All right, all right, the stupid septic is in

    State mandated expense of $5500, I’ll avoid bitching too much about it but it’s hard to swallow for the expense and the lifestyle it enforces.

    Concentrate excretions in a huge tank buried where no one can see it.IMG_0002

    Scar your land with giant trenches dug by an excavator the size of your house.IMG_4957

    Replace your great soil with stones and plastic to process a few gallons of effluent. IMG_0010 IMG_0023

    Thankfully, Vermont abounds with great people. I rode the bucket down the tank pit 😀 I wouldn’t say that the ride was worth $5500 but maybe if I could drive the excavator a bit we’d be there. I got to decrease my bill and learn the ins and outs of my septic system by working alongside Eric & E.J.. I hope people like them never go away.

    IMG_0035

  • Adding collaborative editing to the Ace web code editor with web sockets

    Using Ace‘s excellent API, it is relatively easy to enhance it to allow for live collaborative editing.

    The gist of what we’re doing here is to use Ace’s API for extracting and applying delta when changes occur in the editor. Then we simply transmit them over a websocket that all clients are connected to. This example is functional but in no way comprehensive to what a full code editing collaboration could be. It’s meant to be simple thus understandable. It’s a great starting point for whatever other pieces of functionality you want to send across web sockets.

    Loading Ace in a webpage with some custom Javascript

    This is what your web page looks like, load Ace as instructed and add Javascript to handle interaction with the websocket server.

    <!DOCTYPE html>
    <html lang="en">
        <head>
    
            <title>Collaborative Ace Coding!</title>
    
            <style type="text/css" media="screen">
                #editor { 
                    position: absolute;
                    top: 0;
                    right: 0;
                    bottom: 0;
                    left: 0;
                }
            </style>
    
            <script src="https://<?=$_SERVER['HTTP_HOST']?>:1337/socket.io/socket.io.js"></script>
            <script src="ace-builds/src/ace.js" type="text/javascript" charset="utf-8"></script>
            <script src="ace-builds/src/ext-language_tools.js"></script>
            <script>
                var session_id = null ;
                var editor = null ;
                var collaborator = null ;
                var buffer_dumped = false ;
                var last_applied_change = null ;
                var just_cleared_buffer = null ;
    
                function Collaborator( session_id ) {
                    this.collaboration_socket = io.connect( "https://code.thayer.dartmouth.edu:1337", {query:'session_id=' + session_id} ) ;
    
                    this.collaboration_socket.on( "change", function(delta) {
                        delta = JSON.parse( delta ) ;
                        last_applied_change = delta ;
                        editor.getSession().getDocument().applyDeltas( [delta] ) ;
                    }.bind() ) ;
    
                    this.collaboration_socket.on( "clear_buffer", function() {
                        just_cleared_buffer = true ;
                        console.log( "setting editor empty" ) ;
                        editor.setValue( "" ) ;
                    }.bind() ) ;
                }
    
                Collaborator.prototype.change = function( delta ) {
                    this.collaboration_socket.emit( "change", delta ) ;
                }
    
                Collaborator.prototype.clear_buffer = function() {
                    this.collaboration_socket.emit( "clear_buffer" ) ;
                }
    
                Collaborator.prototype.dump_buffer = function() {
                    this.collaboration_socket.emit( "dump_buffer" ) ;
                }
    
                function body_loaded() {
    
                    session_id = "meow" ;
    
                    editor = ace.edit( "editor" ) ;
                    collaborator = new Collaborator( session_id ) ;
                    
    
                    // registering change callback
                    editor.on( "change", function( e ) {
                        // TODO, we could make things more efficient and not likely to conflict by keeping track of change IDs
                        if( last_applied_change!=e && !just_cleared_buffer ) {
                            collaborator.change( JSON.stringify(e) ) ;
                        }
                        just_cleared_buffer = false ;
                    }, false );
    
                    editor.setTheme( "ace/theme/monokai") ;
                    editor.$blockScrolling = Infinity ;
    
                    collaborator.dump_buffer() ;
    
                    document.getElementsByTagName('textarea')[0].focus() ;
                    last_applied_change = null ;
                    just_cleared_buffer = false ;
                }
            </script>
        </head>
    
        <body onLoad="body_loaded()">
            <div id="editor"></div>
        </body>
    </html>
    

    Parallel to this, run the following Node.js server script

    Following is the Node.js websocket server which must be instantiated on the same server serving the web page above. It needs to be up for the page above to work.

    1. Make sure to have port 1337 open in the same capacity as ports 80 & 443, this is what this listens on.
    2. Make sure to update the paths to SSL certs, we use SSL on the websocket server. We do SSL here so browsers can run the websocket Javascript regardless of whether their original context it SSL or not.
    3. You need to have Socket.IO installed
    // config variables
    verbose = false ;
    session_directory = "/tmp" ; // it has to exist
    
    /* https specific */
    var https = require('https'),
        fs =    require('fs');
    
    var options = {
        key:    fs.readFileSync('/path/to/your/ssl.key'),
        cert:   fs.readFileSync('/path/to/your/ssl.crt'),
        ca:     fs.readFileSync('/path/to/your/CA.crt')
    };
    var app = https.createServer(options);
    io = require('socket.io').listen(app);     //socket.io server listens to https connections
    app.listen(1337, "0.0.0.0");
    
    // will use the following for file IO
    var fs = require( "fs" ) ;
    
    //io = require('socket.io').listen(2015) ;
    if( verbose ) { console.log( "> server launched" ) ; }
    
    collaborations = [] ;
    socket_id_to_session_id = [] ;
    
    io.sockets.on('connection', function(socket) {
        var session_id = socket.manager.handshaken[socket.id].query['session_id'] ;
    
        socket_id_to_session_id[socket.id] = session_id ;
    
        if( verbose ) { console.log( session_id + " connected on socket " + socket.id ) ; }
    
    
        if( !(session_id in collaborations) ) {
            // not in memory but is is on the filesystem?
            if( file_exists(session_directory + "/" + session_id) ) {
                if( verbose ) { console.log( "   session terminated previously, pulling back from filesystem" ) ; }
                var data = read_file( session_directory + "/" + session_id ) ;
                if( data!==false ) {
                    collaborations[session_id] = {'cached_instructions':JSON.parse(data), 'participants':[]} ;
                } else {
                    // something went wrong, we start from scratch
                    collaborations[session_id] = {'cached_instructions':[], 'participants':[]} ;
                }
            } else {
                if( verbose ) { console.log( "   creating new session" ) ; }
                collaborations[session_id] = {'cached_instructions':[], 'participants':[]} ;
            }
        }
        collaborations[session_id]['participants'].push( socket.id ) ;
    
    
        socket.on('change', function( delta ) {
            if( verbose ) { console.log( "change " + socket_id_to_session_id[socket.id] + " " + delta ) ; }
            if( socket_id_to_session_id[socket.id] in collaborations ) {
                collaborations[socket_id_to_session_id[socket.id]]['cached_instructions'].push( ["change", delta, Date.now()] ) ;
                for( var i=0 ; i<collaborations[session_id]['participants'].length ; i++ ) {
                    if( socket.id!=collaborations[session_id]['participants'][i] ) {
                        io.sockets.socket(collaborations[session_id]['participants'][i]).emit( "change", delta ) ;
                    }
                }
            } else {
                if( verbose ) { console.log( "WARNING: could not tie socket_id to any collaboration" ) ; }
            }
        });
    
    
        socket.on('change_selection', function( selections ) {
            if( verbose ) { console.log( "change_selection " + socket_id_to_session_id[socket.id] + " " + selections ) ; }
            if( socket_id_to_session_id[socket.id] in collaborations ) {
                for( var i=0 ; i<collaborations[session_id]['participants'].length ; i++ ) {
                    if( socket.id!=collaborations[session_id]['participants'][i] ) {
                        io.sockets.socket(collaborations[session_id]['participants'][i]).emit( "change_selection", selections ) ;
                    }
                }
            } else {
                if( verbose ) { console.log( "WARNING: could not tie socket_id to any collaboration" ) ; }
            }
        });
    
    
        socket.on('clear_buffer', function() {
            if( verbose ) { console.log( "clear_buffer " + socket_id_to_session_id[socket.id] ) ; }
            if( socket_id_to_session_id[socket.id] in collaborations ) {
                collaborations[socket_id_to_session_id[socket.id]]['cached_instructions'] = [] ;
                for( var i=0 ; i<collaborations[session_id]['participants'].length ; i++ ) {
                    if( socket.id!=collaborations[session_id]['participants'][i] ) {
                        io.sockets.socket(collaborations[session_id]['participants'][i]).emit( "clear_buffer" ) ;
                    }
                }
            } else {
                if( verbose ) { console.log( "WARNING: could not tie socket_id to any collaboration" ) ; }
            }
        });
    
    
        socket.on('dump_buffer', function() {
            if( verbose ) { console.log( "dump_buffer " + socket_id_to_session_id[socket.id] ) ; }
            if( socket_id_to_session_id[socket.id] in collaborations ) {
                for( var i=0 ; i<collaborations[socket_id_to_session_id[socket.id]]['cached_instructions'].length ; i++ ) {
                    socket.emit( collaborations[socket_id_to_session_id[socket.id]]['cached_instructions'][i][0], collaborations[socket_id_to_session_id[socket.id]]['cached_instructions'][i][1] ) ;
                }
            } else {
                if( verbose ) { console.log( "WARNING: could not tie socket_id to any collaboration" ) ; }
            }
            socket.emit( "buffer_dumped" ) ;
        });
    
    
        socket.on('disconnect', function () {
            console.log( socket_id_to_session_id[socket.id] + " disconnected" ) ;
            var found_and_removed = false ;
            if( socket_id_to_session_id[socket.id] in collaborations ) {
                //var index = collaborations[socket_id_to_session_id[socket.id]].participants.indexOf( socket.id ) ;
                var index = collaborations[socket_id_to_session_id[socket.id]]['participants'].indexOf( socket.id ) ;
                if( index>-1 ) {
                    //collaborations[socket_id_to_session_id[socket.id]].participants.splice( index, 1 ) ;
                    collaborations[socket_id_to_session_id[socket.id]]['participants'].splice( index, 1 ) ;
                    found_and_removed = true ;
                    //if( collaborations[socket_id_to_session_id[socket.id]].participants.length==0 ) {
                    if( collaborations[socket_id_to_session_id[socket.id]]['participants'].length==0 ) {
                        if( verbose ) { console.log( "last participant in collaboration, committing to disk & removing from memory" ) ; }
                        // no one is left in this session, we commit it to disk & remove it from memory
                        write_file( session_directory + "/" + socket_id_to_session_id[socket.id], JSON.stringify(collaborations[socket_id_to_session_id[socket.id]]['cached_instructions']) ) ;
                        delete collaborations[socket_id_to_session_id[socket.id]] ;
                    }
                }
            }
            if( !found_and_removed ) {
                console.log( "WARNING: could not tie socket_id to any collaboration" ) ;
            }
            console.log( collaborations ) ;
        });
    
    });
    
    
    function write_file( path, data ) {
        try {
            fs.writeFileSync( path, data ) ;
            return true ;
        } catch( e ) {
            return false ;
        }
    }
    
    
    function read_file( path ) {
        try {
            var data = fs.readFileSync( path ) ;
            return data ;
        } catch( e ) {
            return false
        }
    }
    
    
    function file_exists( path ) {
        try {
            stats = fs.lstatSync( path ) ;
            if (stats.isFile()) {
                return true ;
            }
        } catch( e ) {
            return false ;
        }
        // we should not reach that point
        return false ;
    }
    
  • Cool Duplo Project #37 – Vertical Axis Wind Spinner Failed Prototype

    The tower is cool but not super stable and the “spoons” are very heavy to move. Trial outside was a failure.IMG_1991

  • Gave the Sweet Heart a thorough clean up

    It served us very well throughout the Winter but it was hard to find a time to clean it up because it was always hot.IMG_0101 1

  • Cool Duplo Project #35 – A New Son

    Often times my son behaves like a little brat; when he does, I threaten we’ll return him to the store or replace him with a robot child that doesn’t whine all the time.

    We have the technology…

    IMG_4894IMG_4896Ok this is just getting creepy, I’ve watched enough horror movies to know how this ends. Let’s do shorts instead, seeing the Duplos will help me not freak the fuck out if I get up in the middle of the night.

    IMG_4900

    My robots are anatomically accurateIMG_4898

    Sure as hell doesn’t whineIMG_4903

    The tense confrontationIMG_4911

  • Baby Robins are doing great

    I don’t see the third one but I didn’t want to intrude too much.IMG_4892