<commit_before>function leave_room(){
    socket.emit('left', {}, function() {
        socket.disconnect();
        // go back to the login page
        window.location.href = "/";
    });
}

function send_msg(msg){
    msg = msg.trim();
    if(msg != ''){
        $('#text').val('');
        msg = Aes.Ctr.encrypt(msg, Cookies.get('password'), 256)
        socket.emit('text', {'msg': msg});
    }
}

function filter_msg(msg){
    msg = msg.trim();
    msg = filter_code(msg);
    msg = filter_href(msg);
    return msg;
}

function filter_code(msg){
    if(msg.substring(0,5)=='/code'){
        msg = '
' + msg.substring(5) + '
';
    }
    return msg;
}

function filter_href(msg){
    geturl = new RegExp(
        "(^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))",
        "g");
    if(msg.match(geturl) && msg.match(geturl).length){
        msg.match(geturl).forEach(function(_url){
            msg = msg.replace(_url, ''+_url+'')
            ext = _url.slice(-4)
            if(ext=='jpeg' || ext=='.jpg' || ext=='.gif' || ext=='.png'){
                msg = msg + '
';
            }
        });
    }
    return msg;
}

function spawnNotification(theBody,theIcon,theTitle) {
    if( ! window.onfocus ){
        var options = {
          body: theBody,
          icon: theIcon
        }
        var n = new Notification(theTitle,options);
        setTimeout(n.close.bind(n), 5000);
    }
}

function store_message(data){
    data_string = (JSON.stringify(data));
    room_key = 'room_' + data.room_key;
    chat_room_data = JSON.parse(localStorage.getItem(room_key));
    if(chat_room_data==null){
        chat_room_data = [];
    }
    chat_room_data.push(data_string);
    localStorage.setItem(room_key, JSON.stringify(chat_room_data));
}

function build_local_data(room_name){
    room_key = 'room_' + room_name;
    // localStorage.setItem(room_key, '[]');
    chat_room_data = JSON.parse(localStorage.getItem(room_key));
    if(chat_room_data == null){
        empty_array = [];
        localStorage.setItem(room_key, JSON.stringify(empty_array));
        return;
    }
    for(chat of chat_room_data){
        c = JSON.parse(chat);
        write_msg(c);
    }
}

function write_msg(msg_obj){
    unencrypted_msg = Aes.Ctr.decrypt(msg_obj.msg, Cookies.get('password'), 256);
    filtered_msg = filter_msg( unencrypted_msg );
    $(msg_obj.tpl).insertBefore('#chat li:last');
    new_msg_li = $('#chat li:nth-last-child(2)');
    new_msg_li.find('.msg_content').html(filtered_msg);
    new_msg_user = new_msg_li.attr('data-user');

    // check if the last message was the same person and recent
    previous_message_li = $('#chat li:nth-last-child(3)');
    previous_message_type = previous_message_li.attr('data-type');
    append_to_last = false;
    if(
         previous_message_li.attr('data-user') == new_msg_user
        &&
        previous_message_type == 'msg')
        {
        new_msg_li.find('.user_image_container').find('img').hide();
        previous_message_time = new Date(previous_message_li.attr('data-date'));
        diff = Math.round(Math.abs((new Date() - previous_message_time) / 1000));
        if(diff < 120){
            append_to_last = true;
        }
    }

    $('#chat').scrollTop($('#chat')[0].scrollHeight);
    if( previous_message_type == 'msg'){
    if(append_to_last == true){
            new_msg_li.find('h3').hide();
            new_msg_li.find('.msg_date').hide();
            new_msg_li.addClass('no_break_above');
            previous_message_li.addClass('no_break_below');
        }
    }
    msg_pretty_time = pretty_time_now(msg_obj.sent)
    new_msg_li.find('.msg_date').text(msg_pretty_time);
    if(Cookies.get('user_name')==new_msg_user){
        new_msg_li.addClass('msg_me');
    }
}

function pretty_time_now(msg_time){
    var now = new Date();
    diff = Math.round(Math.abs( now - msg_time) / 1000);

    msg_pretty_time = false;
    if(diff < 20){
        msg_pretty_time = 'seconds ago';
    }else if(diff < 60){
        msg_pretty_time = 'less then a minute ago'
    } else if(diff < 3600){
        minutes = Math.round(diff / 60);
        if(minutes == 1){
            msg_pretty_time = '1 minute ago';
        } else {
            msg_pretty_time = minutes + ' mintues ago';
        }
    } else {
        msg_pretty_time = 'just now';
    }
    return msg_pretty_time;
}

function settings_update(){
    $('.setting-update').click(function(){
        setting_name = $(this).attr('name');
        if(setting_name=='clear_local'){
            localStorage.clear();
        }
    });
}

function lock_console(){
    $('#settings_btn').fadeOut();
    $('#chat_window').fadeOut();
    $('#lock_status').removeClass('fa-unlock-alt').addClass('fa-lock');
    $('#repassword_container').fadeIn(1500);
    Cookies.set('password', '');
    Cookies.set('terminal', 'locked');
}

function unlock_console(password){
    console.log('lets open this bitch');
    Cookies.set('password', password);
    build_local_data(Cookies.get('room_name'));
    $('#repassword_container').fadeOut(1500);
    $('#settings_btn').fadeIn();
    $('#chat_window').fadeIn();
    Cookies.set('terminal', 'open');
}

var socket;
var CHATSEC = CHATSEC || (function(){

    return {
        init : function(Args) {
            _args = Args;

            $('#room_name').html(':: ' + Cookies.set('room_name'));

            Notification.requestPermission().then(function(result) {
                console.log(result);
            });

        },
        launch : function(){
            console.log('check to see if they have a password');
            if(Cookies.get('password')==''){
                window.location = '/';
            }

            $(document).ready(function(){
                $('.typing').hide();
                $("#textbox").focus();
                build_local_data(Cookies.get('room_name'));

                // lockout
                var timeoutTime = 300000;
                var timeoutTimer = setTimeout(lock_console, timeoutTime);
                $('body').bind('mousedown mousemove keydown', function(event) {
                    timeoutTimer = setTimeout(lock_console, timeoutTime);
                });
                $('.cs_login').keypress(function(e) {
                    var code = e.keyCode || e.which;
                    if (code == 13) {
                        unlock_console($(this).val());
                        $(this).val('');
                        // set_login_creds($(this));
                    }
                });

                // Sockets
                socket = io.connect(window.location.protocol + '//' + document.domain + ':' + location.port + '/chat');

                socket.on('connect', function() {
                    socket.emit('joined', {});
                });

                socket.on('status', function(data) {
                    $(data.tpl).insertBefore('#chat li:last');
                    $('#chat').scrollTop($('#chat')[0].scrollHeight);
                });

                // Reciving user currently typing
                socket.on('typing', function(data) {
                    if(Cookies.get('user_name') != data.username ){
                        $('.typing').find('.typing_avatar').attr(
                            'src',
                            '/static/imgs/avatars/black/' + data.avatar
                        );
                        $('.typing').find('h3').text(data.username);
                        $('.typing').show().delay(750).fadeOut();
                        $('#chat').scrollTop($('#chat')[0].scrollHeight);
                    }
                });

                // Recieving a message
                socket.on('message', function(data) {
                    write_msg(data);

                    if(Cookies.get('user_name') != data.user_name ){
                        var audio = new Audio('/static/audio/new_msg.mp3');
                        audio.play();
                        spawnNotification(
                            filtered_msg,
                            'http://www.google.com/',
                            'ChatSec - ' + data.user_name );
                    }
                    store_message(data);
                    console.log('here');
                });

                // Sending a message
                $('#textbox').keypress(function(e) {
                    var code = e.keyCode || e.which;
                    if (code == 13) {
                        message = $('#textbox').val();
                        send_msg(message);
                        $('#textbox').val('');
                    } else {
                        socket.emit('typing', {'msg': 'chatsec-user-typing'});
                    }
                });

                $('#send').click(function(e){
                    send_msg($('#textbox').val());
                });

                // @todo: make t his actually work or kill it
                $(window).on('beforeunload', function(){
                    socket.emit('left', {}, function() {
                        socket.disconnect();
                    });
                });

                // Timer Scripts
                window.setInterval(function(){
                    // Update msg time
                    $("#chat li").each(function( index ) {
                        var sent_time = new Date( $(this).attr('data-date') );
                        msg_pretty_time = pretty_time_now(sent_time)
                        if(msg_pretty_time){
                            $(this).find('.msg_date').text(msg_pretty_time);
                        }
                    });
                    // Highlight code blocks
                    $('pre').each(function(i, block) {
                        // hljs.highlightBlock(block);
                    });
                }, 10000);
            });
        }
    };
}());<commit_msg>Removing notifications<commit_after>function leave_room(){
    socket.emit('left', {}, function() {
        socket.disconnect();
        // go back to the login page
        window.location.href = "/";
    });
}

function send_msg(msg){
    msg = msg.trim();
    if(msg != ''){
        $('#text').val('');
        msg = Aes.Ctr.encrypt(msg, Cookies.get('password'), 256)
        socket.emit('text', {'msg': msg});
    }
}

function filter_msg(msg){
    msg = msg.trim();
    msg = filter_code(msg);
    msg = filter_href(msg);
    return msg;
}

function filter_code(msg){
    if(msg.substring(0,5)=='/code'){
        msg = '
' + msg.substring(5) + '
';
    }
    return msg;
}

function filter_href(msg){
    geturl = new RegExp(
        "(^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))",
        "g");
    if(msg.match(geturl) && msg.match(geturl).length){
        msg.match(geturl).forEach(function(_url){
            msg = msg.replace(_url, ''+_url+'')
            ext = _url.slice(-4)
            if(ext=='jpeg' || ext=='.jpg' || ext=='.gif' || ext=='.png'){
                msg = msg + '
';
            }
        });
    }
    return msg;
}

