<commit_before>// Globals
var API_SERVER = 'https://successwhale-api.herokuapp.com/v3';
var NARROW_SCREEN_WIDTH = 800;

// Viewmodel for SW
function SWUserViewModel() {
  var self = this;

  // SuccessWhale user API token
  self.token = ko.observable();

  // Display setting - number of columns per screen (honoured on wide screens only)
  self.colsPerScreen = ko.observable(3);
  // Display setting - user theme
  self.theme = ko.observable('default');
  // Display setting - highlight items newer than this (mins)
  self.highlightTime = ko.observable(0);
  // Display setting - show inline media
  self.inlineMedia = ko.observable(true);
  // Which SuccessWhale service accounts to post to
  self.postToAccounts = ko.observableArray();
  // Giant data blob of all data from all columns
  self.columns = ko.observableArray();

  // Content of the post entry box
  self.postEntryText = ko.observable('');
  // Using mobile view?
  self.mobileView = ko.observable(false);
  // Currently shown column in mobile view
  self.mobileCurrentColumn = ko.observable(0);

  self.postToAccountsString = ko.computed(function () {
     return getPostToAccountsString(self.postToAccounts());
  });
}

// Activate knockout.js
viewModel = new SWUserViewModel();
ko.applyBindings(viewModel, document.getElementById("htmlTop")); // htmlTop is a hack to allow knockout to data-bind elements in the

// Add a Knockout "visible" binding with animation to the handlers list
ko.bindingHandlers.slideVisible = {
    init: function(element, valueAccessor) {
        // Initially set the element to be instantly visible/hidden depending on the value
        var value = valueAccessor();
        $(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable
    },
    update: function(element, valueAccessor) {
        // Whenever the value subsequently changes, slide the element in or out
        var value = valueAccessor();
        ko.unwrap(value) ? $(element).slideDown() : $(element).slideUp();
    }
};

// Set up a jQuery Form for the main and reply post forms so we can submit them in an
// AJAXy way, and handle success/failure of the post with callbacks
var postItemOptions = {
  url:        API_SERVER+'/item',
  type:       'POST',
  dataType:   'json',
  success:    function(jsonResponse) {
    showSuccess('Item posted.');
    // Clear inputs
    $('form#postform #postentry').val("");
    $('form#postform #filetoupload').clearFields();
    $('.replyentry').each(function() {
        $(this).val("");
    });
    // Reload everything
    refreshColumns();
  },
  error:      function(jsonResponse) {
    showError('Item could not be posted due to an error.', JSON.stringify(jsonResponse));
  }
};

// Checks the user is logged in (via a cookie) - if not, punts them
// to the login page
function checkLoggedIn() {
  if (!readCookie('token')) {
    window.location = '/login';
  } else {
    viewModel.token(readCookie('token'));
  }
}

// Shows an error box for a set time, with the supplied HTML and optionally also
// a SuccessWhale API error message extracted from the returnedData of an unsuccessful
//  request
function showError(html, returnedData) {
  if (typeof(returnedData) != "undefined") {
    if (returnedData.responseText) {
      html += "
The SuccessWhale API reported the following error:
" + JSON.parse(returnedData.responseText).error
    }
  }
  $('#errorbox').html(html);
  $('#errorbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#errorbox').hide('slow');
    }, 5000);
  });
}

// Shows a success notification for a set time, with the supplied HTML content
function showSuccess(html) {
  $('#successbox').html(html);
  $('#successbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#successbox').hide('slow');
    }, 5000);
  });
}

// Turn an item's text into proper HTML
function makeItemTextHTML(content) {
  return linkify_entities(content, viewModel.inlineMedia());
}

// Load feed for a single column
function loadFeedForColumn(j) {
  viewModel.columns()[j].loading(true);
  var jqxhr = $.get(API_SERVER+'/feed', {sources: viewModel.columns()[j].fullpath, token: viewModel.token()})
    .done(function(returnedData) {
      // Add dummy observables to manage the inline display of thread and comment boxes
      var i = 0;
      for (; i<returnedData.items.length; i++) {
        returnedData.items[i].replyvisible = ko.observable(false);
        returnedData.items[i].threadvisible = ko.observable(false);
        returnedData.items[i].threadloading = ko.observable(false);
        returnedData.items[i].thread = ko.observableArray();
      }
      // Put all the items into the viewmodel for display
      viewModel.columns()[j].items.removeAll();
      viewModel.columns()[j].items.push.apply(viewModel.columns()[j].items, returnedData.items);
      // Add fancybox to items that need it
      $('.fancybox').each(function() {
        $(this).fancybox({
          'scrolling'   : 'no'
        });
      });
      // Add autosize to items that need it
      $('.replyentry').each(function() {
        $(this).autosize();
      });
      // Bind the reply forms
      $('.replyentry').each(function() {
        $(this).keydown(function (e) {
          if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
            $(this).parents('form:first').submit();
          }
        });
      });
      $('.replybutton').each(function() {
        $(this).click(function (e) {
          $(this).parents('form:first').submit();
        });
      });
      $('form.replyform').ajaxForm(postItemOptions);
      // Set the loading status to false
      viewModel.columns()[j].loading(false);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column "' + viewModel.columns()[j].title + '"', returnedData);
      viewModel.columns()[j].loading(false);
    });
}

// Load the thread for a single item into its "thread" array
// Set skipfirst = true for items where the original post is already displayed, or false
// where it isn't (e.g. Facebook notifications, where we need to display the post that
// the notification was about
function loadThreadForItem(item, skipfirst) {
  item.threadloading(true);
  var jqxhr = $.get(API_SERVER+'/thread', {service: item.service, uid: item.fetchedforuserid, postid: item.content.replytoid, skipfirst: skipfirst, token: viewModel.token()})
    .done(function(returnedData) {
      item.thread.removeAll();
      item.thread.push.apply(item.thread, returnedData.items);
      item.threadloading(false);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch thread for this item.', returnedData);
      item.threadloading(false);
    });
}

// Fetch and display column content, with a delay between each load to avoid overloading
// the browser and the API.
function refreshColumns() {
  var j = 0;
  for (; j < viewModel.columns().length; j++) {
    (function(index) {
        setTimeout(function() { loadFeedForColumn(index); }, 5000*index);
    })(j);
  }

  // Hide loading overlay, if it's displayed, as we have now fully loaded the display.
  $('body').removeClass("loading");
}

// Get the user's display settings
function getDisplaySettings() {
  var jqxhr = $.get(API_SERVER+'/displaysettings', {token: viewModel.token()})
    .done(function(returnedData) {
      // Store the columns-per-screen value for use in rendering
      // (ignored in mobile view)
      viewModel.colsPerScreen(returnedData.colsperscreen);
      // Get theme, highlight time and inline media setting
      viewModel.theme(returnedData.theme);
      viewModel.highlightTime(returnedData.highlighttime);
      viewModel.inlineMedia(returnedData.inlinemedia);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch display settings', returnedData);
    });
}

// Fetch and display the list of accounts to post to
function displayPostToAccounts() {
  var jqxhr = $.get(API_SERVER+'/posttoaccounts', {token: viewModel.token()})
    .done(function(returnedData) {
      var accounts = returnedData.posttoaccounts
      var i = 0;
      for (; i<accounts.length; i++) {
        accounts[i].enabled = ko.observable(accounts[i].enabled);
      }
      viewModel.postToAccounts.push.apply(viewModel.postToAccounts, accounts);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch account list', returnedData);
    });
}

// Fetch and display columns
function displayColumns() {
  var jqxhr = $.get(API_SERVER+'/columns', {token: viewModel.token()})
    .done(function(returnedData) {
      var cols = returnedData.columns;

      // Add a dummy observableArray to hold items later, and an observable for the
      // loading state
      var i = 0;
      for (; i<cols.length; i++) {
        cols[i].items = ko.observableArray();
        cols[i].loading = ko.observable(false);
      }

      // Update the view model
      viewModel.columns.push.apply(viewModel.columns, cols);

      // Refresh all columns to pull in items
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column list', returnedData);
    });
}

// Perform an action on an item. 2013 winner of the "most generic function description"
// award. See https://github.com/ianrenton/successwhale-api/blob/master/docs/action.md
function performAction(params) {
  // We have already been given all the params we need, except for the token
  params['token'] = viewModel.token();
  // Now make the call
  var jqxhr = $.post(API_SERVER+'/action', params)
    .done(function(returnedData) {
      showSuccess('Item ' + params['action'] + ' successful');
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to ' + params['action'] + ' the item', returnedData);
    });
}

// Build a list of service/uid:service/uid... for every service we have selected
function getPostToAccountsString(postToAccounts) {
  var postToAccountString = '';
  for (i=0; i<postToAccounts.length; i++) {
    if (postToAccounts[i].enabled()) {
      postToAccountString += postToAccounts[i].service + "/" + postToAccounts[i].uid + ":";
    }
  }
  return postToAccountString;
}

// Automatic stuff on page load
$(document).ready(function() {

  // Loading overlay. Hidden at the end of displayColumns().
  $('body').addClass("loading");

  // Check window size
  viewModel.mobileView($(window).width() <= NARROW_SCREEN_WIDTH);

  // Bind "post item" on button click or textarea Ctrl+Enter
  // and remove focus on Escape
  $('#postentry').keydown(function (e) {
    if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
      $('form#postform').submit();
    } else if (e.keyCode == 27) {
      $('#postentry').blur();
    }
  });
  $('#postbutton').click(function (e) {
    $('form#postform').submit();
  });
  $('form#postform').ajaxForm(postItemOptions);

  // Bind the "T" key when an input is unfocussed to focus on the
  // text entry box.
  $(document).keydown(function (e) {
    if ($(e.target).is('input')) {
      return;
    } else if (e.keyCode == 84) {
      $('#postentry').focus();
      return false; // prevent 't' being typed in post entry box
    }
  });

  // Bind Attach File clear button
  $('input#fileclearbutton').click(function (e) {
    $('form#postform #filetoupload').clearFields();
  });

  // Bind gpopover items
  $('#posttoaccountsbutton').gpopover({preventHide: true});
  $('#attachbutton').gpopover({preventHide: true});

  // Bind other menu buttons
  $('#logoutbutton').click(function (e) {
    eraseCookie('token');
    window.location = '/';
  });

  // Enable autosize on post entry box
  $('#postentry').autosize();

  // Bind swipe actions for mobile
  if (viewModel.mobileView()) {
    $("#columns").swipe( {
      swipeLeft:function(event, direction, distance, duration, fingerCount) {
        if (viewModel.mobileCurrentColumn() < viewModel.columns().length-1) {
          viewModel.mobileCurrentColumn(viewModel.mobileCurrentColumn()+1);
        }
      },
      swipeRight:function(event, direction, distance, duration, fingerCount) {
        if (viewModel.mobileCurrentColumn() > 0) {
          viewModel.mobileCurrentColumn(viewModel.mobileCurrentColumn()-1);
        }
      },
      threshold:25
    });
  }

  // Main API calls to display data
  checkLoggedIn();
  getDisplaySettings();
  displayPostToAccounts();
  displayColumns();

  // Refresh every 5 minutes
  setInterval( function() { refreshColumns(); }, 300000);
});
<commit_msg>Scroll to top when swiping columns in mobile view<commit_after>// Globals
var API_SERVER = 'https://successwhale-api.herokuapp.com/v3';
var NARROW_SCREEN_WIDTH = 800;