function spawnNotification(theBody,theIcon,theTitle) {
    if( ! window.onfocus ){
        var options = {
          body: theBody,
          icon: theIcon
        }
        var n = new Notification(theTitle,options);
        setTimeout(n.close.bind(n), 5000);
    }
}

function store_message(data){
    data_string = (JSON.stringify(data));
    room_key = 'room_' + data.room_key;
    chat_room_data = JSON.parse(localStorage.getItem(room_key));
    if(chat_room_data==null){
        chat_room_data = [];
    }
    chat_room_data.push(data_string);
    localStorage.setItem(room_key, JSON.stringify(chat_room_data));
}

function build_local_data(room_name){
    room_key = 'room_' + room_name;
    // localStorage.setItem(room_key, '[]');
    chat_room_data = JSON.parse(localStorage.getItem(room_key));
    if(chat_room_data == null){
        empty_array = [];
        localStorage.setItem(room_key, JSON.stringify(empty_array));
        return;
    }
    for(chat of chat_room_data){
        c = JSON.parse(chat);
        write_msg(c);
    }
}

function write_msg(msg_obj){
    unencrypted_msg = Aes.Ctr.decrypt(msg_obj.msg, Cookies.get('password'), 256);
    filtered_msg = filter_msg( unencrypted_msg );
    $(msg_obj.tpl).insertBefore('#chat li:last');
    new_msg_li = $('#chat li:nth-last-child(2)');
    new_msg_li.find('.msg_content').html(filtered_msg);
    new_msg_user = new_msg_li.attr('data-user');

    // check if the last message was the same person and recent
    previous_message_li = $('#chat li:nth-last-child(3)');
    previous_message_type = previous_message_li.attr('data-type');
    append_to_last = false;
    if(
         previous_message_li.attr('data-user') == new_msg_user
        &&
        previous_message_type == 'msg')
        {
        new_msg_li.find('.user_image_container').find('img').hide();
        previous_message_time = new Date(previous_message_li.attr('data-date'));
        diff = Math.round(Math.abs((new Date() - previous_message_time) / 1000));
        if(diff < 120){
            append_to_last = true;
        }
    }

    $('#chat').scrollTop($('#chat')[0].scrollHeight);
    if( previous_message_type == 'msg'){
    if(append_to_last == true){
            new_msg_li.find('h3').hide();
            new_msg_li.find('.msg_date').hide();
            new_msg_li.addClass('no_break_above');
            previous_message_li.addClass('no_break_below');
        }
    }
    msg_pretty_time = pretty_time_now(msg_obj.sent)
    new_msg_li.find('.msg_date').text(msg_pretty_time);
    if(Cookies.get('user_name')==new_msg_user){
        new_msg_li.addClass('msg_me');
    }
}

function pretty_time_now(msg_time){
    var now = new Date();
    diff = Math.round(Math.abs( now - msg_time) / 1000);

    msg_pretty_time = false;
    if(diff < 20){
        msg_pretty_time = 'seconds ago';
    }else if(diff < 60){
        msg_pretty_time = 'less then a minute ago'
    } else if(diff < 3600){
        minutes = Math.round(diff / 60);
        if(minutes == 1){
            msg_pretty_time = '1 minute ago';
        } else {
            msg_pretty_time = minutes + ' mintues ago';
        }
    } else {
        msg_pretty_time = 'just now';
    }
    return msg_pretty_time;
}

function settings_update(){
    $('.setting-update').click(function(){
        setting_name = $(this).attr('name');
        if(setting_name=='clear_local'){
            localStorage.clear();
        }
    });
}

function lock_console(){
    console.log('locking');
    $('#settings_btn').fadeOut();
    $('#chat_window').fadeOut();
    $('#lock_status').removeClass('fa-unlock-alt').addClass('fa-lock');
    $('#repassword_container').fadeIn(1500);
    Cookies.set('password', '');
    Cookies.set('terminal', 'locked');
}

function unlock_console(password){
    console.log('lets open this bitch');
    Cookies.set('password', password);
    build_local_data(Cookies.get('room_name'));
    $('#repassword_container').fadeOut(1500);
    $('#settings_btn').fadeIn();
    $('#chat_window').fadeIn();
    Cookies.set('terminal', 'open');
}

var socket;
var CHATSEC = CHATSEC || (function(){

    return {
        init : function(Args) {
            _args = Args;

            $('#room_name').html(':: ' + Cookies.set('room_name'));

            // Notification.requestPermission().then(function(result) {
            //     console.log(result);
            // });

        },
        launch : function(){
            if(Cookies.get('password')==''){
                window.location = '/';
            }

            $(document).ready(function(){
                $('.typing').hide();
                $("#textbox").focus();

                $(document).bind("idle.idleTimer", function(){
                 // function you want to fire when the user goes idle
                    console.log('hey did it, he left!');
                });

                build_local_data(Cookies.get('room_name'));

                $('.cs_login').keypress(function(e) {
                    var code = e.keyCode || e.which;
                    if (code == 13) {
                        unlock_console($(this).val());
                        $(this).val('');
                        timeoutTime = 0;
                        // set_login_creds($(this));
                    }
                });

                // Sockets
                socket = io.connect(window.location.protocol + '//' + document.domain + ':' + location.port + '/chat');

                socket.on('connect', function() {
                    socket.emit('joined', {});
                });

                socket.on('status', function(data) {
                    $(data.tpl).insertBefore('#chat li:last');
                    $('#chat').scrollTop($('#chat')[0].scrollHeight);
                });

                // Reciving user currently typing
                socket.on('typing', function(data) {
                    if(Cookies.get('user_name') != data.username ){
                        $('.typing').find('.typing_avatar').attr(
                            'src',
                            '/static/imgs/avatars/black/' + data.avatar
                        );
                        $('.typing').find('h3').text(data.username);
                        $('.typing').show().delay(750).fadeOut();
                        $('#chat').scrollTop($('#chat')[0].scrollHeight);
                    }
                });

                // Recieving a message
                socket.on('message', function(data) {
                    write_msg(data);

                    if(Cookies.get('user_name') != data.user_name ){
                        var audio = new Audio('/static/audio/new_msg.mp3');
                        audio.play();
                        // spawnNotification(
                        //     filtered_msg,
                        //     'http://www.google.com/',
                        //     'ChatSec - ' + data.user_name );
                    }
                    store_message(data);
                });

                // Sending a message
                $('#textbox').keypress(function(e) {
                    var code = e.keyCode || e.which;
                    if (code == 13) {
                        message = $('#textbox').val();
                        send_msg(message);
                        $('#textbox').val('');
                    } else {
                        socket.emit('typing', {'msg': 'chatsec-user-typing'});
                    }
                });

                $('#send').click(function(e){
                    send_msg($('#textbox').val());
                });

                // @todo: make t his actually work or kill it
                $(window).on('beforeunload', function(){
                    socket.emit('left', {}, function() {
                        socket.disconnect();
                    });
                });

                // Timer Scripts
                window.setInterval(function(){
                    // Update msg time
                    $("#chat li").each(function( index ) {
                        var sent_time = new Date( $(this).attr('data-date') );
                        msg_pretty_time = pretty_time_now(sent_time)
                        if(msg_pretty_time){
                            $(this).find('.msg_date').text(msg_pretty_time);
                        }
                    });

                    // Highlight code blocks
                    $('pre').each(function(i, block) {
                        // hljs.highlightBlock(block);
                    });
                }, 1000);
            });
        }
    };
}());<|endoftext|>


<commit_before>

var app =  angular.module('WordcountApp', [])

  app.controller('WordcountController', ['$scope', '$log', '$http', '$timeout',
    function($scope, $log, $http, $timeout) {

    $scope.submitButtonText = 'Submit';
    $scope.loading = false;
    $scope.urlerror = false;

    $scope.getResults = function() {

      $log.log('test');

      // get the URL from the input
      var userInput = $scope.myFile;

      // fire the API request
      var fd = new FormData();
        fd.append('file', userInput);
        $http.post('/upload', fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .success(function(results) {
          $log.log(results);
          getWordCount(results);
          $scope.wordcounts = null;
          $scope.loading = true;
          $scope.submitButtonText = 'Loading...';
          $scope.urlerror = false;
        })
        .error(function(error) {
          $log.log(error);
        });

    };

    function getWordCount(results) {

      var timeout = '';

      var poller = function(jobID) {
        // fire another request
        $http.get('/results/'+jobID).
          success(function(data, status, headers, config) {
            if(status === 202) {
              $log.log(data, status);
            } else if (status === 200){
              $log.log(data);
              $scope.loading = false;
              $scope.submitButtonText = "Submit";
              $scope.wordcounts = $scope.wordcounts.concat(data);
              $timeout.cancel(timeout);
              return false;
            }
            // continue to call the poller() function every 2 seconds
            // until the timeout is cancelled
            timeout = $timeout(poller, 2000);
          }).
          error(function(error) {
            $log.log(error);
            $scope.loading = false;
            $scope.submitButtonText = "Submit";
            $scope.urlerror = true;
          });
      };

      angular.forEach(Object.keys(results), poller());

    }

  }])

  app.directive('wordCountChart', ['$parse', function ($parse) {
    return {
      restrict: 'E',
      replace: true,
      template: '

',
      link: function (scope) {
        scope.$watch('wordcounts', function() {
          d3.select('#chart').selectAll('*').remove();
          var data = scope.wordcounts;
          for (var word in data) {
            d3.select('#chart')
              .append('div')
              .selectAll('div')
              .data(word[0])
              .enter()
              .append('div')
              .style('width', function() {
                return (data[word] * 20) + 'px';
              })
              .text(function(d){
                return word;
              });
          }
        }, true);
      }
     };
  }]);

  app.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);
<commit_msg>not working, debugging<commit_after>