// Viewmodel for SW
function SWUserViewModel() {
  var self = this;

  // SuccessWhale user API token
  self.token = ko.observable();

  // Display setting - number of columns per screen (honoured on wide screens only)
  self.colsPerScreen = ko.observable(3);
  // Display setting - user theme
  self.theme = ko.observable('default');
  // Display setting - highlight items newer than this (mins)
  self.highlightTime = ko.observable(0);
  // Display setting - show inline media
  self.inlineMedia = ko.observable(true);
  // Which SuccessWhale service accounts to post to
  self.postToAccounts = ko.observableArray();
  // Giant data blob of all data from all columns
  self.columns = ko.observableArray();

  // Content of the post entry box
  self.postEntryText = ko.observable('');
  // Using mobile view?
  self.mobileView = ko.observable(false);
  // Currently shown column in mobile view
  self.mobileCurrentColumn = ko.observable(0);

  self.postToAccountsString = ko.computed(function () {
     return getPostToAccountsString(self.postToAccounts());
  });
}

// Activate knockout.js
viewModel = new SWUserViewModel();
ko.applyBindings(viewModel, document.getElementById("htmlTop")); // htmlTop is a hack to allow knockout to data-bind elements in the

// Add a Knockout "visible" binding with animation to the handlers list
ko.bindingHandlers.slideVisible = {
    init: function(element, valueAccessor) {
        // Initially set the element to be instantly visible/hidden depending on the value
        var value = valueAccessor();
        $(element).toggle(ko.unwrap(value)); // Use "unwrapObservable" so we can handle values that may or may not be observable
    },
    update: function(element, valueAccessor) {
        // Whenever the value subsequently changes, slide the element in or out
        var value = valueAccessor();
        ko.unwrap(value) ? $(element).slideDown() : $(element).slideUp();
    }
};

// Set up a jQuery Form for the main and reply post forms so we can submit them in an
// AJAXy way, and handle success/failure of the post with callbacks
var postItemOptions = {
  url:        API_SERVER+'/item',
  type:       'POST',
  dataType:   'json',
  success:    function(jsonResponse) {
    showSuccess('Item posted.');
    // Clear inputs
    $('form#postform #postentry').val("");
    $('form#postform #filetoupload').clearFields();
    $('.replyentry').each(function() {
        $(this).val("");
    });
    // Reload everything
    refreshColumns();
  },
  error:      function(jsonResponse) {
    showError('Item could not be posted due to an error.', JSON.stringify(jsonResponse));
  }
};

// Checks the user is logged in (via a cookie) - if not, punts them
// to the login page
function checkLoggedIn() {
  if (!readCookie('token')) {
    window.location = '/login';
  } else {
    viewModel.token(readCookie('token'));
  }
}

// Shows an error box for a set time, with the supplied HTML and optionally also
// a SuccessWhale API error message extracted from the returnedData of an unsuccessful
//  request
function showError(html, returnedData) {
  if (typeof(returnedData) != "undefined") {
    if (returnedData.responseText) {
      html += "
The SuccessWhale API reported the following error:
" + JSON.parse(returnedData.responseText).error
    }
  }
  $('#errorbox').html(html);
  $('#errorbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#errorbox').hide('slow');
    }, 5000);
  });
}

// Shows a success notification for a set time, with the supplied HTML content
function showSuccess(html) {
  $('#successbox').html(html);
  $('#successbox').show('slow', function hideLater() {
    setTimeout(function() {
      $('#successbox').hide('slow');
    }, 5000);
  });
}

// Turn an item's text into proper HTML
function makeItemTextHTML(content) {
  return linkify_entities(content, viewModel.inlineMedia());
}

// Load feed for a single column
function loadFeedForColumn(j) {
  viewModel.columns()[j].loading(true);
  var jqxhr = $.get(API_SERVER+'/feed', {sources: viewModel.columns()[j].fullpath, token: viewModel.token()})
    .done(function(returnedData) {
      // Add dummy observables to manage the inline display of thread and comment boxes
      var i = 0;
      for (; i<returnedData.items.length; i++) {
        returnedData.items[i].replyvisible = ko.observable(false);
        returnedData.items[i].threadvisible = ko.observable(false);
        returnedData.items[i].threadloading = ko.observable(false);
        returnedData.items[i].thread = ko.observableArray();
      }
      // Put all the items into the viewmodel for display
      viewModel.columns()[j].items.removeAll();
      viewModel.columns()[j].items.push.apply(viewModel.columns()[j].items, returnedData.items);
      // Add fancybox to items that need it
      $('.fancybox').each(function() {
        $(this).fancybox({
          'scrolling'   : 'no'
        });
      });
      // Add autosize to items that need it
      $('.replyentry').each(function() {
        $(this).autosize();
      });
      // Bind the reply forms
      $('.replyentry').each(function() {
        $(this).keydown(function (e) {
          if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
            $(this).parents('form:first').submit();
          }
        });
      });
      $('.replybutton').each(function() {
        $(this).click(function (e) {
          $(this).parents('form:first').submit();
        });
      });
      $('form.replyform').ajaxForm(postItemOptions);
      // Set the loading status to false
      viewModel.columns()[j].loading(false);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column "' + viewModel.columns()[j].title + '"', returnedData);
      viewModel.columns()[j].loading(false);
    });
}

// Load the thread for a single item into its "thread" array
// Set skipfirst = true for items where the original post is already displayed, or false
// where it isn't (e.g. Facebook notifications, where we need to display the post that
// the notification was about
function loadThreadForItem(item, skipfirst) {
  item.threadloading(true);
  var jqxhr = $.get(API_SERVER+'/thread', {service: item.service, uid: item.fetchedforuserid, postid: item.content.replytoid, skipfirst: skipfirst, token: viewModel.token()})
    .done(function(returnedData) {
      item.thread.removeAll();
      item.thread.push.apply(item.thread, returnedData.items);
      item.threadloading(false);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch thread for this item.', returnedData);
      item.threadloading(false);
    });
}

// Fetch and display column content, with a delay between each load to avoid overloading
// the browser and the API.
function refreshColumns() {
  var j = 0;
  for (; j < viewModel.columns().length; j++) {
    (function(index) {
        setTimeout(function() { loadFeedForColumn(index); }, 5000*index);
    })(j);
  }

  // Hide loading overlay, if it's displayed, as we have now fully loaded the display.
  $('body').removeClass("loading");
}

// Get the user's display settings
function getDisplaySettings() {
  var jqxhr = $.get(API_SERVER+'/displaysettings', {token: viewModel.token()})
    .done(function(returnedData) {
      // Store the columns-per-screen value for use in rendering
      // (ignored in mobile view)
      viewModel.colsPerScreen(returnedData.colsperscreen);
      // Get theme, highlight time and inline media setting
      viewModel.theme(returnedData.theme);
      viewModel.highlightTime(returnedData.highlighttime);
      viewModel.inlineMedia(returnedData.inlinemedia);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch display settings', returnedData);
    });
}

// Fetch and display the list of accounts to post to
function displayPostToAccounts() {
  var jqxhr = $.get(API_SERVER+'/posttoaccounts', {token: viewModel.token()})
    .done(function(returnedData) {
      var accounts = returnedData.posttoaccounts
      var i = 0;
      for (; i<accounts.length; i++) {
        accounts[i].enabled = ko.observable(accounts[i].enabled);
      }
      viewModel.postToAccounts.push.apply(viewModel.postToAccounts, accounts);
    })
    .fail(function(returnedData) {
      showError('Failed to fetch account list', returnedData);
    });
}

// Fetch and display columns
function displayColumns() {
  var jqxhr = $.get(API_SERVER+'/columns', {token: viewModel.token()})
    .done(function(returnedData) {
      var cols = returnedData.columns;

      // Add a dummy observableArray to hold items later, and an observable for the
      // loading state
      var i = 0;
      for (; i<cols.length; i++) {
        cols[i].items = ko.observableArray();
        cols[i].loading = ko.observable(false);
      }

      // Update the view model
      viewModel.columns.push.apply(viewModel.columns, cols);

      // Refresh all columns to pull in items
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to fetch column list', returnedData);
    });
}

// Perform an action on an item. 2013 winner of the "most generic function description"
// award. See https://github.com/ianrenton/successwhale-api/blob/master/docs/action.md
function performAction(params) {
  // We have already been given all the params we need, except for the token
  params['token'] = viewModel.token();
  // Now make the call
  var jqxhr = $.post(API_SERVER+'/action', params)
    .done(function(returnedData) {
      showSuccess('Item ' + params['action'] + ' successful');
      refreshColumns();
    })
    .fail(function(returnedData) {
      showError('Failed to ' + params['action'] + ' the item', returnedData);
    });
}

// Build a list of service/uid:service/uid... for every service we have selected
function getPostToAccountsString(postToAccounts) {
  var postToAccountString = '';
  for (i=0; i<postToAccounts.length; i++) {
    if (postToAccounts[i].enabled()) {
      postToAccountString += postToAccounts[i].service + "/" + postToAccounts[i].uid + ":";
    }
  }
  return postToAccountString;
}

// Automatic stuff on page load
$(document).ready(function() {

  // Loading overlay. Hidden at the end of displayColumns().
  $('body').addClass("loading");

  // Check window size
  viewModel.mobileView($(window).width() <= NARROW_SCREEN_WIDTH);

  // Bind "post item" on button click or textarea Ctrl+Enter
  // and remove focus on Escape
  $('#postentry').keydown(function (e) {
    if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
      $('form#postform').submit();
    } else if (e.keyCode == 27) {
      $('#postentry').blur();
    }
  });
  $('#postbutton').click(function (e) {
    $('form#postform').submit();
  });
  $('form#postform').ajaxForm(postItemOptions);

  // Bind the "T" key when an input is unfocussed to focus on the
  // text entry box.
  $(document).keydown(function (e) {
    if ($(e.target).is('input')) {
      return;
    } else if (e.keyCode == 84) {
      $('#postentry').focus();
      return false; // prevent 't' being typed in post entry box
    }
  });

  // Bind Attach File clear button
  $('input#fileclearbutton').click(function (e) {
    $('form#postform #filetoupload').clearFields();
  });

  // Bind gpopover items
  $('#posttoaccountsbutton').gpopover({preventHide: true});
  $('#attachbutton').gpopover({preventHide: true});

  // Bind other menu buttons
  $('#logoutbutton').click(function (e) {
    eraseCookie('token');
    window.location = '/';
  });

  // Enable autosize on post entry box
  $('#postentry').autosize();

  // Bind swipe actions for mobile
  if (viewModel.mobileView()) {
    $("#columns").swipe( {
      swipeLeft:function(event, direction, distance, duration, fingerCount) {
        if (viewModel.mobileCurrentColumn() < viewModel.columns().length-1) {
          viewModel.mobileCurrentColumn(viewModel.mobileCurrentColumn()+1);
          window.scrollTo(0, 0);
        }
      },
      swipeRight:function(event, direction, distance, duration, fingerCount) {
        if (viewModel.mobileCurrentColumn() > 0) {
          viewModel.mobileCurrentColumn(viewModel.mobileCurrentColumn()-1);
          window.scrollTo(0, 0);
        }
      },
      threshold:25
    });
  }

  // Main API calls to display data
  checkLoggedIn();
  getDisplaySettings();
  displayPostToAccounts();
  displayColumns();

  // Refresh every 5 minutes
  setInterval( function() { refreshColumns(); }, 300000);
});
<|endoftext|>