var app =  angular.module('WordcountApp', [])

  app.controller('WordcountController', ['$scope', '$log', '$http', '$timeout',
    function($scope, $log, $http, $timeout) {

    $scope.submitButtonText = 'Submit';
    $scope.loading = false;
    $scope.urlerror = false;

    $scope.getResults = function() {

      $log.log('test');

      // get the URL from the input
      var userInput = $scope.myFile;

      // fire the API request
      var fd = new FormData();
        fd.append('file', userInput);
        $http.post('/upload', fd, {
            transformRequest: angular.identity,
            headers: {'Content-Type': undefined}
        })
        .success(function(results) {
          $log.log(results);
          getWordCount(results);
          $scope.wordcounts = null;
          $scope.loading = true;
          $scope.submitButtonText = 'Loading...';
          $scope.urlerror = false;
        })
        .error(function(error) {
          $log.log(error);
        });

    };

    function getWordCount(results) {

      var timeout = '';

      var poller = function(jobID) {
        // fire another request
        $http.get('/results/'+jobID).
          success(function(data, status, headers, config) {
            if(status === 202) {
              $log.log(data, status);
            } else if (status === 200){
              $log.log(data);
              $scope.loading = false;
              $scope.submitButtonText = "Submit";
              $scope.wordcounts = $scope.wordcounts.concat(data);
              $timeout.cancel(timeout);
              return false;
            }
            // continue to call the poller() function every 2 seconds
            // until the timeout is cancelled
            timeout = $timeout(poller, 2000);
          }).
          error(function(error) {
            $log.log(error);
            $scope.loading = false;
            $scope.submitButtonText = "Submit";
            $scope.urlerror = true;
          });
      };

      angular.forEach(results, poller(value, jobID));

    }

  }])

  app.directive('wordCountChart', ['$parse', function ($parse) {
    return {
      restrict: 'E',
      replace: true,
      template: '

',
      link: function (scope) {
        scope.$watch('wordcounts', function() {
          d3.select('#chart').selectAll('*').remove();
          var data = scope.wordcounts;
          for (var word in data) {
            d3.select('#chart')
              .append('div')
              .selectAll('div')
              .data(word[0])
              .enter()
              .append('div')
              .style('width', function() {
                return (data[word] * 20) + 'px';
              })
              .text(function(d){
                return word;
              });
          }
        }, true);
      }
     };
  }]);

  app.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function(){
                scope.$apply(function(){
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}]);
<|endoftext|>


<commit_before>var vasync = require('vasync');
var _ = require('lodash');
var tweetToHTML = require('tweet-to-html');

var dbManager = require('./db-manager');

var twitterApi = require('./twitter-api');

var handlers = { syncUser, getUser, getProfile, getTweets, syncUsers};

process.on('message', function(work) {
  handlers[work.jobName](work.jobData);
});

function getUser(userData) {
  dbManager.getUser(userData.username, function(user) {
    endWork(user);
  });
}

function syncUser(userData) {
  dbManager.saveUser(userData, function(user) {
    var count = 1;
    var max_id = '';

    twitterApi.fetchHomeTimeline(userData.username, {count}, function(err,res, tweets) {
      if(err) {
        console.log(err);
      } else {
        dbManager.getUser(userData.username, function(user) {
          user.maxSyncedTweetId = tweets[0].id_str;
          user.syncCount++;
          user.tweets = user.tweets.concat(tweetToHTML.parse(tweets));
          dbManager.saveUser(user, endWork);
        })
      }
    });
  });
}

function getProfile(data) {
  twitterApi.fetchProfile(data.username, data.condition, function(err,res, body) {
    endWork(body);
  });
}

function getTweets(username) {
  dbManager.getUser(username, function(user) {
    endWork(user.tweets);
  });
}

function syncUsers() {
  dbManager.getUsers(function(users) {
    vasync.forEachPipeline({
      'func': syncUserTweets,
      'inputs': users
    }, function (err, results) {
      if(err) {
        endWork(false);
      } else {
        var response = results.successes[0];
        console.log(response.fetchedTweets.length);
        console.log(response.maxSyncedTweetId);
        endWork(true);
      }
    })
  });
}

function syncUserTweets(user, callback) {
  var count = 30;
  var fetchedTweets = [];
  var maxSyncedTweetId = user.maxSyncedTweetId;

  function fetchUserTweets(condition) {
    twitterApi.fetchHomeTimeline(user.username, condition, function(err,res, tweets) {

      if(err) {
        console.log(err);
        callback(err);
      } else {
        var maxTweetIndex = _.findIndex(tweets, (tweet) => {
          return tweet.id_str === user.maxSyncedTweetId;
        });

        if(maxTweetIndex >= 0) {
          tweets.splice(maxTweetIndex, tweets.length - maxTweetIndex);
          fetchedTweets = fetchedTweets.concat(tweets);

          //When no new tweet is there, the tweet with the max_id we sent comes back as response which gets removed in
          //above lines. In that case fetchedTweets is empty and dont do below step.
          if(fetchedTweets.length) {
           maxSyncedTweetId = fetchedTweets[0].id_str;
          }

          //fetched all tweets. Now dump to database.
          user.maxSyncedTweetId = maxSyncedTweetId;
          user.tweets.unshift.apply(user.tweets, tweetToHTML.parse(fetchedTweets));

          dbManager.saveUser(user, function() {
            callback(null, {fetchedTweets, maxSyncedTweetId});
          });
        } else {
          fetchedTweets = fetchedTweets.concat(tweets);
          maxSyncedTweetId = tweets[tweets.length - 1].id_str;
          fetchUserTweets({count, max_id: maxSyncedTweetId});
        }
      }
    });
  }

  fetchUserTweets({count});
}

function endWork(user) {
  process.send(user);
}
<commit_msg>Now even if the max_id is too old or some error happens during sync, Whatever tweets that are fetched are saved to database.<commit_after>var vasync = require('vasync');
var _ = require('lodash');
var tweetToHTML = require('tweet-to-html');

var dbManager = require('./db-manager');

var twitterApi = require('./twitter-api');

var handlers = { syncUser, getUser, getProfile, getTweets, syncUsers};

process.on('message', function(work) {
  handlers[work.jobName](work.jobData);
});

function getUser(userData) {
  dbManager.getUser(userData.username, function(user) {
    endWork(user);
  });
}

function syncUser(userData) {
  dbManager.saveUser(userData, function(user) {
    var count = 1;
    var max_id = '';

    twitterApi.fetchHomeTimeline(userData.username, {count}, function(err,res, tweets) {
      if(err) {
        console.log(err);
      } else {
        dbManager.getUser(userData.username, function(user) {
          user.maxSyncedTweetId = tweets[0].id_str;
          user.syncCount++;
          user.tweets = user.tweets.concat(tweetToHTML.parse(tweets));
          dbManager.saveUser(user, endWork);
        })
      }
    });
  });
}

function getProfile(data) {
  twitterApi.fetchProfile(data.username, data.condition, function(err,res, body) {
    endWork(body);
  });
}

function getTweets(username) {
  dbManager.getUser(username, function(user) {
    endWork(user.tweets);
  });
}

function syncUsers() {
  dbManager.getUsers(function(users) {
    vasync.forEachPipeline({
      'func': syncUserTweets,
      'inputs': users
    }, function (err, results) {
      if(err) {
        endWork(false);
      } else {
        var response = results.successes[0];
        console.log(response.fetchedTweets.length);
        console.log(response.maxSyncedTweetId);
        endWork(true);
      }
    })
  });
}

function syncUserTweets(user, callback) {
  var count = 30;
  var fetchedTweets = [];
  var maxSyncedTweetId = user.maxSyncedTweetId;

  function fetchUserTweets(condition) {
    twitterApi.fetchHomeTimeline(user.username, condition, function(err,res, tweets) {

      if(err) {
        console.log(err);

        //If fetchedTweets have any tweets save them to db.
        //This can happen if maxSyncedTweetId is very old. The api will only go back to a certain limit.
        //Some error happened like rate limit.
        if(fetchedTweets.length > 0) {
          maxSyncedTweetId = fetchedTweets[0].id_str;
          user.maxSyncedTweetId = maxSyncedTweetId;
          user.tweets.unshift.apply(user.tweets, tweetToHTML.parse(fetchedTweets));

          dbManager.saveUser(user, function() {
            callback(null, {fetchedTweets, maxSyncedTweetId});
          });
        } else {
          callback(err);
        }
      } else {
        var maxTweetIndex = _.findIndex(tweets, (tweet) => {
          return tweet.id_str === user.maxSyncedTweetId;
        });

        if(maxTweetIndex >= 0) {
          tweets.splice(maxTweetIndex, tweets.length - maxTweetIndex);
          fetchedTweets = fetchedTweets.concat(tweets);

          //When no new tweet is there, the tweet with the max_id we sent comes back as response which gets removed in
          //above lines. In that case fetchedTweets is empty and dont do below step.
          if(fetchedTweets.length > 0) {
           maxSyncedTweetId = fetchedTweets[0].id_str;
          }

          //fetched all tweets. Now dump to database.
          user.maxSyncedTweetId = maxSyncedTweetId;
          user.tweets.unshift.apply(user.tweets, tweetToHTML.parse(fetchedTweets));

          dbManager.saveUser(user, function() {
            callback(null, {fetchedTweets, maxSyncedTweetId});
          });
        } else {
          fetchedTweets = fetchedTweets.concat(tweets);
          maxSyncedTweetId = tweets[tweets.length - 1].id_str;
          fetchUserTweets({count, max_id: maxSyncedTweetId});
        }
      }
    });
  }

  fetchUserTweets({count});
}

function endWork(user) {
  process.send(user);
}
<|endoftext|>


<commit_before>// include closure
var goog = require('closure').Closure({CLOSURE_BASE_PATH: 'ext\\closure-library\\closure\\goog\\'});
require('./ext/closure-library/closure/goog/bootstrap/nodejs')

/*
var path = require('path');
var closureBasePath = path.join(__dirname, '/ext/closure-library/closure/goog/');
print(closureBasePath)
var goog = require('closure').Closure({CLOSURE_BASE_PATH: closureBasePath});

*/

// include fixed version of XMLHttpRequest (supports sync calls)
global.XMLHttpRequest = require('./ext/xmlhttprequest-sync/lib/XMLHttpRequest').XMLHttpRequest;

// include GEE sources
require('./ext/earthengine-api/javascript/src/encodable')
require('./ext/earthengine-api/javascript/src/serializer')
require('./ext/earthengine-api/javascript/src/data')
require('./ext/earthengine-api/javascript/src/computedobject')
require('./ext/earthengine-api/javascript/src/types')
require('./ext/earthengine-api/javascript/src/function')
require('./ext/earthengine-api/javascript/src/apifunction')
require('./ext/earthengine-api/javascript/src/element')
require('./ext/earthengine-api/javascript/src/filter')
require('./ext/earthengine-api/javascript/src/collection')
require('./ext/earthengine-api/javascript/src/number')
require('./ext/earthengine-api/javascript/src/string')
require('./ext/earthengine-api/javascript/src/date')
require('./ext/earthengine-api/javascript/src/list')
require('./ext/earthengine-api/javascript/src/dictionary')
require('./ext/earthengine-api/javascript/src/geometry')
require('./ext/earthengine-api/javascript/src/feature')
require('./ext/earthengine-api/javascript/src/customfunction')
require('./ext/earthengine-api/javascript/src/featurecollection')
require('./ext/earthengine-api/javascript/src/image')
require('./ext/earthengine-api/javascript/src/imagecollection')
require('./ext/earthengine-api/javascript/src/terrain')
require('./ext/earthengine-api/javascript/src/ee')

var fs = require('fs')
var request = require('request');

// replacements used in playground code
global.print = function(arg) { console.log(arg); }
global.Map.addLayer = function(arg) {}
global.Map.addCenterObject = function(arg) {}
global.download = function(url, path) {
  var finished = false;

  var downloadAsync = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
      request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    });
  };

  downloadAsync(url, path, function(){
    finished = true;
  });

  while(!finished) {
     require('deasync').sleep(100);
  }
}

// setup authorization
gee = require('./authenticate')

// initialize google earth engine and call script
gee.initialize(function() {
  // parse command line
  var cmd = require('commander')

  var cmd = require('commander')

  cmd
    .version('0.0.1')
    .description('Google Earth Engine Playground code runner')
    .usage('ee-runner <path>')
    .parse(process.argv);

  if(cmd.args.length != 1) {
    cmd.help();
    process.exit();
  }

  var path = cmd.args[0]

  require('./' + path)
});<commit_msg>fixed path windows / linux<commit_after>// include closure
var goog = require('closure').Closure({CLOSURE_BASE_PATH: 'ext/closure-library/closure/goog' + require('path').sep});
require('./ext/closure-library/closure/goog/bootstrap/nodejs')

/*
var path = require('path');
var closureBasePath = path.join(__dirname, '/ext/closure-library/closure/goog/');
print(closureBasePath)
var goog = require('closure').Closure({CLOSURE_BASE_PATH: closureBasePath});

*/

// include fixed version of XMLHttpRequest (supports sync calls)
global.XMLHttpRequest = require('./ext/xmlhttprequest-sync/lib/XMLHttpRequest').XMLHttpRequest;

// include GEE sources
require('./ext/earthengine-api/javascript/src/encodable')
require('./ext/earthengine-api/javascript/src/serializer')
require('./ext/earthengine-api/javascript/src/data')
require('./ext/earthengine-api/javascript/src/computedobject')
require('./ext/earthengine-api/javascript/src/types')
require('./ext/earthengine-api/javascript/src/function')
require('./ext/earthengine-api/javascript/src/apifunction')
require('./ext/earthengine-api/javascript/src/element')
require('./ext/earthengine-api/javascript/src/filter')
require('./ext/earthengine-api/javascript/src/collection')
require('./ext/earthengine-api/javascript/src/number')
require('./ext/earthengine-api/javascript/src/string')
require('./ext/earthengine-api/javascript/src/date')
require('./ext/earthengine-api/javascript/src/list')
require('./ext/earthengine-api/javascript/src/dictionary')
require('./ext/earthengine-api/javascript/src/geometry')
require('./ext/earthengine-api/javascript/src/feature')
require('./ext/earthengine-api/javascript/src/customfunction')
require('./ext/earthengine-api/javascript/src/featurecollection')
require('./ext/earthengine-api/javascript/src/image')
require('./ext/earthengine-api/javascript/src/imagecollection')
require('./ext/earthengine-api/javascript/src/terrain')
require('./ext/earthengine-api/javascript/src/ee')

var fs = require('fs')
var request = require('request');

// replacements used in playground code
global.print = function(arg) { console.log(arg); }
global.Map.addLayer = function(arg) {}
global.Map.addCenterObject = function(arg) {}
global.download = function(url, path) {
  var finished = false;

  var downloadAsync = function(uri, filename, callback){
    request.head(uri, function(err, res, body){
      request(uri).pipe(fs.createWriteStream(filename)).on('close', callback);
    });
  };

  downloadAsync(url, path, function(){
    finished = true;
  });

  while(!finished) {
     require('deasync').sleep(100);
  }
}

// setup authorization
gee = require('./authenticate')

// initialize google earth engine and call script
gee.initialize(function() {
  // parse command line
  var cmd = require('commander')

  var cmd = require('commander')

  cmd
    .version('0.0.1')
    .description('Google Earth Engine Playground code runner')
    .usage('ee-runner <path>')
    .parse(process.argv);

  if(cmd.args.length != 1) {
    cmd.help();
    process.exit();
  }

  var path = cmd.args[0]

  require('./' + path)
});<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'DocPropertyView',
  extendsModel: 'DetailView',
  help: 'Displays the documentation of the given Properties.',

  properties: [
    {
      name:  'data',
      postSet: function(_, data) {
        if ( ! this.model && data && data.model_ ) this.model = data.model_;
        this.dataDAO = data;
        this.onValueChange();
      }
    },
    {
      name:  'dataDAO',
      preSet: function(_, data) {
        if (Array.isArray(data)) {
          var newDAO = this.X.ProxyDAO.create({delegate: data, model: Property});

          newDAO = newDAO.where(EQ(Property.HIDDEN, FALSE)); // only keep non-hidden

          newDAO.select(COUNT())(function(c) {
            this.count = c.count;
          }.bind(this));
          return newDAO;
        } else {
          return data;
        }
      },
    },
    {
      name: 'count',
      postSet: function(_, nu) {
        this.onValueChange();
      }
    }
  ],

  templates: [
    function toHTML()    {/*

        <%=this.toInnerHTML()%>

    */},
    function toInnerHTML()    {/*
    <%    if (this.count > 0)
          {  %>

Properties:

<%= this.X.DAOListView.create({ rowView: 'DocPropertyRowView', data$: this.dataDAO$ }) %>

    <%    } else { %>

No Properties.

    <%    } %>
    */}
  ]
});

MODEL({
  name: 'DocPropertyRowView',
  extendsModel: 'DetailView',

  templates: [
    function toHTML() {/*

<%=this.data.name%>

<%=this.data.help%>

    */}
  ]
});

MODEL({
  name: 'DocModelView',
  extendsModel: 'View',
  label: 'Documentation Model View',
  help: 'Shows documentation for a Model.',

  properties: [
    {
      name: 'data',
      help: 'The Model from which to extract documentation.',
      postSet: function() {
        // grab the documentation and compile a template to use in this view
        if (this.data && Documentation.isInstance(this.data.documentation))
        {
          // bind docSource
          this.docSource$ = this.data.documentation$;
        }
      }
    },
    {
      name: 'docSource',
      type: 'Documentation',
      help: 'The documentation object to render.',
      postSet: function() {

        this.updateHTML();
      }
    }

  ],

  templates: [

    function toInnerHTML()    {/*
      <%    if (this.data) {  %>

<%=this.data.name%>

docs here

      <%    } %>
    */}
  ],

  methods: {

    /** Create the sub-view from property info. **/
    createReferenceView: function(prop, opt_args) {
      if (!opt_args.parentModel) {
        opt_args.parentModel = data; // pass along the model in case it's needed to resolve a local link
      }
      var X = ( opt_args && opt_args.X ) || this.X;
      var v = X.DocRefView.create({args: opt_args});
      this.addChild(v);
      return v;
    },

    createTemplateView: function(name, opt_args) {
      // name has been constantized ('PROP_NAME'), but we're
      // only looking for certain doc tags anyway.
      if (name === 'DOC') {
        var v = this.createReferenceView(opt_args) ;
        v.data = this;
        return v;
      } else {
        return this.SUPER(name, opt_args);
      }
    }
  }
});