<commit_before>var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var raygun = require('raygun');

var routes = require('./routes/index');
var config = require('./config.js');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

var nocache = function (req, res, next) {
  res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
  res.header('Expires', '-1');
  res.header('Pragma', 'no-cache');
  next();
};
app.use(nocache);

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser('ultrasecretcookiesecret'));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// Raygun
var raygunClient = new raygun.Client().init({ apiKey: config.raygunApiKey });

// User tracking
raygunClient.user = function(req){
  var token = req.signedCookies.token;
  var username = req.signedCookies.username;
  return {
    isAnonymous: !token,
    identifier: token || "",
    fullName: username
  };
};

app.use(function(err, req, res, next) {
  if(err){
    raygunClient.send(err, {}, function() {
      next(err);
    }, req);
  }
  else {
    next(err);
  }
});

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;
<commit_msg>Only set nocache headers on the actual routes, not the assets<commit_after>var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var raygun = require('raygun');

var routes = require('./routes/index');
var config = require('./config.js');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

var nocache = function (req, res, next) {
  res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
  res.header('Expires', '-1');
  res.header('Pragma', 'no-cache');
  next();
};

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser('ultrasecretcookiesecret'));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', [nocache, routes]);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// Raygun
var raygunClient = new raygun.Client().init({ apiKey: config.raygunApiKey });

// User tracking
raygunClient.user = function(req){
  var token = req.signedCookies.token;
  var username = req.signedCookies.username;
  return {
    isAnonymous: !token,
    identifier: token || "",
    fullName: username
  };
};

app.use(function(err, req, res, next) {
  if(err){
    raygunClient.send(err, {}, function() {
      next(err);
    }, req);
  }
  else {
    next(err);
  }
});

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

module.exports = app;
<|endoftext|>


<commit_before>
/*!--------------------------------*\
   3-Jekyll Theme
   @author Peiwen Lu (P233)
   https://github.com/P233/3-Jekyll
\*---------------------------------*/

// Detect window size, if less than 1280px add class 'mobile' to sidebar therefore it will be auto hide when trigger the pjax request in small screen devices.
if ($(window).width() <= 1280) {
  $('#sidebar').addClass('mobile')
}

// Variables
var sidebar    = $('#sidebar'),
    container  = $('#post'),
    content    = $('#pjax'),
    button     = $('#icon-arrow');

// Tags switcher
var clickHandler = function(id) {
  return function() {
    $(this).addClass('active').siblings().removeClass('active');
    $('.pl__all').hide();
    $('.' + id).delay(50).fadeIn(350);
  }
};

$('#tags__ul li').each(function(index){
  $('#' + $(this).attr('id')).on('click', clickHandler($(this).attr('id')));
});

// If sidebar has class 'mobile', hide it after clicking.
$('.pl__all').on('click', function() {
  $(this).addClass('active').siblings().removeClass('active');
  if (sidebar.hasClass('mobile')) {
    $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
  }
});

// Enable fullscreen.
$('#js-fullscreen').on('click', function() {
  if (button.hasClass('fullscreen')) {
    sidebar.removeClass('fullscreen');
    button.removeClass('fullscreen');
    content.delay(300).queue(function(){
      $(this).removeClass('fullscreen').dequeue();
    });
  } else {
    sidebar.addClass('fullscreen');
    button.addClass('fullscreen');
    content.delay(200).queue(function(){
      $(this).addClass('fullscreen').dequeue();
    });
  }
});

$('#mobile-avatar').on('click', function(){
  $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
});

// Pjax
$(document).pjax('#avatar, #mobile-avatar, .pl__all', '#pjax', { fragment: '#pjax', timeout: 10000 });
$(document).on({
  'pjax:click': function() {
    content.removeClass('fadeIn').addClass('fadeOut');
    NProgress.start();
  },
  'pjax:start': function() {
    content.css({'opacity':0});
  },
  'pjax:end': function() {
    NProgress.done();
    container.scrollTop(0);
    content.css({'opacity':1}).removeClass('fadeOut').addClass('fadeIn');
    afterPjax();
  }
});