MODEL({
  name: 'DocRefView',
  extendsModel: 'View',
  label: 'Documentation Reference View',
  help: 'The view of a documentation reference link.',

  properties: [
    {
      name: 'data',
      help: 'The referenced object.',
      postSet: function() {
        updateHTML();
      }
    },
    {
      name: 'parentModel',
      help: 'The data of our container view. Used to resolve local links that do not include an explicit Model name.',
      postSet: function() {
        // might have to resolve again since ref may have been set first
        var o = resolveReference(this.ref);
        if ( o ) data = o;
      }
    },
    {
      name: 'ref',
      help: 'The reference to link.',
      postSet: function() {
        var o = resolveReference(this.ref);
        if ( o ) data = o;
      }
    }
  ],

  templates: [

    function toInnerHTML()    {/*
      <%    if (this.data) {  %>
              [<%=this.data.label%>]
      <%    } else { %>
              [INVALID_REF:<%=this.ref%>]
      <%    } %>
    */}
  ],

  methods: {
    init: function() {
      this.SUPER();

      this.tagName = 'span';

    },

    resolveReference: function(reference) {
      var model;
      var feature;
      // parse "Model.feature" or "Model" or "feature" with implicit Model==this.data
      args = reference.split('.');
      var foundObject;
      if (args.length > 1)
      {
        // two params specified explicitly
        model = args[0];
        feature = args[1];
        foundObject = this.X[model].getFeature(feature);
      }
      else
      {
        // see if there's a feature on the parent model that matches
        model = parentModel;
        feature = args[0];
        foundObject = parentModel.getFeature(feature);

        if (!foundObject) {
          // no feature found, so assume it's a model
          model = args[0];
          feature = undefined;
          foundObject = this.X[model];
        }
      }
      return foundObject;
    },

  }
});
<commit_msg>Doc View templates and link resolving now working.<commit_after>/**
 * @license
 * Copyright 2013 Google Inc. All Rights Reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MODEL({
  name: 'DocPropertyView',
  extendsModel: 'DetailView',
  help: 'Displays the documentation of the given Properties.',

  properties: [
    {
      name:  'data',
      postSet: function(_, data) {
        if ( ! this.model && data && data.model_ ) this.model = data.model_;
        this.dataDAO = data;
        this.onValueChange();
      }
    },
    {
      name:  'dataDAO',
      preSet: function(_, data) {
        if (Array.isArray(data)) {
          var newDAO = this.X.ProxyDAO.create({delegate: data, model: Property});

          newDAO = newDAO.where(EQ(Property.HIDDEN, FALSE)); // only keep non-hidden

          newDAO.select(COUNT())(function(c) {
            this.count = c.count;
          }.bind(this));
          return newDAO;
        } else {
          return data;
        }
      },
    },
    {
      name: 'count',
      postSet: function(_, nu) {
        this.onValueChange();
      }
    }
  ],

  templates: [
    function toHTML()    {/*

        <%=this.toInnerHTML()%>

    */},
    function toInnerHTML()    {/*
    <%    if (this.count > 0)
          {  %>

Properties:

<%= this.X.DAOListView.create({ rowView: 'DocPropertyRowView', data$: this.dataDAO$ }) %>

    <%    } else { %>

No Properties.

    <%    } %>
    */}
  ]
});

MODEL({
  name: 'DocPropertyRowView',
  extendsModel: 'DetailView',

  templates: [
    function toHTML() {/*

<%=this.data.name%>

<%=this.data.help%>

    */}
  ]
});

MODEL({
  name: 'DocModelView',
  extendsModel: 'View',
  label: 'Documentation Model View',
  help: 'Shows documentation for a Model.',

  properties: [
    {
      name: 'data',
      help: 'The Model from which to extract documentation.',
      postSet: function() {
        // grab the documentation and compile a template to use in this view
        if (this.data && Documentation.isInstance(this.data.documentation))
        {
          // bind docSource
          this.docSource$ = this.data.documentation$;
        }
      }
    },
    {
      name: 'docSource',
      type: 'Documentation',
      help: 'The documentation object to render.',
      postSet: function() {
        if (this.docSource && this.docSource.body)
        {
          this.renderDocSourceHTML = TemplateUtil.lazyCompile(this.docSource.body);
        }
      }
    },
    {
      name: 'renderDocSourceHTML',
      help: 'Automatically created to render the docSource.body template.',
      postSet: function() {
        this.updateHTML();
      }
    }

  ],

  templates: [
    function toInnerHTML()    {/*
      <%    if (this.data) {  %>

<%=this.data.name%>

<%=this.renderDocSourceHTML()%>

      <%    } %>
    */}
  ],

  methods: {

    /** Create the special reference lookup sub-view from property info. **/
    createReferenceView: function(opt_args) {
      var X = ( opt_args && opt_args.X ) || this.X;
      var v = X.DocRefView.create({parentModel:this.data, ref:opt_args.ref, args: opt_args});
      this.addChild(v);
      return v;
    },

    createTemplateView: function(name, opt_args) {
      // name has been constantized ('PROP_NAME'), but we're
      // only looking for certain doc tags anyway.
      if (name === 'DOC') {
        var v = this.createReferenceView(opt_args) ;
        return v;
      } else {
        return this.SUPER(name, opt_args);
      }
    }
  }
});

MODEL({
  name: 'DocRefView',
  extendsModel: 'View',
  label: 'Documentation Reference View',
  help: 'The view of a documentation reference link.',

  properties: [
    {
      name: 'data',
      help: 'The referenced object.',
      postSet: function() {
        this.updateHTML();
      }
    },
    {
      name: 'parentModel',
      help: 'The data of our container view. Used to resolve local links that do not include an explicit Model name.',
      postSet: function() {
        // might have to resolve again since ref may have been set first
        var o = this.resolveReference(this.ref);
        if ( o ) this.data = o;
      }
    },
    {
      name: 'ref',
      help: 'The reference to link.',
      postSet: function() {
        console.log("Setting ref [" + this.ref + "]");
        var o = this.resolveReference(this.ref);
        if ( o ) this.data = o;
      }
    }
  ],

  templates: [

    function toInnerHTML()    {/*
      <%    if (this.data && this.data.label && this.data.label.length > 0) {  %>
              [<%=this.data.label%>]
      <%    } else if (this.data && this.data.name) {  %>
              [<%=this.data.name%>]
      <%    } else if (this.data.id) {  %>
              [<%=this.data.id%>]
      <%    } else { %>
              [INVALID_REF:<%=this.ref%>]
      <%    } %>
    */}
  ],

  methods: {
    init: function() {
      this.SUPER();

      this.tagName = 'span';

    },

    resolveReference: function(reference) {
      var model;
      var feature;
      // parse "Model.feature" or "Model" or "feature" with implicit Model==this.data
      args = reference.split('.');
      var foundObject;
      if (args.length > 1)
      {
        // two params specified explicitly
        model = args[0];
        feature = args[1];
        foundObject = this.X[model].getFeature(feature);
        console.log("Resolved explicit "+model+"."+feature+" to "+ foundObject);
      }
      else
      {
        // see if there's a feature on the parent model that matches
        model = this.parentModel;
        feature = args[0];
        foundObject = this.parentModel.getFeature(feature);

        if (!foundObject) {
          // no feature found, so assume it's a model
          model = args[0];
          feature = undefined;
          foundObject = this.X[model];
          console.log("Resolved no feature, found model "+model+" to "+ foundObject);
        }
        else
          console.log("Resolved on this.parentModel "+model.name+"."+feature+" to "+ foundObject);

      }
      console.log(foundObject);
      return foundObject;
    },

  }
});
<|endoftext|>


<commit_before>'use strict';

const env = require('./env'),
    path = require('path'),
    BbPromise = require('bluebird'),
    Loki = require('lokijs'),
    fs = BbPromise.promisifyAll(require("fs-extra")),
    md5 = require('md5'),
    BlobHttpProperties = require('./model/BlobHttpProperties');

const CONTAINERS_COL_NAME = 'Containers',
    COMMITS = 'Commits';

class StorageManager {
    constructor() {
        this.dbName = '__azurite_db__.json'
    }

    init(localStoragePath) {
        this.db = BbPromise.promisifyAll(new Loki(env.azuriteDBPath, { autosave: true, autosaveInterval: 5000 }));
        return fs.statAsync(env.azuriteDBPath)
            .then((stat) => {
                return this.db.loadDatabaseAsync(env.dbName);
            })
            .then((data) => {
                if (!this.db.getCollection(CONTAINERS_COL_NAME)) {
                    this.db.addCollection(CONTAINERS_COL_NAME);
                    return this.db.saveDatabaseAsync();
                }
            })
            .catch((e) => {
                if (e.code === 'ENOENT') {
                    // No DB hasn't been persisted / initialized yet.
                    this.db.addCollection(CONTAINERS_COL_NAME);
                    this.db.addCollection(COMMITS);
                    return this.db.saveDatabaseAsync();
                }
                // This should never happen!
                console.error(`Failed to initialize database at "${this.dbPath}"`);
                throw e;
            });
    }

    createContainer(model) {
        let p = path.join(env.localStoragePath, model.name);
        return fs.mkdirAsync(p)
            .then(() => {
                let tables = this.db.getCollection(CONTAINERS_COL_NAME);
                tables.insert({ name: model.name, http_props: model.httpProps, meta_props: model.metaProps, access: model.access });
            });
    }

    deleteContainer(name) {
        let container = path.join(env.localStoragePath, name);
        return fs.statAsync(container)
            .then((stat) => {
                return fs.removeAsync(container);
            })
            .then(() => {
                let tables = this.db.getCollection(CONTAINERS_COL_NAME);
                tables.chain().find({ 'name': { '$eq': name } }).remove();
                this.db.removeCollection(name);
            });
    }

    listContainer(prefix, maxresults) {
        return BbPromise.try(() => {
            maxresults = parseInt(maxresults);
            let tables = this.db.getCollection(CONTAINERS_COL_NAME);
            let result = tables.chain()
                .find({ 'name': { '$contains': prefix } })
                .simplesort('name')
                .limit(maxresults)
                .data();
            return result;
        });
    }

    createBlockBlob(container, blobName, body, httpProps, metaProps) {
        let containerPath = path.join(env.localStoragePath, container);
        let blobPath = path.join(containerPath, blobName);
        let response = {};
        const targetMD5 = md5(body);
        httpProps.ContentMD5 = targetMD5;
        return fs.statAsync(containerPath)
            .then((stat) => {
                const sourceMD5 = httpProps['Content-MD5'];
                response.md5 = targetMD5;
                if (sourceMD5) {
                    if (targetMD5 !== sourceMD5) {
                        const err = new Error('MD5 hash corrupted.');
                        err.name = 'md5';
                        throw err;
                    }
                }
            })
            .then(() => {
                // Container exists, otherwise fs.statAsync throws error
                return fs.outputFileAsync(blobPath, body, { encoding: httpProps['Content-Encoding'] });
            })
            .then(() => {
                let coll = this.db.getCollection(container);
                if (!coll) {
                    coll = this.db.addCollection(container);
                }
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data();

                if (blobResult.length === 0) {
                    const newBlob = coll.insert({
                        name: blobName,
                        http_props: httpProps,
                        meta_props: metaProps
                    });
                    response.ETag = newBlob.meta.revision;
                    response.lastModified = httpProps.lastModified;
                } else {
                    const updateBlob = blobResult[0];
                    updateBlob.http_props = httpProps;
                    updateBlob.meta_props = metaProps;
                    coll.update(updateBlob);
                    response.ETag = updateBlob.meta.revision;
                    response.lastModified = httpProps.lastModified;
                }
            })
            .then(() => {
                return response;
            });
    }

    deleteBlob(container, name) {
        let blobPath = path.join(env.localStoragePath, container, name);
        return fs.statAsync(blobPath)
            .then((stat) => {
                return fs.removeAsync(blobPath);
            })
            .then(() => {
                let coll = this.db.getCollection(container);
                coll.chain().find({ 'name': { '$eq': name } }).remove();
            });
    }

    getBlob(containerName, blobName) {
        const response = {};
        const blobPath = path.join(env.localStoragePath, containerName, blobName);
        response.blobPath = blobPath;
        response.x_ms_server_encrypted = false;
        return fs.statAsync(blobPath)
            .then((stat) => {
                const coll = this.db.getCollection(containerName);
                const blob = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data()[0];
                response.httpProps = blob.http_props;
                response.metaProps = blob.meta_props;
                response.ETag = blob.meta.revision;
                return response;
            })
    }

    listBlobs(containerName, options) {
        return BbPromise.try(() => {
            const coll = this.db.getCollection(containerName);
            let blobs = coll.chain()
                .find({ 'name': { '$contains': options.prefix } })
                .simplesort('name')
                .limit(options.maxresults);
            if (options.marker) {
                let offset = parseInt(options.marker);
                offset *= (options.maxresults || 1000);
                blobs.offset(offset);
            }
            const result = blobs.data();
            return result;
        });
    }

    putBlock(containerName, blobName, body, options) {
        const response = {};
        const blobPath = path.join(env.localStoragePath, containerName, blobName);
        // Make sure that the parent blob exists on storage.
        return fs.ensureFileAsync(blobPath)
            .then(() => {
                let coll = this.db.getCollection(containerName);
                if (!coll) {
                    coll = this.db.addCollection(containerName);
                }
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data();

                // We only create the blob in DB if it does not already exists.
                if (blobResult.length === 0) {
                    const httpProps = new BlobHttpProperties();
                    httpProps.committed = false;
                    coll.insert({
                        name: blobName,
                        http_props: httpProps
                    });
                }

                // Checking MD5 in case 'Content-MD5' header was set.
                const sourceMD5 = options.httpProps['Content-MD5'];
                const targetMD5 = md5(body);
                response['Content-MD5'] = targetMD5;
                if (sourceMD5) {
                    if (targetMD5 !== sourceMD5) {
                        const err = new Error('MD5 hash corrupted.');
                        err.name = 'md5';
                        throw err;
                    }
                }
            })
            .then(() => {
                const blockPath = path.join(env.commitsPath, options.fileName);
                return fs.outputFileAsync(blockPath, body, { encoding: options.httpProps['Content-Encoding'] });
            })
            .then(() => {
                const coll = this.db.getCollection(COMMITS);
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': options.fileName } })
                    .data();

                if (blobResult.length === 0) {
                    const newBlob = coll.insert({
                        name: options.fileName,
                        parent: options.parent,
                        http_props: options.httpProps
                    });
                    response.ETag = newBlob.meta.revision;
                    response.lastModified = options.httpProps.lastModified;
                } else {
                    const updateBlob = blobResult[0];
                    updateBlob.http_props = options.httpProps;
                    coll.update(updateBlob);
                    response.ETag = updateBlob.meta.revision;
                    response.lastModified = options.httpProps.lastModified;
                }
                return response;
            });
    }
}

module.exports = new StorageManager;<commit_msg>added comments<commit_after>'use strict';

const env = require('./env'),
    path = require('path'),
    BbPromise = require('bluebird'),
    Loki = require('lokijs'),
    fs = BbPromise.promisifyAll(require("fs-extra")),
    md5 = require('md5'),
    BlobHttpProperties = require('./model/BlobHttpProperties');

const CONTAINERS_COL_NAME = 'Containers',
    COMMITS = 'Commits';

class StorageManager {
    constructor() {
        this.dbName = '__azurite_db__.json'
    }

    init(localStoragePath) {
        this.db = BbPromise.promisifyAll(new Loki(env.azuriteDBPath, { autosave: true, autosaveInterval: 5000 }));
        return fs.statAsync(env.azuriteDBPath)
            .then((stat) => {
                return this.db.loadDatabaseAsync(env.dbName);
            })
            .then((data) => {
                if (!this.db.getCollection(CONTAINERS_COL_NAME)) {
                    this.db.addCollection(CONTAINERS_COL_NAME);
                    return this.db.saveDatabaseAsync();
                }
            })
            .catch((e) => {
                if (e.code === 'ENOENT') {
                    // No DB hasn't been persisted / initialized yet.
                    this.db.addCollection(CONTAINERS_COL_NAME);
                    this.db.addCollection(COMMITS);
                    return this.db.saveDatabaseAsync();
                }
                // This should never happen!
                console.error(`Failed to initialize database at "${this.dbPath}"`);
                throw e;
            });
    }

    createContainer(model) {
        let p = path.join(env.localStoragePath, model.name);
        return fs.mkdirAsync(p)
            .then(() => {
                let tables = this.db.getCollection(CONTAINERS_COL_NAME);
                tables.insert({ name: model.name, http_props: model.httpProps, meta_props: model.metaProps, access: model.access });
            });
    }

    deleteContainer(name) {
        let container = path.join(env.localStoragePath, name);
        return fs.statAsync(container)
            .then((stat) => {
                return fs.removeAsync(container);
            })
            .then(() => {
                let tables = this.db.getCollection(CONTAINERS_COL_NAME);
                tables.chain().find({ 'name': { '$eq': name } }).remove();
                this.db.removeCollection(name);
            });
    }

    listContainer(prefix, maxresults) {
        return BbPromise.try(() => {
            maxresults = parseInt(maxresults);
            let tables = this.db.getCollection(CONTAINERS_COL_NAME);
            let result = tables.chain()
                .find({ 'name': { '$contains': prefix } })
                .simplesort('name')
                .limit(maxresults)
                .data();
            return result;
        });
    }

    createBlockBlob(container, blobName, body, httpProps, metaProps) {
        let containerPath = path.join(env.localStoragePath, container);
        let blobPath = path.join(containerPath, blobName);
        let response = {};
        const targetMD5 = md5(body);
        httpProps.ContentMD5 = targetMD5;
        return fs.statAsync(containerPath)
            .then((stat) => {
                const sourceMD5 = httpProps['Content-MD5'];
                response.md5 = targetMD5;
                if (sourceMD5) {
                    if (targetMD5 !== sourceMD5) {
                        const err = new Error('MD5 hash corrupted.');
                        err.name = 'md5';
                        throw err;
                    }
                }
            })
            .then(() => {
                // Container exists, otherwise fs.statAsync throws error
                return fs.outputFileAsync(blobPath, body, { encoding: httpProps['Content-Encoding'] });
            })
            .then(() => {
                let coll = this.db.getCollection(container);
                if (!coll) {
                    coll = this.db.addCollection(container);
                }
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data();

                if (blobResult.length === 0) {
                    const newBlob = coll.insert({
                        name: blobName,
                        http_props: httpProps,
                        meta_props: metaProps
                    });
                    response.ETag = newBlob.meta.revision;
                    response.lastModified = httpProps.lastModified;
                } else {
                    const updateBlob = blobResult[0];
                    updateBlob.http_props = httpProps;
                    updateBlob.meta_props = metaProps;
                    coll.update(updateBlob);
                    response.ETag = updateBlob.meta.revision;
                    response.lastModified = httpProps.lastModified;
                }
            })
            .then(() => {
                return response;
            });
    }

    deleteBlob(container, name) {
        let blobPath = path.join(env.localStoragePath, container, name);
        return fs.statAsync(blobPath)
            .then((stat) => {
                return fs.removeAsync(blobPath);
            })
            .then(() => {
                let coll = this.db.getCollection(container);
                coll.chain().find({ 'name': { '$eq': name } }).remove();
            });
    }

    getBlob(containerName, blobName) {
        const response = {};
        const blobPath = path.join(env.localStoragePath, containerName, blobName);
        response.blobPath = blobPath;
        response.x_ms_server_encrypted = false;
        return fs.statAsync(blobPath)
            .then((stat) => {
                const coll = this.db.getCollection(containerName);
                const blob = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data()[0];
                response.httpProps = blob.http_props;
                response.metaProps = blob.meta_props;
                response.ETag = blob.meta.revision;
                return response;
            })
    }

    listBlobs(containerName, options) {
        return BbPromise.try(() => {
            const coll = this.db.getCollection(containerName);
            let blobs = coll.chain()
                .find({ 'name': { '$contains': options.prefix } })
                .simplesort('name')
                .limit(options.maxresults);
            if (options.marker) {
                let offset = parseInt(options.marker);
                offset *= (options.maxresults || 1000);
                blobs.offset(offset);
            }
            const result = blobs.data();
            return result;
        });
    }