// Re-run scripts for post content after pjax
function afterPjax() {
  // Open links in new tab
  $('#post__content a').attr('target','_blank');

  // Generate post TOC for h1 h2 and h3
  var toc = $('#post__toc-ul');
  // Empty TOC and generate an entry for h1
  toc.empty().append('
' + $('#post__title').text() + '
');

  // Generate entries for h2 and h3
  $('#post__content').children('h2,h3').each(function() {
    // Generate random ID for each heading
    $(this).attr('id', function() {
      var ID = "",
          alphabet = "abcdefghijklmnopqrstuvwxyz";

      for(var i=0; i < 5; i++) {
        ID += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
      }
      return ID;
    });

    if ($(this).prop("tagName") == 'H2') {
      toc.append('
' + $(this).text() + '
');
    } else {
      toc.append('
' + $(this).text() + '
');
    }
  });

  // Smooth scrolling
  $('.js-anchor-link').on('click', function() {
    var target = $(this.hash);
    container.animate({scrollTop: target.offset().top + container.scrollTop() - 70}, 500, function() {
      target.addClass('flash').delay(700).queue(function() {
        $(this).removeClass('flash').dequeue();
      });
    });
  });

  // Lazy Loading Disqus
  // http://jsfiddle.net/dragoncrew/SHGwe/1/
  var ds_loaded = false,
      top = $('#disqus_thread').offset().top;
  window.disqus_shortname = $('#disqus_thread').attr('name');

  function check() {
    if ( !ds_loaded && container.scrollTop() + container.height() > top ) {
      $.ajax({
        type: 'GET',
        url: 'http://' + disqus_shortname + '.disqus.com/embed.js',
        dataType: 'script',
        cache: true
      });
      ds_loaded = true;
    }
  }check();
  container.scroll(check);
}afterPjax();

<commit_msg>update script.js<commit_after>
/*!--------------------------------*\
   3-Jekyll Theme
   @author Peiwen Lu (P233)
   https://github.com/P233/3-Jekyll
\*---------------------------------*/

// Detect window size, if less than 1280px add class 'mobile' to sidebar therefore it will be auto hide when trigger the pjax request in small screen devices.
if ($(window).width() <= 1280) {
  $('#sidebar').addClass('mobile')
}

// Variables
var sidebar    = $('#sidebar'),
    container  = $('#post'),
    content    = $('#pjax'),
    button     = $('#icon-arrow');

// Tags switcher
var clickHandler = function(id) {
  return function() {
    $(this).addClass('active').siblings().removeClass('active');
    $('.pl__all').hide();
    $('.' + id).delay(50).fadeIn(350);
  }
};

$('#tags__ul li').each(function(index){
  $('#' + $(this).attr('id')).on('click', clickHandler($(this).attr('id')));
});

// If sidebar has class 'mobile', hide it after clicking.
$('.pl__all').on('click', function() {
  $(this).addClass('active').siblings().removeClass('active');
  if (sidebar.hasClass('mobile')) {
    $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
  }
});

// Enable fullscreen.
$('#js-fullscreen').on('click', function() {
  if (button.hasClass('fullscreen')) {
    sidebar.removeClass('fullscreen');
    button.removeClass('fullscreen');
    content.delay(300).queue(function(){
      $(this).removeClass('fullscreen').dequeue();
    });
  } else {
    sidebar.addClass('fullscreen');
    button.addClass('fullscreen');
    content.delay(200).queue(function(){
      $(this).addClass('fullscreen').dequeue();
    });
  }
});

$('#mobile-avatar').on('click', function(){
  $('#sidebar, #pjax, #icon-arrow').addClass('fullscreen');
});

// Pjax
$(document).pjax('#avatar, #mobile-avatar, .pl__all', '#pjax', { fragment: '#pjax', timeout: 10000 });
$(document).on({
  'pjax:click': function() {
    content.removeClass('fadeIn').addClass('fadeOut');
    NProgress.start();
  },
  'pjax:start': function() {
    content.css({'opacity':0});
  },
  'pjax:end': function() {
    NProgress.done();
    container.scrollTop(0);
    content.css({'opacity':1}).removeClass('fadeOut').addClass('fadeIn');
    afterPjax();
  }
});

// Re-run scripts for post content after pjax
function afterPjax() {
  // Open links in new tab
  $('#post__content a').attr('target','_blank');

  // Generate post TOC for h1 h2 and h3
  var toc = $('#post__toc-ul');
  // Empty TOC and generate an entry for h1
  toc.empty().append('
' + $('#post__title').text() + '
');

  // Generate entries for h2 and h3
  $('#post__content').children('h2,h3').each(function() {
    // Generate random ID for each heading
    $(this).attr('id', function() {
      var ID = "",
          alphabet = "abcdefghijklmnopqrstuvwxyz";

      for(var i=0; i < 5; i++) {
        ID += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
      }
      return ID;
    });

    if ($(this).prop("tagName") == 'H2') {
      toc.append('
' + $(this).text() + '
');
    } else {
      toc.append('
' + $(this).text() + '
');
    }
  });

  // Smooth scrolling
  $('.js-anchor-link').on('click', function() {
    var target = $(this.hash);
    container.animate({scrollTop: target.offset().top + container.scrollTop() - 70}, 500, function() {
      target.addClass('flash').delay(700).queue(function() {
        $(this).removeClass('flash').dequeue();
      });
    });
  });

  // Lazy Loading Disqus
  // http://jsfiddle.net/dragoncrew/SHGwe/1/
  var ds_loaded = false,
      top = $('#disqus_thread').offset().top;
  window.disqus_shortname = $('#disqus_thread').attr('name');

  function check() {
    if ( !ds_loaded && container.scrollTop() + container.height() > top ) {
      $.ajax({
        type: 'GET',
        url: 'http://' + disqus_shortname + '.disqus.com/embed.js',
        dataType: 'script',
        cache: true
      });
      ds_loaded = true;
    }
  }check();
  container.scroll(check);
}afterPjax();

<|endoftext|>


<commit_before>/*
	cutis.js
*/

var cutis = {
	id : "cutis",
	baseUrl : "http://lukenickerson.github.io/ludum-dare-cutis/",
	skins : [],
	$elt : null,
	selectedSkinIndex : -1,
	loadCSS : function(url, classes){
		$('<link class="' + classes + '"></link>')
			.appendTo('head')
			.attr({type : 'text/css', rel : 'stylesheet'})
			.attr('href', url)
			.ready(function(){ console.log("Loaded", url); })
		;
	},
	loadJS : function(url, classes){
		$('')
			.appendTo('body')
			.attr('src', url)
			.ready(function(){ console.log("Loaded", url); })
		;
	},
	selectSkin : function(si){
		var c = this;
		c.$elt.find("cutis_skin_" + c.selectedSkinIndex).removeClass("selected");
		c.selectedSkinIndex = si;
		c.loadSkin();
		c.$elt.find("cutis_skin_" + si).addClass("selected");
	},
	loadSkin : function(){
		var c = this;
		var s = c.skins[c.selectedSkinIndex];
		console.log("Load Skin", c.selectedSkinIndex, s);
		c.removeSkins();
		if (!s.dir) s.dir = s.base;
		c.loadCSS(c.baseUrl + "skins/" + s.dir + "/" + s.base + ".css", "cutis_skin_file");
		c.loadJS(c.baseUrl + "skins/" + s.dir + "/" + s.base + ".js", "cutis_skin_file");
	},
	removeSkins : function(){
		$('.cutis_skin_file').remove();
	},
	loadSkinList : function(){
		var c = this;
		$.getJSON(
			c.baseUrl + "skin_list.json" + "?callback=?"
		).done(function(d){
			console.log("Loaded", d);
			c.skins = d;
			c.drawSkinList();
		}).fail(function(){

		});
	},
	drawSkinList : function(){
		var c = this;
		var h = '
Original
';
		for (var i = 0; i < c.skins.length; i++){
			h += '
'
				+ c.skins[i].display + '
';
		}
		c.$elt.find('.skinList').html(h);
	},
	build : function(){
		var c = this;
		var $s = $(
			'

Select a skin:
'
			+ '

'
			+ '
Skins provided by Cutis.
'
			+ '
'
			);
		$s.find('.skinList').on("click", "a", function(e){
				var si = parseInt($(this).data("skinindex"));
				if (si < 0) {
					c.removeSkins();
				} else {
					c.selectSkin(si);
				}
				e.preventDefault();
			});
		var $b = $('<button type="button">Skins</button>')
			.click(function(){
				// *** toggle open / closed
			});

		c.$elt = $('

')
			.append($s).append($b)
		;
	},
	init : function(){
		var c = this;
		c.build();
		c.loadSkinList();
		c.loadCSS("cutis.css");
		$('body')
			.find('#' + c.id).remove().end()
			.append(c.$elt);
	}
}
$(document).ready(function(){
	cutis.init();
});<commit_msg>JSONP Tests<commit_after>/*
	cutis.js
*/

var cutis = {
	id : "cutis",
	baseUrl : "http://lukenickerson.github.io/ludum-dare-cutis/",
	skins : [],
	$elt : null,
	selectedSkinIndex : -1,
	loadCSS : function(url, classes){
		$('<link class="' + classes + '"></link>')
			.appendTo('head')
			.attr({type : 'text/css', rel : 'stylesheet'})
			.attr('href', url)
			.ready(function(){ console.log("Loaded", url); })
		;
	},
	loadJS : function(url, classes){
		$('')
			.appendTo('body')
			.attr('src', url)
			.ready(function(){ console.log("Loaded", url); })
		;
	},
	selectSkin : function(si){
		var c = this;
		c.$elt.find("cutis_skin_" + c.selectedSkinIndex).removeClass("selected");
		c.selectedSkinIndex = si;
		c.loadSkin();
		c.$elt.find("cutis_skin_" + si).addClass("selected");
	},
	loadSkin : function(){
		var c = this;
		var s = c.skins[c.selectedSkinIndex];
		console.log("Load Skin", c.selectedSkinIndex, s);
		c.removeSkins();
		if (!s.dir) s.dir = s.base;
		c.loadCSS(c.baseUrl + "skins/" + s.dir + "/" + s.base + ".css", "cutis_skin_file");
		c.loadJS(c.baseUrl + "skins/" + s.dir + "/" + s.base + ".js", "cutis_skin_file");
	},
	removeSkins : function(){
		$('.cutis_skin_file').remove();
	},
	loadSkinList : function(){
		var c = this;
		// TEST 1
		$.getJSON(
			c.baseUrl + "skin_list.json"
			+ "?callback=?" // Make JSONP (http://stackoverflow.com/a/10872804/1766230)
		).done(function(d){
			console.log("Loaded", d);
			c.skins = d;
			c.drawSkinList();
		}).fail(function(){
			console.log(arguments);
		});
		// TEST 2
		$.ajax({
			url: c.baseUrl + "skin_list.json"
			,dataType: "jsonp"
		}).done(function(){
			console.log(arguments);
		}).fail(function(){
			console.log(arguments);
		});
	},
	drawSkinList : function(){
		var c = this;
		var h = '
Original
';
		for (var i = 0; i < c.skins.length; i++){
			h += '
'
				+ c.skins[i].display + '
';
		}
		c.$elt.find('.skinList').html(h);
	},
	build : function(){
		var c = this;
		var $s = $(
			'

Select a skin:
'
			+ '

'
			+ '
Skins provided by Cutis.
'
			+ '
'
			);
		$s.find('.skinList').on("click", "a", function(e){
				var si = parseInt($(this).data("skinindex"));
				if (si < 0) {
					c.removeSkins();
				} else {
					c.selectSkin(si);
				}
				e.preventDefault();
			});
		var $b = $('<button type="button">Skins</button>')
			.click(function(){
				// *** toggle open / closed
			});

		c.$elt = $('

')
			.append($s).append($b)
		;
	},
	init : function(){
		var c = this;
		c.build();
		c.loadSkinList();
		c.loadCSS(c.baseUrl + "cutis.css");
		$('body')
			.find('#' + c.id).remove().end()
			.append(c.$elt);
	}
}
$(document).ready(function(){
	cutis.init();
});<|endoftext|>


<commit_before>import { createSelector } from 'reselect'
import _ from 'lodash'

import getDauerBisFristMitarbeiter from '../src/getDauerBisFristMitarbeiter'
import getFristMitarbeiterWarnung from '../src/getFristMitarbeiterWarnung'
import getItKontoForVerantwortlich from '../src/getItKontoForVerantwortlich'

const getGeschaefte = (state) =>
  state.geschaefte.geschaefte
const getGki = (state) =>
  state.geschaefteKontakteIntern.geschaefteKontakteIntern
const getGke = (state) =>
  state.geschaefteKontakteExtern.geschaefteKontakteExtern
const getInterne = (state) =>
  state.geschaefte.interneOptions
const getExterne = (state) =>
  state.geschaefte.externeOptions
const getGeko = (state) =>
  state.geschaefte.geko
const getLinks = (state) =>
  state.geschaefte.links
const getFaelligeStati = (state) =>
  state.geschaefte.faelligeStatiOptions

export const getGeschaefteWithNSideData = createSelector(  // eslint-disable-line import/prefer-default-export
  [getGeschaefte, getGki, getGke, getInterne, getExterne, getGeko, getLinks, getFaelligeStati],
  (geschaefte, gki, gke, interne, externe, geko, links, faelligeStati) =>
    geschaefte.map((g) => {
      const newGeschaeft = _.clone(g)
      // newGeschaeft.verantwortlichName = ''
      // newGeschaeft.interne = []
      // newGeschaeft.verantwortlichItKonto = ''
      // newGeschaeft.externe = []
      // newGeschaeft.geko = []
      // newGeschaeft.links = []
      // newGeschaeft.kannFaelligSein = false
      // newGeschaeft.dauerBisFristMitarbeiter = ''
      // newGeschaeft.fristMitarbeiterWarnung = ''
      newGeschaeft.verantwortlichName = interne
        .filter(i => i.kurzzeichen === newGeschaeft.verantwortlich)
        .map(i =>
          (i.name ? `${i.vorname} ${i.name}` : '')
        )
        .join(', ')
      newGeschaeft.interne = gki
        .filter(i => i.idGeschaeft === newGeschaeft.idGeschaeft)
        .map((gk) =>
          interne.find(i => i.id === gk.idKontakt) || null
        )
      newGeschaeft.externe = gke
        .filter(i => i.idGeschaeft === newGeschaeft.idGeschaeft)
        .map((gk) =>
          externe.find(i => i.id === gk.idKontakt) || null
        )
      newGeschaeft.verantwortlichItKonto = getItKontoForVerantwortlich(interne, g.verantwortlich)
      newGeschaeft.geko = geko
        .filter(gko => gko.idGeschaeft === newGeschaeft.idGeschaeft)
      newGeschaeft.links = links
        .filter(link => link.idGeschaeft === newGeschaeft.idGeschaeft)
      newGeschaeft.kannFaelligSein = faelligeStati ? faelligeStati.includes(g.status) : false
      newGeschaeft.dauerBisFristMitarbeiter = getDauerBisFristMitarbeiter(newGeschaeft)
      newGeschaeft.fristMitarbeiterWarnung = getFristMitarbeiterWarnung(newGeschaeft.dauerBisFristMitarbeiter)
      return newGeschaeft
    })
)
<commit_msg>clean<commit_after>import { createSelector } from 'reselect'
import _ from 'lodash'

import getDauerBisFristMitarbeiter from '../src/getDauerBisFristMitarbeiter'
import getFristMitarbeiterWarnung from '../src/getFristMitarbeiterWarnung'
import getItKontoForVerantwortlich from '../src/getItKontoForVerantwortlich'

const getGeschaefte = (state) =>
  state.geschaefte.geschaefte
const getGki = (state) =>
  state.geschaefteKontakteIntern.geschaefteKontakteIntern
const getGke = (state) =>
  state.geschaefteKontakteExtern.geschaefteKontakteExtern
const getInterne = (state) =>
  state.geschaefte.interneOptions
const getExterne = (state) =>
  state.geschaefte.externeOptions
const getGeko = (state) =>
  state.geschaefte.geko
const getLinks = (state) =>
  state.geschaefte.links
const getFaelligeStati = (state) =>
  state.geschaefte.faelligeStatiOptions

export const getGeschaefteWithNSideData = createSelector(  // eslint-disable-line import/prefer-default-export
  [getGeschaefte, getGki, getGke, getInterne, getExterne, getGeko, getLinks, getFaelligeStati],
  (geschaefte, gki, gke, interne, externe, geko, links, faelligeStati) =>
    geschaefte.map((g) => {
      const newGeschaeft = _.clone(g)
      newGeschaeft.verantwortlichName = interne
        .filter(i => i.kurzzeichen === newGeschaeft.verantwortlich)
        .map(i =>
          (i.name ? `${i.vorname} ${i.name}` : '')
        )
        .join(', ')
      newGeschaeft.interne = gki
        .filter(i => i.idGeschaeft === newGeschaeft.idGeschaeft)
        .map((gk) =>
          interne.find(i => i.id === gk.idKontakt) || null
        )
      newGeschaeft.externe = gke
        .filter(i => i.idGeschaeft === newGeschaeft.idGeschaeft)
        .map((gk) =>
          externe.find(i => i.id === gk.idKontakt) || null
        )
      newGeschaeft.verantwortlichItKonto = getItKontoForVerantwortlich(interne, g.verantwortlich)
      newGeschaeft.geko = geko
        .filter(gko => gko.idGeschaeft === newGeschaeft.idGeschaeft)
      newGeschaeft.links = links
        .filter(link => link.idGeschaeft === newGeschaeft.idGeschaeft)
      newGeschaeft.kannFaelligSein = faelligeStati ? faelligeStati.includes(g.status) : false
      newGeschaeft.dauerBisFristMitarbeiter = getDauerBisFristMitarbeiter(newGeschaeft)
      newGeschaeft.fristMitarbeiterWarnung = getFristMitarbeiterWarnung(newGeschaeft.dauerBisFristMitarbeiter)
      return newGeschaeft
    })
)
<|endoftext|>


<commit_before>module.exports = function(grunt) {
	grunt.initConfig({
		jshint: {
			options: {
				jshintrc: '.jshintrc'
			},
			files: [
				'Gruntfile.js',
				'package.json',
				'js/**/*.js'
			]
		},
		connect: {
			www: {
				options: {
					base: '.',
					port: 4545
				}
			}
		},
		ghost: {
			dist: {
				filesSrc:  ['tests/integration/casperjs/*_test.js'],
				options: {
					args: {
						baseUrl: 'http://localhost:' +
							'<%= connect.www.options.port %>/'
					},
					direct: false,
					logLevel: 'error',
					printCommand: false,
					printFilePaths: true
				}
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-connect');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-ghost');

	grunt.registerTask('test', ['jshint', 'connect', 'ghost']);

};
<commit_msg>Set back original jshint path<commit_after>module.exports = function(grunt) {
	grunt.initConfig({
		jshint: {
			options: {
				jshintrc: '.jshintrc'
			},
			files: [
				'Gruntfile.js',
				'package.json',
				'js/*.js'
			]
		},
		connect: {
			www: {
				options: {
					base: '.',
					port: 4545
				}
			}
		},
		ghost: {
			dist: {
				filesSrc:  ['tests/integration/casperjs/*_test.js'],
				options: {
					args: {
						baseUrl: 'http://localhost:' +
							'<%= connect.www.options.port %>/'
					},
					direct: false,
					logLevel: 'error',
					printCommand: false,
					printFilePaths: true
				}
			}
		}
	});

	grunt.loadNpmTasks('grunt-contrib-connect');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-ghost');

	grunt.registerTask('test', ['jshint', 'connect', 'ghost']);

};
<|endoftext|>


<commit_before>var mappingDefaults = {
	styles: ['css', 'less', 'scss', 'sass', 'styl'],
	scripts: ['js', 'jsx', 'es6', 'es', 'coffee'],
	templates: ['jade', 'json', 'html', 'htm'],
	images: ['jpg', 'jpeg', 'png', 'svg'],
	sprites: ['svg']
}

module.exports = function(gulp, config, watch) {
	var mapping = require('lodash/object/merge')({}, mappingDefaults, config.mapping || {})

	var fileExtensions = ['css','styl','scss','sass','less','js','jsx','es6','es','coffee','jade','json','html','htm']
	var mappingRegEx = {}
	Object.keys(mapping).forEach(function(key) {
		mappingRegEx[key] = []
		mappingRegEx[key] = mapping[key].map(function(i) {
			var ext = '.' + i.replace(/^\./, '') // remove . from the beginning of the string
			if(fileExtensions.indexOf(key) == -1) {
				fileExtensions.push(ext.substring(1))
			}
			return new RegExp(ext + '$')
		})
	})

	return function(done) {
		var c = require('better-console')
		c.info('Watching sources for change and recompilation...')

		var log = require('better-console')
		var minimatch = require('minimatch')
		var path = require('path')
		var runcmd = require('../helpers/runcmd')
		var glob = '**/*.{' + fileExtensions.join() + '}'
		var subtractPaths = require('../helpers/subtractPaths')
		var distFolderRelative = subtractPaths(config.dist_folder, config.dir)
		var globDist = distFolderRelative + '/**/*.*'

		var completeHandler = function(e) {
			if(config.hooks && config.hooks.watch) {
				log.info('~ watch hook: ' + config.hooks.watch)
				runcmd(config.hooks.watch, config.dir, function() {
					log.info('/>')
				})
			}
		}

		var changeHandler = function(filepath) {
			// Filter dist sources
			if (minimatch(filepath, globDist)) {
				return
			}

			config._lastChanged = filepath

			var handlers = []

			Object.keys(mappingRegEx).forEach(function (key) {

				mappingRegEx[key].forEach(function(regexp) {
					if(filepath.match(regexp)) {
						handlers.push(key)
					}
				})
			})

			if (!handlers.length) {
				c.warn('! unknown extension', path.extname(filepath), filepath)
				return
			}

			handlers.forEach(function(handler) {
				gulp.start(handler, completeHandler)
			})

		}

		watch(glob, changeHandler)

		done()
	}
}
<commit_msg>watch_sources: added pug ext<commit_after>var mappingDefaults = {
	styles: ['css', 'less', 'scss', 'sass', 'styl'],
	scripts: ['js', 'jsx', 'es6', 'es', 'coffee'],
	templates: ['jade', 'json', 'html', 'htm'],
	images: ['jpg', 'jpeg', 'png', 'svg'],
	sprites: ['svg']
}

module.exports = function(gulp, config, watch) {
	var mapping = require('lodash/object/merge')({}, mappingDefaults, config.mapping || {})

	var fileExtensions = ['css','styl','scss','sass','less','js','jsx','es6','es','coffee','jade','pug','json','html','htm']
	var mappingRegEx = {}
	Object.keys(mapping).forEach(function(key) {
		mappingRegEx[key] = []
		mappingRegEx[key] = mapping[key].map(function(i) {
			var ext = '.' + i.replace(/^\./, '') // remove . from the beginning of the string
			if(fileExtensions.indexOf(key) == -1) {
				fileExtensions.push(ext.substring(1))
			}
			return new RegExp(ext + '$')
		})
	})

	return function(done) {
		var c = require('better-console')
		c.info('Watching sources for change and recompilation...')

		var log = require('better-console')
		var minimatch = require('minimatch')
		var path = require('path')
		var runcmd = require('../helpers/runcmd')
		var glob = '**/*.{' + fileExtensions.join() + '}'
		var subtractPaths = require('../helpers/subtractPaths')
		var distFolderRelative = subtractPaths(config.dist_folder, config.dir)
		var globDist = distFolderRelative + '/**/*.*'

		var completeHandler = function(e) {
			if(config.hooks && config.hooks.watch) {
				log.info('~ watch hook: ' + config.hooks.watch)
				runcmd(config.hooks.watch, config.dir, function() {
					log.info('/>')
				})
			}
		}

		var changeHandler = function(filepath) {
			// Filter dist sources
			if (minimatch(filepath, globDist)) {
				return
			}

			config._lastChanged = filepath

			var handlers = []

			Object.keys(mappingRegEx).forEach(function (key) {

				mappingRegEx[key].forEach(function(regexp) {
					if(filepath.match(regexp)) {
						handlers.push(key)
					}
				})
			})

			if (!handlers.length) {
				c.warn('! unknown extension', path.extname(filepath), filepath)
				return
			}

			handlers.forEach(function(handler) {
				gulp.start(handler, completeHandler)
			})

		}

		watch(glob, changeHandler)

		done()
	}
}
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });
};
<commit_msg>Added grunt plugins<commit_after>module.exports = function (grunt) {
  'use strict';

  require('jit-grunt')(grunt);
  require('time-grunt')(grunt);

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json')
  });
};
<|endoftext|>


<commit_before>var babelify = require("babelify");
var browserify = require("browserify");
var concat = require("gulp-concat");
var globalReact = require("global-react");
var gulp = require("gulp");
var source = require("vinyl-source-stream");
var template = require("gulp-template");
var watchify = require("watchify");

function time() {
    return "[" + new Date().toLocaleTimeString() + "] ";
}

var dist = ".";

var bundlers = [
    // Add more entries here
    "./js/app.jsx",
    "./js/vendor.js"
].map(function(entry) {
    return browserify({
        debug: true,
        entries: [entry],
        extensions: [".js", ".jsx"],
        transform: [babelify]
    }).exclude("react").transform({global: true}, globalReact);
    // Same as `browserify app.jsx -t babelify -g global-react`
});

function build(bundler) {
    (bundler ? [bundler] : bundlers).map(function(bundler) {
        var entry = bundler._options.entries[0];
        var target = entry.replace(/^.*\/([^\/]+js)x?$/, "$1");
        var start = Date.now();
        console.log(time() + "Bundling " + entry + " ...");
        bundler.bundle()
            .on("error", console.error)
            .on("end", function() {
                var duration = Date.now() - start + " ms";
                console.log(time() + "Finished " + target + " in " + duration);
            })
            .pipe(source(target))
            .pipe(gulp.dest("dist"));
    });

    gulp.src("node_modules/global-react/global.react.js")
        .pipe(gulp.dest("dist"));

    gulp.src(["css/app.css"])       // Add more CSS files here
        .pipe(concat("all.css"))
        .pipe(gulp.dest("dist"));

    gulp.src("html/index.html")
        .pipe(template({ts: Date.now(), dist: dist}))
        .pipe(gulp.dest("dist"));
}

gulp.task("watch", function() {
    bundlers = bundlers.map(function(bundler) {
        return watchify(bundler, {poll: true});
    });
    bundlers.forEach(function(bundler) {
        bundler.on("update", function() { build(bundler) });
    });

    build();
});

gulp.task("build", function() { build() });
gulp.task("publish", function() { dist = "CDN_URL"; build() });
gulp.task("default", ["watch"]);
<commit_msg>Fix gulpfile comment<commit_after>var babelify = require("babelify");
var browserify = require("browserify");
var concat = require("gulp-concat");
var globalReact = require("global-react");
var gulp = require("gulp");
var source = require("vinyl-source-stream");
var template = require("gulp-template");
var watchify = require("watchify");

function time() {
    return "[" + new Date().toLocaleTimeString() + "] ";
}

var dist = ".";

var bundlers = [
    // Add more entries here
    "./js/app.jsx",
    "./js/vendor.js"
].map(function(entry) {
    return browserify({
        debug: true,
        entries: [entry],
        transform: [babelify]
    }).exclude("react").transform({global: true}, globalReact);
    // Same as `browserify app.jsx -t babelify -u react -g global-react`
});

function build(bundler) {
    (bundler ? [bundler] : bundlers).map(function(bundler) {
        var entry = bundler._options.entries[0];
        var target = entry.replace(/^.*\/([^\/]+js)x?$/, "$1");
        var start = Date.now();
        console.log(time() + "Bundling " + entry + " ...");
        bundler.bundle()
            .on("error", console.error)
            .on("end", function() {
                var duration = Date.now() - start + " ms";
                console.log(time() + "Finished " + target + " in " + duration);
            })
            .pipe(source(target))
            .pipe(gulp.dest("dist"));
    });

    gulp.src("node_modules/global-react/global.react.js")
        .pipe(gulp.dest("dist"));

    gulp.src(["css/app.css"])       // Add more CSS files here
        .pipe(concat("all.css"))
        .pipe(gulp.dest("dist"));

    gulp.src("html/index.html")
        .pipe(template({ts: Date.now(), dist: dist}))
        .pipe(gulp.dest("dist"));
}

gulp.task("watch", function() {
    bundlers = bundlers.map(function(bundler) {
        return watchify(bundler, {poll: true});
    });
    bundlers.forEach(function(bundler) {
        bundler.on("update", function() { build(bundler) });
    });

    build();
});

gulp.task("build", function() { build() });
gulp.task("publish", function() { dist = "CDN_URL"; build() });
gulp.task("default", ["watch"]);
<|endoftext|>


<commit_before>var SpatioTemporalUI = function(props)  {

  var model = new Model({ token: props.token }),

      resultListContainer = jQuery(props.resultList),
      resultList = new ResultList(resultListContainer, model),

      timeHistogramContainer = jQuery(props.timeHistogram),
      timeHistogram = new TimeHistogram(timeHistogramContainer, model),

      mapContainer = jQuery(props.map),
      map = new Map(mapContainer, model, props.imagePath),

      filtersContainer = (props.filters) ? jQuery(props.filters) : false,
      filters = (filtersContainer) ? new FiltersPanel(filtersContainer, model) : false,

      dateFlipperContainer = (function() {
        var el = jQuery('

');
        resultListContainer.parent().append(el);
        return el;
      })(),
      dateFlipper = new DateFlipper(dateFlipperContainer),

      dateFlipperDisplayTimer,

      update = function(json_or_query) {
        model.load(json_or_query, function() {
          map.render();
          filters.render();
          resultList.render();
          timeHistogram.render();
        });
      },

      onSelectOnList = function(result) {
        console.log(result);
        window.open(result.path, '_blank');
      },

      onSelectOnMap = function(results) {
        resultList.highlight(results);
        if (results.length > 0)
          resultList.scrollTo(results[0]);
      },

      onChangeFilters = function(e) {
        model.setFilter(e);
        map.render();
        resultList.render();
        timeHistogram.render();
      },

      onSelectTimeRange = function(e) {
        resultList.highlight(); // De-highlight
        resultList.scrollTo(model.getFirstResultBefore(e.to));
      },

      onScroll = function(e) {
        var winHeight = $(window).height(), latestVisible, date;

        if (dateFlipperDisplayTimer) {
          clearTimeout(dateFlipperDisplayTimer);
          dateFlipperDisplayTimer = false;
        }

        dateFlipperContainer.fadeIn();
        dateFlipperDisplayTimer = setTimeout(function() {
          dateFlipperContainer.fadeOut();
        }, 1200);

        jQuery('.resultListSection').toArray().some(function(element) {
          var elementTop = element.getBoundingClientRect().top;

          if (elementTop < winHeight) {
            latestVisible = element;
            return false;
          } else {
            return true;
          }
        });

        date = jQuery(latestVisible).data('date');
        if (date)
          dateFlipper.set(new Date(date));
      };

  dateFlipperContainer.hide();
  jQuery(document).scroll(onScroll);

  map.on('select', onSelectOnMap);
  filters.on('change', onChangeFilters);
  timeHistogram.on('select', onSelectTimeRange);
  resultList.on('select', onSelectOnList);

  this.update = update;

};
<commit_msg>Bugfix<commit_after>var SpatioTemporalUI = function(props)  {

  var model = new Model({ token: props.token }),

      resultListContainer = jQuery(props.resultList),
      resultList = new ResultList(resultListContainer, model),

      timeHistogramContainer = jQuery(props.timeHistogram),
      timeHistogram = new TimeHistogram(timeHistogramContainer, model),

      mapContainer = jQuery(props.map),
      map = new Map(mapContainer, model, props.imagePath),

      filtersContainer = (props.filters) ? jQuery(props.filters) : false,
      filters = (filtersContainer) ? new FiltersPanel(filtersContainer, model) : false,

      dateFlipperContainer = (function() {
        var el = jQuery('

');
        resultListContainer.parent().append(el);
        return el;
      })(),
      dateFlipper = new DateFlipper(dateFlipperContainer),

      dateFlipperDisplayTimer,

      update = function(json_or_query) {
        model.load(json_or_query, function() {
          map.render();
          resultList.render();
          timeHistogram.render();

          if (filters)
            filters.render();
        });
      },

      onSelectOnList = function(result) {
        console.log(result);
        window.open(result.path, '_blank');
      },

      onSelectOnMap = function(results) {
        resultList.highlight(results);
        if (results.length > 0)
          resultList.scrollTo(results[0]);
      },

      onChangeFilters = function(e) {
        model.setFilter(e);
        map.render();
        resultList.render();
        timeHistogram.render();
      },

      onSelectTimeRange = function(e) {
        resultList.highlight(); // De-highlight
        resultList.scrollTo(model.getFirstResultBefore(e.to));
      },

      onScroll = function(e) {
        var winHeight = $(window).height(), latestVisible, date;

        if (dateFlipperDisplayTimer) {
          clearTimeout(dateFlipperDisplayTimer);
          dateFlipperDisplayTimer = false;
        }

        dateFlipperContainer.fadeIn();
        dateFlipperDisplayTimer = setTimeout(function() {
          dateFlipperContainer.fadeOut();
        }, 1200);

        jQuery('.resultListSection').toArray().some(function(element) {
          var elementTop = element.getBoundingClientRect().top;

          if (elementTop < winHeight) {
            latestVisible = element;
            return false;
          } else {
            return true;
          }
        });

        date = jQuery(latestVisible).data('date');
        if (date)
          dateFlipper.set(new Date(date));
      };

  dateFlipperContainer.hide();
  jQuery(document).scroll(onScroll);

  map.on('select', onSelectOnMap);

  if (filters)
    filters.on('change', onChangeFilters);

  timeHistogram.on('select', onSelectTimeRange);
  resultList.on('select', onSelectOnList);

  this.update = update;

};
<|endoftext|>


<commit_before>require.config({
	baseUrl : "./",
	waitSeconds: 15,
	paths : {
		"knockout" : "scripts/lib/knockout-3.1.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min"
	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".documentList circle", "mouseover", function(){
		$(this).attr("r", "7");
	}).delegate(".documentList circle", "mouseout", function(){
		$(this).attr("r", "5");
	}).delegate(".ui-menu-item circle", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
		$(self).attr("r", "7");
	}).delegate(".ui-menu-item circle", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
		$(this).attr("r", "5");
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".documentList circle, #groupG rect", "click", moveToTopic);

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
	}, 2500);

});

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<commit_msg>#6 just trigger a resize event ...<commit_after>require.config({
	baseUrl : "./",
	waitSeconds: 15,
	paths : {
		"knockout" : "scripts/lib/knockout-3.1.0",
		"knockout-amd-helpers" : "scripts/lib/knockout-amd-helpers",
		"knockout-postbox" : "scripts/lib/knockout-postbox",
		"text" : "scripts/lib/text",
		"jquery" : "scripts/lib/jquery-1.9.1.min",
		"jquery-ui" : "scripts/lib/jquery-ui-1.10.3.custom.min"
	}
});

require([ "knockout","jquery", "scripts/modules/topicexplorer-view-model",
		"knockout-amd-helpers", "knockout-postbox",
		"text", "jquery-ui" ], function(ko, $, App) {
	var timeoutId = null;
	var self=this;
	ko.bindingHandlers.module.baseDir = "scripts/modules";

	// global delegates
	$(document).delegate(".menuActivator", 'click', function(e){
		if($(this).next().height() + 26 > $(this).parent().height()) {
	    	$(this).next().css('max-height', $(this).parent().height() - 26);
	    }

		$(this).toggleClass("rotate1 rotate2");
	    $(this).next().toggle('blind');

	}).delegate("#invisbleTabs > span", 'click', function() {
		$('#tabMenu').toggle('blind');
	}).delegate("a.ui-corner-all", 'mouseover', function() {
		$(this).addClass('ui-state-focus');
	}).delegate("a.ui-corner-all", 'mouseout', function() {
		$(this).removeClass('ui-state-focus');
	}).delegate(".tab", 'mouseover', function() {
		$(this).children('img').show();
	}).delegate(".tab", 'mouseout', function() {
		$(this).children('img').hide();
	}).delegate(".documentList li", 'mouseenter', function(){
		$(this).addClass('myHover').find(".docButtons").show();
	}).delegate(".documentList li", 'mouseleave', function(){
		$(this).removeClass('myHover').find(".docButtons").hide();
	}).delegate(".documentList circle", "mouseover", function(){
		$(this).attr("r", "7");
	}).delegate(".documentList circle", "mouseout", function(){
		$(this).attr("r", "5");
	}).delegate(".ui-menu-item circle", "mouseover", function(){
		var self = this;
		if (!timeoutId) {
	        timeoutId = window.setTimeout(function() {
	            timeoutId = null;
	            moveToTopic($(self).attr('id').split('_')[1]);
		    }, 1500);
		}
		$(self).attr("r", "7");
	}).delegate(".ui-menu-item circle", "mouseout", function(){
		if (timeoutId) {
		    window.clearTimeout(timeoutId);
		    timeoutId = null;
		}
		$(this).attr("r", "5");
	}).delegate("#groupG rect", "mouseover", function(){
		$(this).attr("height", "17");
		$(this).attr("y", "0");
	}).delegate("#groupG rect", "mouseout", function(){
		$(this).attr("height", "13");
		$(this).attr("y", "2");
	}).delegate(".topicList", "mouseenter", function(){
		$(this).children(":first").show();
	}).delegate("#desktop", "mouseenter", function(){
		$(this).children(":first").show();
		if($("#desktop").scrollTop() > 1000) {
			$("#jumpToStart").show();
		}
	}).delegate("#desktop, .topicList", "mouseleave", function(){
		$(this).children(":first").hide();
		$(this).children(":first").next().hide();
		$(this).children(":first").removeClass("rotate2");
		$(this).children(":first").addClass("rotate1");
		$("#jumpToStart").hide();
	}).delegate("#jumpToStart", "click", function() {
		$("#desktop").animate({
			scrollTop: 0
		});
	}).delegate(".documentList circle, #groupG rect", "click", moveToTopic);

	self.minHeight = 400;
	self.minWidth = 800;

	$(window).resize(function() {
		ko.postbox.publish("windowWidth",Math.max(self.minWidth, $(window).width(), /* For opera: */ document.documentElement.clientWidth));
		ko.postbox.publish("windowHeight",Math.max(self.minHeight, $(window).height(), /* For opera: */ document.documentElement.clientHeight));
	});

	setTimeout(function() {
		ko.applyBindings(new App());
	}, 0);

	setTimeout(function() {
		$(window).trigger('resize');
	}, 3000);

});

function makeMenu(el) {
	$(el).menu({
		select : function(event, ui) {
			$(this).parent().parent().prev()
				.toggleClass("rotate1 rotate2");
			$(this).parent().parent().toggle('blind');
		}
	});
};

function getScrollPositionByValue(val) {
	var offset = 3;
	var scroll = val;
	var maxScrollPos = $('.topicBottomSliderDiv').width() - $('.topicPrevSlider').width();
	var maxScroll = $('.topicList > ul').width() - $('.topicList').width();
	var position = Math.round(scroll*maxScrollPos/maxScroll)+offset;
	if(position < 0)
		position = 0;
	if(position > maxScrollPos)
		position = maxScrollPos;
	return position;
};

function moveToTopic(self) {

	var topic_id = 0;
	if(!self.currentTarget) {
		topic_id = self;
	} else {
		topic_id = $(self.currentTarget).attr('id').split("_")[1];
	}
	var offset = $(".topicList").width() / 2 - $("#topic" + topic_id).width() / 2;

	$(".topicList").animate({
		scrollLeft : ($("#topic" + topic_id).position().left - offset)
	}, {
		duration : 2000,
		easing : "swing"
	});
	$(".topicPrevSlider").animate({
		left : getScrollPositionByValue(($("#topic" + topic_id).position().left - offset))
	}, {
		duration : 2000,
		easing : "swing"
	});
	setTimeout(function() {
		$(".topicList > img, #topicMenu").css('left', $(".topicList").scrollLeft());
	}, 2000);
};

<|endoftext|>


<commit_before>import express from 'express';
import http from 'http';
import colors from 'colors';
import config from './config';
const app = express();
// Core
require('./core/engine').default(app);
// Routers
require('./core/router').default(app);
// Swagger
require('./core/swagger').default(app);
// Paths
require('./core/path').default(app);
// MongoDB
require('./core/mongoose/db');
// Create HTTP server.
const server = http.createServer(app);
// Listen Server
server.listen(config.server.port, config.server.ip, () => {
  process.env.NODE_ENV = config.mode;
  console.log(`Server listening on http://${config.server.ip}:${config.server.port} in mode [${config.mode}]`.bgGreen);
});
<commit_msg>updated path mongoose<commit_after>import express from 'express';
import http from 'http';
import colors from 'colors';
import config from './config';
const app = express();
// Core
require('./core/engine').default(app);
// Routers
require('./core/router').default(app);
// Swagger
require('./core/swagger').default(app);
// Paths
require('./core/path').default(app);
// MongoDB
require('./core/mongoose');
// Create HTTP server.
const server = http.createServer(app);
// Listen Server
server.listen(config.server.port, config.server.ip, () => {
  process.env.NODE_ENV = config.mode;
  console.log(`Server listening on http://${config.server.ip}:${config.server.port} in mode [${config.mode}]`.bgGreen);
});
<|endoftext|>


<commit_before>import {parseJSON} from './resources/jsonparser';
import {schema} from './schemas/index';
import YAML from 'yamljs';
import $ from 'jquery';

export class App {
  constructor() {
    // Allow access from browser console
    window.$oai = this;

    this.forms = parseJSON('swagger', schema);
    if (window.localStorage.cachedForm) {
      this.forms.setValue(JSON.parse(window.localStorage.cachedForm));
    }

    this.activeForm = this.forms.getChild('header');
    this.activeForm.show = true;

    window.onhashchange = () => {
      const formID = window.location.hash.substr(2) || 'header';
      if (this.forms.hasChild(formID)) {
        this.activeForm.show = false;
        this.activeForm = this.forms.getChild(formID);
        this.activeForm.show = true;
        $('.nav > .nav-link.open').removeClass('open');
        $(`#nav-${formID}`).addClass('open');
      }
    };
  }

  attached() {
    window.onhashchange();
  }

  download(type) {
    let data;
    if (type === 'json') {
      data = this.json;
    } else if (type === 'yaml') {
      data = this.yaml;
    } else {
      return;
    }

    // Add an anchor element that has the data as the href attribute, then click
    // the element to download the data.
    const blob = new Blob([data], {type: 'text/json'});
    if (window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveBlob(blob, `swagger.${type}`);
    } else {
      const downloadLink = document.createElement('a');
      downloadLink.href = window.URL.createObjectURL(blob);
      downloadLink.download = `swagger.${type}`;
      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink);
    }
  }

  get yaml() {
    return YAML.stringify(this.forms.getValue(), 10, 2);
  }

  get json() {
    const data = JSON.stringify(this.forms.getValue(), '', '  ');
    window.localStorage.cachedForm = data;
    return data;
  }

  get currentFormJSON() {
    const data = JSON.stringify(this.activeForm.getValue(), '', '  ');
    return data;
  }
}
<commit_msg>Add form name to JSON preview<commit_after>import {parseJSON} from './resources/jsonparser';
import {schema} from './schemas/index';
import YAML from 'yamljs';
import $ from 'jquery';

export class App {
  constructor() {
    // Allow access from browser console
    window.$oai = this;

    this.forms = parseJSON('swagger', schema);
    if (window.localStorage.cachedForm) {
      this.forms.setValue(JSON.parse(window.localStorage.cachedForm));
    }

    this.activeForm = this.forms.getChild('header');
    this.activeForm.show = true;

    window.onhashchange = () => {
      const formID = window.location.hash.substr(2) || 'header';
      if (this.forms.hasChild(formID)) {
        this.activeForm.show = false;
        this.activeForm = this.forms.getChild(formID);
        this.activeForm.show = true;
        $('.nav > .nav-link.open').removeClass('open');
        $(`#nav-${formID}`).addClass('open');
      }
    };
  }

  attached() {
    window.onhashchange();
  }

  download(type) {
    let data;
    if (type === 'json') {
      data = this.json;
    } else if (type === 'yaml') {
      data = this.yaml;
    } else {
      return;
    }

    // Add an anchor element that has the data as the href attribute, then click
    // the element to download the data.
    const blob = new Blob([data], {type: 'text/json'});
    if (window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveBlob(blob, `swagger.${type}`);
    } else {
      const downloadLink = document.createElement('a');
      downloadLink.href = window.URL.createObjectURL(blob);
      downloadLink.download = `swagger.${type}`;
      document.body.appendChild(downloadLink);
      downloadLink.click();
      document.body.removeChild(downloadLink);
    }
  }

  get yaml() {
    return YAML.stringify(this.forms.getValue(), 10, 2);
  }

  get json() {
    const data = JSON.stringify(this.forms.getValue(), '', '  ');
    window.localStorage.cachedForm = data;
    return data;
  }

  get currentFormJSON() {
    const data = JSON.stringify(this.activeForm.getValue(), '', '  ');
    return `"${this.activeForm.id}": ${data}`;
  }
}
<|endoftext|>


<commit_before>/* @flow */

/*
 Copyright 2016 Mozilla

 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.
 */

import express from 'express';
import expressWs from 'express-ws';
import expressValidator from 'express-validator';
import bodyParser from 'body-parser';
import morgan from 'morgan';

/**
 * Catch errors from the given async `method` and forward them to `next` for handling.
 * This allows writing "loose" handling code and still opt-in to express's error
 * handling backstop. Optionally, a validator function can be given to make use
 * of the `express-validator` middleware and validate requests before routing.
 */
export function autoCaughtRouteError({ validator, method }) {
  return async function(req, res, next) {
    try {
      if (validator) {
        validator(req);
      }

      const errors = req.validationErrors();
      if (errors) {
        console.warn(errors);
        res.status(401).json(errors);
        return;
      }

      await method(req, res, next);
    } catch (e) {
      next(e);
    }
  };
}

/**
 * Exposes functionality that starts up an express static server on the given
 * address and port, routing requests through the `version` path.
 * Returns the `setup` and `stop` functions to start and stop the server.
 * A configuration function can be passed to `setup`, called with the
 * `app` and `routes` objects, to configure additional server behavior.
 */
export function makeServer(version, port) {
  let server;
  let wsserver;
  let app;
  let router;

  function setup(config) {
    return new Promise(async function(resolve, reject) {
      if (server) {
        reject(new Error('The server was already started.'));
      }

      app = express();
      router = express.Router();
      const { getWss } = expressWs(app);

      app.use(morgan('combined'));

      // These need to be registered before our routes.
      app.use(bodyParser.json());
      app.use(expressValidator());

      await config(app, router);

      // Version namespace!
      app.use(`/${version}`, router);

      // Must follow route definitions! Need to have four arguments here
      // so express knows that we want to handle errors.
      app.use((error, req, res, _next) => {
        console.error(error.stack);
        res.status(500).json({ error, stack: error.stack });
      });

      // Sadly, app.listen does not return the HTTP server just yet.
      // Therefore, we extract it manually below.
      app.listen(port, resolve);
      wsserver = getWss();
      server = wsserver._server;

      server.on('error', reject);
      wsserver.on('error', reject);
    });
  }

  function stop() {
    return new Promise((resolve, reject) => {
      if (!server) {
        reject(new Error('The server was not started yet.'));
      }
      server.close(err => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  }

  return { setup, stop };
}
<commit_msg>Use a `dev` output for morgan instead of the `combined` Apache-style output<commit_after>/* @flow */

/*
 Copyright 2016 Mozilla

 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.
 */

import express from 'express';
import expressWs from 'express-ws';
import expressValidator from 'express-validator';
import bodyParser from 'body-parser';
import morgan from 'morgan';

/**
 * Catch errors from the given async `method` and forward them to `next` for handling.
 * This allows writing "loose" handling code and still opt-in to express's error
 * handling backstop. Optionally, a validator function can be given to make use
 * of the `express-validator` middleware and validate requests before routing.
 */
export function autoCaughtRouteError({ validator, method }) {
  return async function(req, res, next) {
    try {
      if (validator) {
        validator(req);
      }

      const errors = req.validationErrors();
      if (errors) {
        console.warn(errors);
        res.status(401).json(errors);
        return;
      }

      await method(req, res, next);
    } catch (e) {
      next(e);
    }
  };
}

/**
 * Exposes functionality that starts up an express static server on the given
 * address and port, routing requests through the `version` path.
 * Returns the `setup` and `stop` functions to start and stop the server.
 * A configuration function can be passed to `setup`, called with the
 * `app` and `routes` objects, to configure additional server behavior.
 */
export function makeServer(version, port) {
  let server;
  let wsserver;
  let app;
  let router;

  function setup(config) {
    return new Promise(async function(resolve, reject) {
      if (server) {
        reject(new Error('The server was already started.'));
      }

      app = express();
      router = express.Router();
      const { getWss } = expressWs(app);

      app.use(morgan('dev'));

      // These need to be registered before our routes.
      app.use(bodyParser.json());
      app.use(expressValidator());

      await config(app, router);

      // Version namespace!
      app.use(`/${version}`, router);

      // Must follow route definitions! Need to have four arguments here
      // so express knows that we want to handle errors.
      app.use((error, req, res, _next) => {
        console.error(error.stack);
        res.status(500).json({ error, stack: error.stack });
      });

      // Sadly, app.listen does not return the HTTP server just yet.
      // Therefore, we extract it manually below.
      app.listen(port, resolve);
      wsserver = getWss();
      server = wsserver._server;

      server.on('error', reject);
      wsserver.on('error', reject);
    });
  }

  function stop() {
    return new Promise((resolve, reject) => {
      if (!server) {
        reject(new Error('The server was not started yet.'));
      }
      server.close(err => {
        if (err) {
          reject(err);
        } else {
          resolve();
        }
      });
    });
  }

  return { setup, stop };
}
<|endoftext|>


<commit_before>const commonjs = require('rollup-plugin-commonjs');
const resolve = require('rollup-plugin-node-resolve');
const uglify = require('rollup-plugin-uglify-es');

module.exports = function (grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    sass: {
      options: {
        style: 'compressed',
        sourcemap: 'file'
      },
      all: {
        files: [{
          expand: true,
          cwd: 'src/styles',
          src: '*.scss',
          dest: 'dist/styles',
          ext: '.css'
        }]
      }
    },

    rollup: {
      options: {
        format: 'iife',
        moduleName: 'yvs',
        plugins: [
          resolve(),
          commonjs(),
          uglify()
        ]
      },
      all: {
        files: {
          'dist/scripts/popup.js': ['src/scripts/popup.js']
        }
      }
    },

    watch: {
      pages: {
        files: ['src/*.html'],
        tasks: ['copy']
      },
      scripts: {
        files: ['src/scripts/*.js'],
        tasks: ['rollup']
      },
      styles: {
        files: ['src/styles/*.scss'],
        tasks: ['sass']
      }
    },

    copy: {
      all: {
        files: [{
            expand: true,
            cwd: 'src',
            src: ['*.html'],
            dest: 'dist'
        }]
      }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-postcss');
  grunt.loadNpmTasks('grunt-rollup');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-copy');

  grunt.registerTask('build', [
    'copy',
    'sass',
    'rollup'
  ]);

  grunt.registerTask('serve', [
    'build',
    'watch'
  ]);

};
<commit_msg>Copy manifest and icon (not just HTML) to dist/<commit_after>const commonjs = require('rollup-plugin-commonjs');
const resolve = require('rollup-plugin-node-resolve');
const uglify = require('rollup-plugin-uglify-es');

module.exports = function (grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON('package.json'),

    sass: {
      options: {
        style: 'compressed',
        sourcemap: 'file'
      },
      all: {
        files: [{
          expand: true,
          cwd: 'src/styles',
          src: '*.scss',
          dest: 'dist/styles',
          ext: '.css'
        }]
      }
    },

    rollup: {
      options: {
        format: 'iife',
        moduleName: 'yvs',
        plugins: [
          resolve(),
          commonjs(),
          uglify()
        ]
      },
      all: {
        files: {
          'dist/scripts/popup.js': ['src/scripts/popup.js']
        }
      }
    },

    watch: {
      pages: {
        files: ['src/*.html'],
        tasks: ['copy']
      },
      scripts: {
        files: ['src/scripts/*.js'],
        tasks: ['rollup']
      },
      styles: {
        files: ['src/styles/*.scss'],
        tasks: ['sass']
      }
    },

    copy: {
      all: {
        files: [{
            expand: true,
            cwd: 'src',
            src: [
              '*.html',
              'manifest.json',
              'icon.png'
            ],
            dest: 'dist'
        }]
      }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-postcss');
  grunt.loadNpmTasks('grunt-rollup');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-copy');

  grunt.registerTask('build', [
    'copy',
    'sass',
    'rollup'
  ]);

  grunt.registerTask('serve', [
    'build',
    'watch'
  ]);

};
<|endoftext|>


<commit_before>/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

module.exports = function(grunt) {
    // configure tasks
    grunt.initConfig({
        watch: {
            js: {
                files: ['src/**/*.js', 'test/specs/**/*.js'],
                tasks: ['jshint', 'jsdoc', 'test']
            }
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc',
            },
            all: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js', '!src/specification-data.js']
        },
        jsdoc: {
            html: {
                src: [
                    'README.md',
                    'src/**/*.js',
                    '!src/specification-data.js'
                ],
                options: {
                    destination: 'docs',
                    configure: 'jsdoc.json'
                }
            }
        },
        'mocha-chai-sinon': {
            build: {
                src: ['./test/specs/**/*.spec.js'],
                options: {
                    ui: 'bdd',
                    reporter: 'spec'
                }
            },
            xcoverage: {
                src: ['./test/specs/**/*.spec.js'],
                options: {
                    ui: 'bdd',
                    reporter: 'html-cov',
                    quiet: true,
                    captureFile: './coverage.html',
                }
            }
        },
        copy: {
            logger: {
                files: [ {
                    expand: true,
                    cwd: '.',
                    src: [
                        'package.json',
                        'src/**/*',
                    ],
                    dest: 'nw-apps/logger/node_modules/resol-vbus'
                }, {
                    expand: true,
                    cwd: 'nw-apps/logger',
                    src: [
                        '**/*',
                    ],
                    dest: 'nw-builds/tmp/logger'
                } ]
            }
        },
        shell: {
            logger: {
                command: '(cd nw-builds/tmp/logger/; npm install --production; bower install; cd node_modules/resol-vbus; npm install --production)'
            }
        },
        nodewebkit: {
            options: {
                version: '0.8.2',
                build_dir: './nw-builds',
                mac: true,
                win: false,
                linux32: false,
                linux64: false
            },
            logger: {
                options: {
                    app_name: 'RESOL VBus Logger'
                },
                src: [
                    'nw-builds/tmp/logger/**/*',
                ]
            }
        },
        open: {
            logger: {
                path: 'nw-builds/releases/RESOL VBus Logger/mac/RESOL VBus Logger.app',
            },
        },
        compress: {
            logger: {
                options: {
                    archive: 'nw-builds/releases/RESOL VBus Logger/mac/RESOL VBus Logger.zip'
                },
                files: [ {
                    expand: true,
                    cwd: 'nw-builds/releases/RESOL VBus Logger/mac',
                    src: [ 'RESOL VBus Logger.app/**/*' ],
                } ]
            }
        }
    });

    require('load-grunt-tasks')(grunt);

    // register tasks for execution chain
    grunt.registerTask('test', [
        'mocha-chai-sinon:build'
    ]);
    grunt.registerTask('coverage', [
        'mocha-chai-sinon:coverage'
    ]);

    grunt.registerTask('deploy:logger', [
        'copy:logger',
        'shell:logger',
        'nodewebkit:logger',
        'open:logger',
        'compress:logger',
    ]);
};
<commit_msg>JSDoc output now uses DocStrap template<commit_after>/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

module.exports = function(grunt) {
    // configure tasks
    grunt.initConfig({
        watch: {
            js: {
                files: ['src/**/*.js', 'test/specs/**/*.js'],
                tasks: ['jshint', 'jsdoc', 'test']
            }
        },
        jshint: {
            options: {
                jshintrc: '.jshintrc',
            },
            all: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js', '!src/specification-data.js']
        },
        jsdoc: {
            html: {
                src: [
                    'README.md',
                    'src/**/*.js',
                    '!src/specification-data.js'
                ],
                options: {
                    destination: 'docs',
                    template: 'node_modules/grunt-jsdoc/node_modules/ink-docstrap/template',
                    configure: 'jsdoc.json'
                }
            }
        },
        'mocha-chai-sinon': {
            build: {
                src: ['./test/specs/**/*.spec.js'],
                options: {
                    ui: 'bdd',
                    reporter: 'spec'
                }
            },
            xcoverage: {
                src: ['./test/specs/**/*.spec.js'],
                options: {
                    ui: 'bdd',
                    reporter: 'html-cov',
                    quiet: true,
                    captureFile: './coverage.html',
                }
            }
        },
        copy: {
            logger: {
                files: [ {
                    expand: true,
                    cwd: '.',
                    src: [
                        'package.json',
                        'src/**/*',
                    ],
                    dest: 'nw-apps/logger/node_modules/resol-vbus'
                }, {
                    expand: true,
                    cwd: 'nw-apps/logger',
                    src: [
                        '**/*',
                    ],
                    dest: 'nw-builds/tmp/logger'
                } ]
            }
        },
        shell: {
            logger: {
                command: '(cd nw-builds/tmp/logger/; npm install --production; bower install; cd node_modules/resol-vbus; npm install --production)'
            }
        },
        nodewebkit: {
            options: {
                version: '0.8.2',
                build_dir: './nw-builds',
                mac: true,
                win: false,
                linux32: false,
                linux64: false
            },
            logger: {
                options: {
                    app_name: 'RESOL VBus Logger'
                },
                src: [
                    'nw-builds/tmp/logger/**/*',
                ]
            }
        },
        open: {
            logger: {
                path: 'nw-builds/releases/RESOL VBus Logger/mac/RESOL VBus Logger.app',
            },
        },
        compress: {
            logger: {
                options: {
                    archive: 'nw-builds/releases/RESOL VBus Logger/mac/RESOL VBus Logger.zip'
                },
                files: [ {
                    expand: true,
                    cwd: 'nw-builds/releases/RESOL VBus Logger/mac',
                    src: [ 'RESOL VBus Logger.app/**/*' ],
                } ]
            }
        }
    });

    require('load-grunt-tasks')(grunt);

    // register tasks for execution chain
    grunt.registerTask('test', [
        'mocha-chai-sinon:build'
    ]);
    grunt.registerTask('coverage', [
        'mocha-chai-sinon:coverage'
    ]);

    grunt.registerTask('deploy:logger', [
        'copy:logger',
        'shell:logger',
        'nodewebkit:logger',
        'open:logger',
        'compress:logger',
    ]);
};
<|endoftext|>