    putBlock(containerName, blobName, body, options) {
        const response = {};
        const blobPath = path.join(env.localStoragePath, containerName, blobName);
        // Make sure that the parent blob exists on storage.
        return fs.ensureFileAsync(blobPath)
            .then(() => {
                let coll = this.db.getCollection(containerName);
                if (!coll) {
                    coll = this.db.addCollection(containerName);
                }
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': blobName } })
                    .data();

                // We only create the blob in DB if it does not already exists.
                if (blobResult.length === 0) {
                    const httpProps = new BlobHttpProperties();
                    httpProps.committed = false;
                    coll.insert({
                        name: blobName,
                        http_props: httpProps
                    });
                }

                // Checking MD5 in case 'Content-MD5' header was set.
                const sourceMD5 = options.httpProps['Content-MD5'];
                const targetMD5 = md5(body);
                response['Content-MD5'] = targetMD5;
                if (sourceMD5) {
                    if (targetMD5 !== sourceMD5) {
                        const err = new Error('MD5 hash corrupted.');
                        err.name = 'md5';
                        throw err;
                    }
                }
            })
            .then(() => {
                // Writing block to disk.
                const blockPath = path.join(env.commitsPath, options.fileName);
                return fs.outputFileAsync(blockPath, body, { encoding: options.httpProps['Content-Encoding'] });
            })
            .then(() => {
                // Storing block information in DB.
                const coll = this.db.getCollection(COMMITS);
                const blobResult = coll.chain()
                    .find({ 'name': { '$eq': options.fileName } })
                    .data();

                if (blobResult.length === 0) {
                    const newBlob = coll.insert({
                        name: options.fileName,
                        parent: options.parent,
                        http_props: options.httpProps
                    });
                    response.ETag = newBlob.meta.revision;
                    response.lastModified = options.httpProps.lastModified;
                } else {
                    const updateBlob = blobResult[0];
                    updateBlob.http_props = options.httpProps;
                    coll.update(updateBlob);
                    response.ETag = updateBlob.meta.revision;
                    response.lastModified = options.httpProps.lastModified;
                }
                return response;
            });
    }
}

module.exports = new StorageManager;<|endoftext|>


<commit_before>var timestamp = null;
var data = {};
var curKey = '';
var sort = null;
var curOrder = "country";
function sortBtnClickHandler() {
  curOrder === 'percentage' ? curOrder = 'country' : curOrder = 'percentage';
  sort(data[curKey], curOrder);
}
function navClickHandler(e) {
  curKey = e.innerText.toLowerCase();
  curOrder = "country";
  sort(data[curKey], "country");
}
$.ajax({
  url: "/awwwards-analysis/data/result (nominees).csv",
}).then(function(data, status, xhr) {
  timestamp = xhr.getResponseHeader("Last-Modified");
  /* main */
  function analyze(error, nominees, honorable, developer, sotd, sotm, soty) {
    if (error) {
      console.log(error);
    }
    function parseData(d) {
      return {
        country: d.Country,
        percentage: +d.Percentage,
        submission: +d.Submission,
        population: +d.Population,
      };
    }
    data['nominees'] = nominees.map(parseData);
    data['honorable mention'] = honorable.map(parseData);
    data['developer award'] = developer.map(parseData);
    data['site of the day'] = sotd.map(parseData);
    data['site of the month'] = sotm.map(parseData);
    data['site of the year'] = soty.map(parseData);
    curKey = 'nominees';
    function numberWithCommas(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }
    sort = function(data, order) {
      if (order === "country") {
        data = data.sort(function(a, b) {
          if (a.country > b.country) {
            return 1;
          } else if (a.country < b.country) {
            return -1;
          }
          return 0;
        });
      } else {
        data = data.sort(function(a, b) {
          return b.percentage - a.percentage;
        });
      }
      update(data, false);
    }
    var prevIdx = 0;
    function mousemove(el, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx) {
      // Invert x coordinate
      var country = mapXCoordToCountry(d3.mouse(el)[0]);
      var x = xScale(country);
      var idx = mapCountryToIdx(country);
      var y = yScale(data[idx].percentage);
      // Calculating the boundary index to rotate the tooltip
      var xAxisInterval = width / data.length;
      let sum = 0, lastIdx = data.length - 1;
      while(sum < 190) {
        sum += xAxisInterval;
        lastIdx--;
      }
      g.select(".guideline")
          .transition()
          .duration(50)
          .attr("x1", x)
          .attr("x2", x);
      g.selectAll("circle")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("fill", "white")
          .attr("r", 3.5);
      g.selectAll("circle")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("fill", "#0f747f")
          .attr("r", 5);
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("font-size", "10px");
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("font-size", "12px");
      if (idx <= lastIdx) {
        d3.select("#tooltip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x + margin.left + tipMargin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "-15px");
      } else {
        d3.select("#tooltip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x - margin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(-45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "173px");
      }
      d3.select("#country")
          .text(data[idx].country);
      d3.select("#percentage")
          .text(data[idx].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[idx].submission);
      d3.select("#population")
          .text(numberWithCommas(data[idx].population));
      prevIdx = idx;
    }
    function update(data, initialize) {
      var xScale = d3.scalePoint().domain(data.map(function(d) { return d.country; })).range([0, width]);
      var yScale = d3.scaleLinear().domain(d3.extent(data, function(d) { return d.percentage; })).range([height, 0]);
      // Used for mapping x coordinate to ordinal data
      var mapXCoordToCountry = d3.scaleQuantize().domain(xScale.range()).range(xScale.domain());
      // Used for mapping ordinal data to data index
      var mapCountryToIdx = d3.scaleBand().domain(data.map(function(d) { return d.country; })).range([0, data.length]);
      // Set up area, line, gridline
      var area = d3.area()
          .x(function(d) { return xScale(d.country); })
          .y0(height)
          .y1(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);
      var line = d3.line()
          .x(function(d) { return xScale(d.country); })
          .y(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);

      if (initialize) {
        // Draw area
        g.append("path")
            .data([data])
            .attr("class", "area")
            .attr("d", area)
            .style('fill', 'url(#gradient)');
        // Draw x-axis
        g.append("g")
            .attr("class", "axis x-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
        // Remove x-axis domain
        // g.select(".x-axis")
        //     .selectAll(".domain")
        //     .remove();
        // Draw y-axis
        g.append("g")
            .attr("class", "axis y-axis")
            .call(d3.axisLeft(yScale))
            .selectAll("text")
            .attr("class", "y-axis-text")
        // Draw y-axis label
        g.select(".y-axis")
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", -70)
            .text("Percentage (%)");
        g.select(".y-axis")
            .append("text")
            .attr("id", "timestamp")
            .attr("x", "45px")
            .attr("y", -(margin.top / 2))
            .text(timestamp + " Nominees");
        // Draw trendline
        g.append("path")
            .datum(data)
            .attr("class", "trendline")
            .attr("d", line);
        // Draw guideline
        g.append("line")
            .attr("class", "guideline")
            .attr("x1", 0)
            .attr("y1", 0)
            .attr("x2", 0)
            .attr("y2", height);
        // Draw circle
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .attr("r", 3.5)
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        // Draw a transparent layer for detecting mouse event
        g.append("rect")
            .attr("class", "overlay")
            .attr("width", width)
            .attr("height", height)
            .attr("opacity", 0)
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
      } else {
        g.select(".area")
            .data([data])
            .transition()
            .duration(500)
            .attr("d", area);
        g.select(".trendline")
            .datum(data)
            .transition()
            .duration(500)
            .attr("d", line);
        // Exit circles
        g.selectAll("circle")
            .data(data)
          .exit()
            .remove();
        // Enter circles
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("r", 3.5);
        // Update circles
        g.selectAll("circle")
            .data(data)
            .style("fill", "white")
            .attr("r", 3.5)
            .transition()
            .duration(500)
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        g.selectAll(".x-axis")
            .transition()
            .duration(500)
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
            // .selectAll(".domain")
            // .remove();
        // Update x-axis texts
        g.selectAll(".x-axis-text")
            .style("font-size", "10px")
            .filter(function(d, i) { return i === 0 })
            .style("font-size", "12px");
        g.selectAll(".y-axis")
            .transition()
            .duration(500)
            .call(d3.axisLeft(yScale));
        g.select(".overlay")
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
        prevIdx = 0;
      }
      g.select(".guideline")
          .transition()
          .duration(500)
          .attr("x1", 0)
          .attr("x2", 0);
      d3.select("#tooltip")
          .transition()
          .duration(500)
          .style("top", yScale(data[0].percentage) + margin.top + tipMargin.top + "px")
          .style("left", xScale(data[0].country) + margin.left + tipMargin.left + "px");
      d3.select("#tipTriangle")
          .style("transform", "rotate(45deg)")
          .transition()
          .duration(50)
          .style("bottom", "-35px")
          .style("left", "-15px");
      d3.select("#country")
          .text(data[0].country);
      d3.select("#percentage")
          .text(data[0].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[0].submission);
      d3.select("#population")
          .text(numberWithCommas(data[0].population));
    }
    // Set up svg
    var svg = d3.select("svg"),
        margin = {
          top: 80,
          right: 80,
          bottom: 125,
          left: 100,
        },
        width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
        height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    // Set up gradient color
    var gradient = svg.append('defs')
        .append('linearGradient')
        .attr('id', 'gradient')
        .attr('x1', '0%')
        .attr('y1', '0%')
        .attr('x2', '0%')
        .attr('y2', '100%')
        .attr('spreadMethod', 'pad');
    gradient.append('stop')
        .attr('offset', '0%')
        .attr('stop-color', 'rgb(36,175,192)')
        .attr('stop-opacity', 1);
    gradient.append('stop')
        .attr('offset', '100%')
        .attr('stop-color', 'rgb(206,254,226)')
        .attr('stop-opacity', 1);
    var tipMargin = {
      top: -60,
      left: 10,
    };
    update(data[curKey], true);
  }
  // Read data
  d3.queue()
    .defer(d3.csv, './data/result (nominees).csv')
    .defer(d3.csv, './data/result (honorable).csv')
    .defer(d3.csv, './data/result (developer).csv')
    .defer(d3.csv, './data/result (sites_of_the_day).csv')
    .defer(d3.csv, './data/result (sites_of_the_month).csv')
    .defer(d3.csv, './data/result (sites_of_the_year).csv')
    .await(analyze);
});
<commit_msg>Testing timestamp<commit_after>var timestamp = null;
var data = {};
var curKey = '';
var sort = null;
var curOrder = "country";
function sortBtnClickHandler() {
  curOrder === 'percentage' ? curOrder = 'country' : curOrder = 'percentage';
  sort(data[curKey], curOrder);
}
function navClickHandler(e) {
  curKey = e.innerText.toLowerCase();
  curOrder = "country";
  sort(data[curKey], "country");
}
$.ajax({
  url: "/awwwards-analysis/data/result (nominees).csv",
}).then(function(d, stat, xhr) {
  timestamp = xhr.getResponseHeader("Last-Modified");
  /* main */
  function analyze(error, nominees, honorable, developer, sotd, sotm, soty) {
    if (error) {
      console.log(error);
    }
    function parseData(d) {
      return {
        country: d.Country,
        percentage: +d.Percentage,
        submission: +d.Submission,
        population: +d.Population,
      };
    }
    data['nominees'] = nominees.map(parseData);
    data['honorable mention'] = honorable.map(parseData);
    data['developer award'] = developer.map(parseData);
    data['site of the day'] = sotd.map(parseData);
    data['site of the month'] = sotm.map(parseData);
    data['site of the year'] = soty.map(parseData);
    curKey = 'nominees';
    function numberWithCommas(x) {
      return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    }
    sort = function(data, order) {
      if (order === "country") {
        data = data.sort(function(a, b) {
          if (a.country > b.country) {
            return 1;
          } else if (a.country < b.country) {
            return -1;
          }
          return 0;
        });
      } else {
        data = data.sort(function(a, b) {
          return b.percentage - a.percentage;
        });
      }
      update(data, false);
    }
    var prevIdx = 0;
    function mousemove(el, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx) {
      // Invert x coordinate
      var country = mapXCoordToCountry(d3.mouse(el)[0]);
      var x = xScale(country);
      var idx = mapCountryToIdx(country);
      var y = yScale(data[idx].percentage);
      // Calculating the boundary index to rotate the tooltip
      var xAxisInterval = width / data.length;
      let sum = 0, lastIdx = data.length - 1;
      while(sum < 190) {
        sum += xAxisInterval;
        lastIdx--;
      }
      g.select(".guideline")
          .transition()
          .duration(50)
          .attr("x1", x)
          .attr("x2", x);
      g.selectAll("circle")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("fill", "white")
          .attr("r", 3.5);
      g.selectAll("circle")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("fill", "#0f747f")
          .attr("r", 5);
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === prevIdx })
          .transition()
          .duration(50)
          .style("font-size", "10px");
      g.selectAll(".x-axis-text")
          .filter(function(d, i) { return i === idx })
          .transition()
          .duration(50)
          .style("font-size", "12px");
      if (idx <= lastIdx) {
        d3.select("#tooltip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x + margin.left + tipMargin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "-15px");
      } else {
        d3.select("#tooltip")
            .transition()
            .duration(50)
            .style("top", y + margin.top + tipMargin.top + "px")
            .style("left", x - margin.left + "px");
        d3.select("#tipTriangle")
            .style("transform", "rotate(-45deg)")
            .transition()
            .duration(50)
            .style("bottom", "-35px")
            .style("left", "173px");
      }
      d3.select("#country")
          .text(data[idx].country);
      d3.select("#percentage")
          .text(data[idx].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[idx].submission);
      d3.select("#population")
          .text(numberWithCommas(data[idx].population));
      prevIdx = idx;
    }
    function update(data, initialize) {
      var xScale = d3.scalePoint().domain(data.map(function(d) { return d.country; })).range([0, width]);
      var yScale = d3.scaleLinear().domain(d3.extent(data, function(d) { return d.percentage; })).range([height, 0]);
      // Used for mapping x coordinate to ordinal data
      var mapXCoordToCountry = d3.scaleQuantize().domain(xScale.range()).range(xScale.domain());
      // Used for mapping ordinal data to data index
      var mapCountryToIdx = d3.scaleBand().domain(data.map(function(d) { return d.country; })).range([0, data.length]);
      // Set up area, line, gridline
      var area = d3.area()
          .x(function(d) { return xScale(d.country); })
          .y0(height)
          .y1(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);
      var line = d3.line()
          .x(function(d) { return xScale(d.country); })
          .y(function(d) { return yScale(d.percentage); })
          .curve(d3.curveMonotoneX);

      if (initialize) {
        // Draw area
        g.append("path")
            .data([data])
            .attr("class", "area")
            .attr("d", area)
            .style('fill', 'url(#gradient)');
        // Draw x-axis
        g.append("g")
            .attr("class", "axis x-axis")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
        // Remove x-axis domain
        // g.select(".x-axis")
        //     .selectAll(".domain")
        //     .remove();
        // Draw y-axis
        g.append("g")
            .attr("class", "axis y-axis")
            .call(d3.axisLeft(yScale))
            .selectAll("text")
            .attr("class", "y-axis-text")
        // Draw y-axis label
        g.select(".y-axis")
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", -70)
            .text("Percentage (%)");
        g.select(".y-axis")
            .append("text")
            .attr("id", "timestamp")
            .attr("x", "45px")
            .attr("y", -(margin.top / 2))
            .text(timestamp + " Nominees");
        // Draw trendline
        g.append("path")
            .datum(data)
            .attr("class", "trendline")
            .attr("d", line);
        // Draw guideline
        g.append("line")
            .attr("class", "guideline")
            .attr("x1", 0)
            .attr("y1", 0)
            .attr("x2", 0)
            .attr("y2", height);
        // Draw circle
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .attr("r", 3.5)
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        // Draw a transparent layer for detecting mouse event
        g.append("rect")
            .attr("class", "overlay")
            .attr("width", width)
            .attr("height", height)
            .attr("opacity", 0)
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
      } else {
        g.select(".area")
            .data([data])
            .transition()
            .duration(500)
            .attr("d", area);
        g.select(".trendline")
            .datum(data)
            .transition()
            .duration(500)
            .attr("d", line);
        // Exit circles
        g.selectAll("circle")
            .data(data)
          .exit()
            .remove();
        // Enter circles
        g.selectAll("circle")
            .data(data)
          .enter()
            .append("circle")
            .attr("class", "circle")
            .attr("r", 3.5);
        // Update circles
        g.selectAll("circle")
            .data(data)
            .style("fill", "white")
            .attr("r", 3.5)
            .transition()
            .duration(500)
            .attr("cx", function(d) { return xScale(d.country); })
            .attr("cy", function(d) { return yScale(d.percentage); })
            .filter(function(d, i) { return i === 0 })
            .style("fill", "#0f747f")
            .attr("r", 5);
        g.selectAll(".x-axis")
            .transition()
            .duration(500)
            .call(d3.axisBottom(xScale))
            .selectAll("text")
            .attr("class", "x-axis-text")
            .attr("dx", "-0.8em")
            .attr("dy", "0.15em")
            .style("transform", "rotate(-65deg)")
            .style("text-anchor", "end");
            // .selectAll(".domain")
            // .remove();
        // Update x-axis texts
        g.selectAll(".x-axis-text")
            .style("font-size", "10px")
            .filter(function(d, i) { return i === 0 })
            .style("font-size", "12px");
        g.selectAll(".y-axis")
            .transition()
            .duration(500)
            .call(d3.axisLeft(yScale));
        g.select(".overlay")
            .on("mousemove", function () {
              mousemove(this, data, xScale, yScale, mapXCoordToCountry, mapCountryToIdx);
            });
        prevIdx = 0;
      }
      g.select(".guideline")
          .transition()
          .duration(500)
          .attr("x1", 0)
          .attr("x2", 0);
      d3.select("#tooltip")
          .transition()
          .duration(500)
          .style("top", yScale(data[0].percentage) + margin.top + tipMargin.top + "px")
          .style("left", xScale(data[0].country) + margin.left + tipMargin.left + "px");
      d3.select("#tipTriangle")
          .style("transform", "rotate(45deg)")
          .transition()
          .duration(50)
          .style("bottom", "-35px")
          .style("left", "-15px");
      d3.select("#country")
          .text(data[0].country);
      d3.select("#percentage")
          .text(data[0].percentage.toFixed(11));
      d3.select("#submission")
          .text(data[0].submission);
      d3.select("#population")
          .text(numberWithCommas(data[0].population));
    }
    // Set up svg
    var svg = d3.select("svg"),
        margin = {
          top: 80,
          right: 80,
          bottom: 125,
          left: 100,
        },
        width = parseInt(d3.select("#chart").style("width")) - margin.left - margin.right,
        height = parseInt(d3.select("#chart").style("height")) - margin.top - margin.bottom;
    var g = svg.append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    // Set up gradient color
    var gradient = svg.append('defs')
        .append('linearGradient')
        .attr('id', 'gradient')
        .attr('x1', '0%')
        .attr('y1', '0%')
        .attr('x2', '0%')
        .attr('y2', '100%')
        .attr('spreadMethod', 'pad');
    gradient.append('stop')
        .attr('offset', '0%')
        .attr('stop-color', 'rgb(36,175,192)')
        .attr('stop-opacity', 1);
    gradient.append('stop')
        .attr('offset', '100%')
        .attr('stop-color', 'rgb(206,254,226)')
        .attr('stop-opacity', 1);
    var tipMargin = {
      top: -60,
      left: 10,
    };
    update(data[curKey], true);
  }
  // Read data
  d3.queue()
    .defer(d3.csv, './data/result (nominees).csv')
    .defer(d3.csv, './data/result (honorable).csv')
    .defer(d3.csv, './data/result (developer).csv')
    .defer(d3.csv, './data/result (sites_of_the_day).csv')
    .defer(d3.csv, './data/result (sites_of_the_month).csv')
    .defer(d3.csv, './data/result (sites_of_the_year).csv')
    .await(analyze);
});
<|endoftext|>