<commit_before>(function(){

  // We check for features which are not universally supported, and don't try to
  // show the app if it would error.
  if (!window.addEventListener || !window.localStorage)
    return;

  // The INSTALL_OPTIONS constant is inserted by the Eager bundler.  It's value is the
  // value of the options defined in the install.json file.
  var options = INSTALL_OPTIONS;

  // For converting the size of an element to a ratio to multiply the font size by.
  // It's probably best to restrict this to a simple small/medium/large so as not
  // to overload the user with options. A user constantly clicking the increase/decrease
  // font size button or dragging the slider to their exact value is a user that
  // wastes time that they should have been using on the website's actual content.
  // However, it's not that hard to modify to allow website owners to add additional sizes
  const sizeToRatio = {
    'small': {
      shownSize: 0.7,
      actualSize: 0.7
    },
    'medium': {
      shownSize: 1,
      actualSize: 1
    },
    'large': {
      shownSize: 1.5,
      actualSize: 1.5
    }
  }

  // We get our stored view size from the last time a visitor viewed the site.
  // If the visitor hasn't viewed the site before, we default to 'medium',
  // which does not change the font size for any elements
  var textContainingElements = [];
  var textResizer = null;
  var updateElement = function(){
    localStorage.removeItem('viewSize');

    // To disable font boosting in order to get proper computed element sizes
    // thanks to http://stackoverflow.com/a/15261825
    var node = document.createElement('style');
    node.innerHTML = 'body, body * { max-height: 1000000px; }';
    document.body.appendChild(node);

    // We pass in the last element to allow us to restore the removed element
    // when we do live updating of the preview.  Details:
    // https://eager.io/developer/docs/install-json/preview#dealing-with-element-fields

    // We need to force options.element to body if options.position is 'fixed', because
    // not showing a selection via showIf doesn't make it change to its default value
    // Probably a bug, since it seems unintuitive
    if (options.position === 'fixed') {
      options.element = {
        "selector": "body",
        "method": "append"
      }
    }

    // The text resizer itself
    textResizer = Eager.createElement(options.element, textResizer);
    textResizer.className = 'example-font-size-resizer';

    // Converting corner position, and vertical/horizontal margins to CSS
    var verticalDirection, horizontalDirection;
    switch (options.corner) {
      case 'topLeft':
        verticalDirection = 'top';
        horizontalDirection = 'left';
        break;
      case 'topRight':
        verticalDirection = 'top';
        horizontalDirection = 'right';
        break;
      case 'bottomLeft':
        verticalDirection = 'bottom';
        horizontalDirection = 'left';
        break;
      case 'bottomRight':
        verticalDirection = 'bottom';
        horizontalDirection = 'right';
      default:
        break;
    };

    textResizer.style.cssText += (`
      ${verticalDirection}: ${options.verticalMargin}px;
      ${horizontalDirection}: ${options.horizontalMargin}px;
    `);

    // We're adding all of our CSS dynamically in js without
    // .css files because the CSS itself is generated depending on the options that the
    // website owner configures depending on his/her preferences
    var hoverCSS = document.createElement('style');
    hoverCSS.innerHTML = (`
      .${textResizer.className} {
        display: inline-block;
        padding: 5px;
        z-index: 2147483647;
        -moz-user-select: none;
        -khtml-user-select: none;
        -webkit-user-select: none;
        -o-user-select: none;
        border-radius: ${options.borderRadius}px;
        background-color: ${options.backgroundColor};
        opacity: ${options.opacity / 100};
        position: ${options.position};
        -webkit-transition-duration: ${options.transitionTime}ms;
        -moz-transition-duration: ${options.transitionTime}ms;
        -o-transition-duration: ${options.transitionTime}ms;
        transition-duration: ${options.transitionTime}ms;
      }

      .${textResizer.className}:hover {
        opacity: ${options.onhoverOpacity / 100};
      }

      .${textResizer.className} div {
        float: left;
        display: block;
        margin: 0;
        font-weight: 200;
        font-family: lato, helvetica, arial;
        background-color: transparent;
        cursor: pointer;
        padding: ${options.fontSize / 5}px;
        color: ${options.textColor};
        opacity: ${options.onhoverOpacity / 100 * 0.7};
      }
      .${textResizer.className} div:hover {
        opacity: ${options.onhoverOpacity / 100};
      }
      .${textResizer.className} div.selected {
        font-weight: 800;
        opacity: ${options.onhoverOpacity / 100 * 0.9};
      }
      .${textResizer.className} div.selected:hover {
        opacity: ${options.onhoverOpacity / 100 * 1.1};
      }
    `);
    document.body.appendChild(hoverCSS);

    // Literally creates the A's in the font size selector
    function createA(name) {
      createdA = document.createElement('div');
      createdA.innerText = 'A';
      // CSS that depends on the size of the A
      createdA.style.cssText += (`
        line-height: ${options.fontSize * sizeToRatio['large'].shownSize}px;
        font-size: ${options.fontSize * sizeToRatio[name].shownSize}px;
      `);
      // For spacing
      if (name === 'medium') {
        createdA.style.cssText += (`
          margin: ${options.spacing}px;
          margin-top: 0;
          margin-bottom: 0;
        `);
      }
      // Add onClick font modification here
      // default to medium if it hasn't been used before
      createdA.onclick = () => {resizeText(localStorage.viewSize, name)};
      return createdA;
    }

    // Actually create the different sized A's
    textResizer.appendChild(createA('small'));
    textResizer.appendChild(createA('medium'));
    textResizer.appendChild(createA('large'));

    resizeText(localStorage.viewSize || 'medium', 'medium'); // initially resize text to medium (original size) or the previous selection by user

    // Create a MutationObserver instance to monitor new text containing nodes if they are
    // added on DOM modification
    var observer = new MutationObserver((mutations) => {
      var addedNodes = [];
      for (var i = 0; i < mutations.length; i++){
        if (mutations[i].type === 'childList'){
          var nodeArray = Array.prototype.slice.call(mutations[i].addedNodes); // convert the added nodes to an array from nodelist
          addedNodes = addedNodes.concat(nodeArray)
        }
      }
      for (var i = 0; i < addedNodes.length; i++) {
        addTextElements(addedNodes[i])
      }
    });
    observer.observe(document.documentElement, {subtree: true, childList: true});
  };

  // Get all text containing elements, so we don't have to waste time on non-text containing
  // elements. This makes it much faster to change text sizes. This also however means that
  // we have to repeat this function each time an element is added to the DOM tree.
  function updateTextElements(){
    textContainingElements = [];
    var elements = document.querySelectorAll('body *');

    //add all elements with inner text that aren't part of the actual text size changer to the textContainingElements array
    for (element in elements) {
      if (elements[element].innerText){//} && textResizer.className != elements[element].parentNode.className) {
        textContainingElements.push(elements[element]);
      }
    }
  }

  // add text elements to the textContainingElements array, so that on an infinitely scrolling webpage, for example, they will still work
  function addTextElements(node) {
    if (node.className === 'truncated-text') {
      console.log('VFOUND!!!')
      console.log('  FOUND!!!')
      console.log('^FOUND!!!')
    }
    if (node.innerText) {
      var sizeMultiplier = sizeToRatio[localStorage.viewSize || 'medium'].actualSize;
      var size = window.getComputedStyle(node, null).getPropertyValue('font-size');
      node.style.fontSize = parseFloat(size) * sizeMultiplier + 'px';
      textContainingElements.push(node);
    } else if (node.children) {
      for (var i=0; i < node.children.length; i++) {
        addTextElements(node.children[i]);
      }
    }
  }

  // For resizing text. Assuming this is completely modular, it's going to be a bit
  // inefficient because we'll be changing the font size for every element
  function resizeText(oldSize, newSize){
    // Modify the A that got selected and reset the one that was previously selected
    textResizer.querySelector(`div:nth-child(${Object.keys(sizeToRatio).indexOf(oldSize) + 1})`).className = '';
    textResizer.querySelector(`div:nth-child(${Object.keys(sizeToRatio).indexOf(newSize) + 1})`).className = 'selected';

    // Find current scroll position
    var scrollRatio = window.scrollY / document.documentElement.scrollHeight;

    // Modify the font size of all text-containing elements
    var sizeMultiplier = sizeToRatio[newSize].actualSize / sizeToRatio[oldSize].actualSize;
    textContainingElements.forEach((element) => {
      var size = window.getComputedStyle(element, null).getPropertyValue('font-size');
      element.style.fontSize = parseFloat(size) * sizeMultiplier + 'px';
    });

    // Return user to approximate previous scroll position
    window.scroll(window.scrollX, scrollRatio * document.documentElement.scrollHeight);

    // Change current size to the new size that we've updated it to
    localStorage.viewSize = newSize;
  }

  var update = function(){
    updateTextElements(); // create initial log of text containing elements
    updateElement();
  }

  var setOptions = function(opts){
    options = opts;
    update();
  }

  // Since we're adding an element to the body, we need to wait until the DOM is
  // ready before inserting our widget.
  if (document.readyState == 'loading')
    document.addEventListener('DOMContentLoaded', update);
  else
    update();

  // This is used by the preview to enable live updating of the app while previewing.
  // See the preview.handlers section of the install.json file to see where it's used.
  INSTALL_SCOPE = {
    setOptions: setOptions
  };

})()
<commit_msg>Revert "bugfix: default to medium font size if no previous uses"<commit_after>(function(){

  // We check for features which are not universally supported, and don't try to
  // show the app if it would error.
  if (!window.addEventListener || !window.localStorage)
    return;

  // The INSTALL_OPTIONS constant is inserted by the Eager bundler.  It's value is the
  // value of the options defined in the install.json file.
  var options = INSTALL_OPTIONS;

  // For converting the size of an element to a ratio to multiply the font size by.
  // It's probably best to restrict this to a simple small/medium/large so as not
  // to overload the user with options. A user constantly clicking the increase/decrease
  // font size button or dragging the slider to their exact value is a user that
  // wastes time that they should have been using on the website's actual content.
  // However, it's not that hard to modify to allow website owners to add additional sizes
  const sizeToRatio = {
    'small': {
      shownSize: 0.7,
      actualSize: 0.7
    },
    'medium': {
      shownSize: 1,
      actualSize: 1
    },
    'large': {
      shownSize: 1.5,
      actualSize: 1.5
    }
  }

  // We get our stored view size from the last time a visitor viewed the site.
  // If the visitor hasn't viewed the site before, we default to 'medium',
  // which does not change the font size for any elements
  var textContainingElements = [];
  var textResizer = null;
  var updateElement = function(){
    localStorage.removeItem('viewSize');

    // To disable font boosting in order to get proper computed element sizes
    // thanks to http://stackoverflow.com/a/15261825
    var node = document.createElement('style');
    node.innerHTML = 'body, body * { max-height: 1000000px; }';
    document.body.appendChild(node);

    // We pass in the last element to allow us to restore the removed element
    // when we do live updating of the preview.  Details:
    // https://eager.io/developer/docs/install-json/preview#dealing-with-element-fields

    // We need to force options.element to body if options.position is 'fixed', because
    // not showing a selection via showIf doesn't make it change to its default value
    // Probably a bug, since it seems unintuitive
    if (options.position === 'fixed') {
      options.element = {
        "selector": "body",
        "method": "append"
      }
    }

    // The text resizer itself
    textResizer = Eager.createElement(options.element, textResizer);
    textResizer.className = 'example-font-size-resizer';

    // Converting corner position, and vertical/horizontal margins to CSS
    var verticalDirection, horizontalDirection;
    switch (options.corner) {
      case 'topLeft':
        verticalDirection = 'top';
        horizontalDirection = 'left';
        break;
      case 'topRight':
        verticalDirection = 'top';
        horizontalDirection = 'right';
        break;
      case 'bottomLeft':
        verticalDirection = 'bottom';
        horizontalDirection = 'left';
        break;
      case 'bottomRight':
        verticalDirection = 'bottom';
        horizontalDirection = 'right';
      default:
        break;
    };

    textResizer.style.cssText += (`
      ${verticalDirection}: ${options.verticalMargin}px;
      ${horizontalDirection}: ${options.horizontalMargin}px;
    `);

    // We're adding all of our CSS dynamically in js without
    // .css files because the CSS itself is generated depending on the options that the
    // website owner configures depending on his/her preferences
    var hoverCSS = document.createElement('style');
    hoverCSS.innerHTML = (`
      .${textResizer.className} {
        display: inline-block;
        padding: 5px;
        z-index: 2147483647;
        -moz-user-select: none;
        -khtml-user-select: none;
        -webkit-user-select: none;
        -o-user-select: none;
        border-radius: ${options.borderRadius}px;
        background-color: ${options.backgroundColor};
        opacity: ${options.opacity / 100};
        position: ${options.position};
        -webkit-transition-duration: ${options.transitionTime}ms;
        -moz-transition-duration: ${options.transitionTime}ms;
        -o-transition-duration: ${options.transitionTime}ms;
        transition-duration: ${options.transitionTime}ms;
      }

      .${textResizer.className}:hover {
        opacity: ${options.onhoverOpacity / 100};
      }

      .${textResizer.className} div {
        float: left;
        display: block;
        margin: 0;
        font-weight: 200;
        font-family: lato, helvetica, arial;
        background-color: transparent;
        cursor: pointer;
        padding: ${options.fontSize / 5}px;
        color: ${options.textColor};
        opacity: ${options.onhoverOpacity / 100 * 0.7};
      }
      .${textResizer.className} div:hover {
        opacity: ${options.onhoverOpacity / 100};
      }
      .${textResizer.className} div.selected {
        font-weight: 800;
        opacity: ${options.onhoverOpacity / 100 * 0.9};
      }
      .${textResizer.className} div.selected:hover {
        opacity: ${options.onhoverOpacity / 100 * 1.1};
      }
    `);
    document.body.appendChild(hoverCSS);

    // Literally creates the A's in the font size selector
    function createA(name) {
      createdA = document.createElement('div');
      createdA.innerText = 'A';
      // CSS that depends on the size of the A
      createdA.style.cssText += (`
        line-height: ${options.fontSize * sizeToRatio['large'].shownSize}px;
        font-size: ${options.fontSize * sizeToRatio[name].shownSize}px;
      `);
      // For spacing
      if (name === 'medium') {
        createdA.style.cssText += (`
          margin: ${options.spacing}px;
          margin-top: 0;
          margin-bottom: 0;
        `);
      }
      // Add onClick font modification here
<<<<<<< HEAD
      // default to medium if it hasn't been used before
=======
>>>>>>> parent of 7411e2d... bugfix: default to medium font size if no previous uses
      createdA.onclick = () => {resizeText(localStorage.viewSize, name)};
      return createdA;
    }

    // Actually create the different sized A's
    textResizer.appendChild(createA('small'));
    textResizer.appendChild(createA('medium'));
    textResizer.appendChild(createA('large'));

    resizeText(localStorage.viewSize || 'medium', 'medium'); // initially resize text to medium (original size) or the previous selection by user

    // Create a MutationObserver instance to monitor new text containing nodes if they are
    // added on DOM modification
    var observer = new MutationObserver((mutations) => {
      var addedNodes = [];
      for (var i = 0; i < mutations.length; i++){
        if (mutations[i].type === 'childList'){
          var nodeArray = Array.prototype.slice.call(mutations[i].addedNodes); // convert the added nodes to an array from nodelist
          addedNodes = addedNodes.concat(nodeArray)
        }
      }
      for (var i = 0; i < addedNodes.length; i++) {
        addTextElements(addedNodes[i])
      }
    });
    observer.observe(document.documentElement, {subtree: true, childList: true});
  };

  // Get all text containing elements, so we don't have to waste time on non-text containing
  // elements. This makes it much faster to change text sizes. This also however means that
  // we have to repeat this function each time an element is added to the DOM tree.
  function updateTextElements(){
    textContainingElements = [];
    var elements = document.querySelectorAll('body *');

    //forEach was messing up here
    for (element in elements) {
      if (elements[element].innerText){//} && textResizer.className != elements[element].parentNode.className) {
        textContainingElements.push(elements[element]);
      }
    }
  }

  // add text elements to the textContainingElements array, so that on an infinitely scrolling webpage, for example, they will still work
  function addTextElements(node) {
    if (node.className === 'truncated-text') {
      console.log('VFOUND!!!')
      console.log('  FOUND!!!')
      console.log('^FOUND!!!')
    }
    if (node.innerText) {
      var sizeMultiplier = sizeToRatio[localStorage.viewSize || 'medium'].actualSize;
      var size = window.getComputedStyle(node, null).getPropertyValue('font-size');
      node.style.fontSize = parseFloat(size) * sizeMultiplier + 'px';
      textContainingElements.push(node);
    } else if (node.children) {
      for (var i=0; i < node.children.length; i++) {
        addTextElements(node.children[i]);
      }
    }
  }

  // For resizing text. Assuming this is completely modular, it's going to be a bit
  // inefficient because we'll be changing the font size for every element
  function resizeText(oldSize, newSize){
    // Modify the A that got selected and reset the one that was previously selected
    textResizer.querySelector(`div:nth-child(${Object.keys(sizeToRatio).indexOf(oldSize) + 1})`).className = '';
    textResizer.querySelector(`div:nth-child(${Object.keys(sizeToRatio).indexOf(newSize) + 1})`).className = 'selected';

    // Find current scroll position
    var scrollRatio = window.scrollY / document.documentElement.scrollHeight;

    // Modify the font size of all text-containing elements
    var sizeMultiplier = sizeToRatio[newSize].actualSize / sizeToRatio[oldSize].actualSize;
    textContainingElements.forEach((element) => {
      var size = window.getComputedStyle(element, null).getPropertyValue('font-size');
      element.style.fontSize = parseFloat(size) * sizeMultiplier + 'px';
    });

    // Return user to approximate previous scroll position
    window.scroll(window.scrollX, scrollRatio * document.documentElement.scrollHeight);

    // Change current size to the new size that we've updated it to
    localStorage.viewSize = newSize;
  }

  var update = function(){
    updateTextElements(); // create initial log of text containing elements
    updateElement();
  }

  var setOptions = function(opts){
    options = opts;
    update();
  }

  // Since we're adding an element to the body, we need to wait until the DOM is
  // ready before inserting our widget.
  if (document.readyState == 'loading')
    document.addEventListener('DOMContentLoaded', update);
  else
    update();

  // This is used by the preview to enable live updating of the app while previewing.
  // See the preview.handlers section of the install.json file to see where it's used.
  INSTALL_SCOPE = {
    setOptions: setOptions
  };

})()
<|endoftext|>


<commit_before>(function () {
  /*global ko: true, Sammy: true, ace: true*/

  var app = {};

  // Navigation
  app.router = Sammy(function() {
    this.get('#/(|download|documentation)', function (context, target) {
      $('[data-tab]')
        .each(function (i, tab) {
          var $tab = $(tab);

          if ($tab.attr('data-tab') === target) {
            $('ul.tabs a[href="#/' + target + '"]').parent().addClass('active');
            $tab.show();
          }
          else {
            $('ul.tabs a[href="#/' + $tab.attr('data-tab') + '"]').parent().removeClass('active');
            $tab.hide();
          }
        });
    });
  });

  // Downloads
  app.downloads = (function () {
    var filesUrl = 'https://api.github.com/repos/gnab/remark/contents/downloads?ref=gh-pages',
      historyUrl = 'https://api.github.com/repos/gnab/remark/contents/HISTORY.md',
      files = ko.observableArray(),
      history = ko.observable(),
      items = ko.computed(createItems);

    function fetch () {
      $.getJSON(filesUrl, function (result, status) {
        if (status === "success") {
          files(result.reverse());
        }
      });
      $.getJSON(historyUrl, function (result, status) {
        if (status === "success") {
          var text = decodeBase64(result.content.replace(/\n/g, '')),
            entries = text.split(/\n*### ([^\n]+)\n+/).slice(1);

          history(entries);
        }
      });
    }

    function decodeBase64 (data) {
      return atob && atob(data) || '';
    }

    function createItems() {
      if (files().length === 0 || history() === undefined) {
        return [];
      }

      return files();
    }

    return {
      fetch: fetch,
      items: items,
      history: history
    };
  }());

  // Editor
  var editor = ace.edit("editor");
  editor.setTheme("ace/theme/twilight");
  editor.getSession().setMode("ace/mode/markdown");
  editor.on('change', function(e) {
    remark.loadFromString(editor.getValue());
  });
  editor.selection.on('changeCursor', function(e) {
    var value = editor.getValue(),
        line = editor.selection.getCursor().row;
  });

  // Slideshow
  remark.createSlideshow(document.getElementById('slideshow'),
    editor.getValue());
  setTimeout(function () {
    remark.resizeSlideshow();
  }, 0);

  ko.applyBindings(app);
  app.router.run('#/');
  app.downloads.fetch();

}());
<commit_msg>Use jsonp to access GitHub API.<commit_after>(function () {
  /*global ko: true, Sammy: true, ace: true*/

  var app = {};

  // Navigation
  app.router = Sammy(function() {
    this.get('#/(|download|documentation)', function (context, target) {
      $('[data-tab]')
        .each(function (i, tab) {
          var $tab = $(tab);

          if ($tab.attr('data-tab') === target) {
            $('ul.tabs a[href="#/' + target + '"]').parent().addClass('active');
            $tab.show();
          }
          else {
            $('ul.tabs a[href="#/' + $tab.attr('data-tab') + '"]').parent().removeClass('active');
            $tab.hide();
          }
        });
    });
  });

  // Downloads
  app.downloads = (function () {
    var filesUrl = 'https://api.github.com/repos/gnab/remark/contents/downloads?ref=gh-pages&callback=?',
      historyUrl = 'https://api.github.com/repos/gnab/remark/contents/HISTORY.md?callback=?',
      files = ko.observableArray(),
      history = ko.observable(),
      items = ko.computed(createItems);

    function fetch () {
      $.getJSON(filesUrl, function (result, status) {
        if (status === "success") {
          files(result.data.reverse());
        }
      });
      $.getJSON(historyUrl, function (result, status) {
        if (status === "success") {
          var text = decodeBase64(result.data.content.replace(/\n/g, '')),
            entries = text.split(/\n*### ([^\n]+)\n+/).slice(1);

          history(entries);
        }
      });
    }

    function decodeBase64 (data) {
      return atob && atob(data) || '';
    }

    function createItems() {
      if (files().length === 0 || history() === undefined) {
        return [];
      }

      return files();
    }

    return {
      fetch: fetch,
      items: items,
      history: history
    };
  }());

  // Editor
  var editor = ace.edit("editor");
  editor.setTheme("ace/theme/twilight");
  editor.getSession().setMode("ace/mode/markdown");
  editor.on('change', function(e) {
    remark.loadFromString(editor.getValue());
  });
  editor.selection.on('changeCursor', function(e) {
    var value = editor.getValue(),
        line = editor.selection.getCursor().row;
  });

  // Slideshow
  remark.createSlideshow(document.getElementById('slideshow'),
    editor.getValue());
  setTimeout(function () {
    remark.resizeSlideshow();
  }, 0);

  ko.applyBindings(app);
  app.router.run('#/');
  app.downloads.fetch();

}());
<|endoftext|>


<commit_before>var config = require('./config.js');
var u = require('./util_modules/utils.js')({seed:100});
var express = require('express');
var app = express();
var hb = require('express3-handlebars');
var http = require("http");
var bodyparser = require("body-parser");
var m = require("mongodb");
var ObjectID = m.ObjectID;
var mConfig = config.mongo;
var PROD = config.isPROD;
var w = require('winston');
var dbUrl = mConfig.url;
var knockoutCollection;
var todos;
var baseUrl = mConfig.baseUrl;
var http = require('http');
var basePort = parseInt(mConfig.basePort);
var meteorPort = basePort+1;
var mainAppPort = basePort+2;

var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxy();

var proxyServer = http.createServer(function(req, res) {
    console.log(req.url);
    var u = req.url.substring(0,5);
    if (u != "/mete" && u != "/sock" && u != "/0546" && u != "/1c62") {
      proxy.web(req, res, {
        target: baseUrl +':' + mainAppPort
     });
    }
    else {
      proxy.web(req, res, {
        target: baseUrl + ':' + meteorPort}, function(e) {
            console.log(e);
            }
      );
    }
});

proxyServer.listen(basePort);

w.add(w.transports.File, { filename: './error.log' });

w.info(dbUrl);

m.MongoClient.connect(dbUrl, {db : {native_parser: false, server:
	{socketOptions: {connectTimeoutMS: 500}}}},
    function(err, db) {
        if (err) {
            w.info(err);
            w.info("could not connect");
            return;
        }
        w.info("connected");
        knockoutCollection = db.collection("knockout");
        todos = db.collection('todos');
        patients = db.collection('patients');
});

app.engine('hbs', hb({extname:'hbs',defaultLayout:"empty.hbs",
	helpers: {
		list: function(items,options) {
		  var out = "
";
		  for(var i=0, l=items.length; i<l; i++) {
			out = out + "
" + options.fn(items[i]) + "
";
		  }
		  return out + "
";
		}}

}));

app.set('view engine', 'hbs');
app.use(bodyparser());
app.use("/",express.static(__dirname + "/public_html"));

app.get('/ben', function(req,res){
    patients.find().toArray(function(err, items) {
        if (err) {
            res.render('ben',{err: 'error'});
            return;
        }
        else {
            res.render('ben',{patients: items});
        }
    });
});

app.get('/benmock', function(req,res){
	var seed = req.param('seed');
	if (seed) {
		u.seed = seed;
	}
	//u.seed = req.seed;
	res.render('ben',{patients: u.getPatientList()});
});

app.get('/todo', function(req,res){
    res.render('todo',{});
});

app.get('/todo/load', function(req,res){

});

app.get('/todo/:uid?',function(req,res){
    var uid = req.params.uid;
    todos.findOne({uid:uid},{w:1},function(err,result) {
        if (err) {
            w.info("error");
            res.end("error");
            return;
        }
        console.log(result);
        if (!result) {
            todos.insert({'uid':uid},{w:1},function(err,result){
                if (err) {
                    res.end("err creating user");
                    return;
                }
                res.end("created new user");
                return;
            });
        }
    });
});

app.get('/greetings', function(req, res){
  res.render('index',{greeting:randElement(greetings),title:req.url});
});

app.get("/request",function(req,res){
    var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    req.connection.socket.remoteAddress;

    var greeting = randElement(greetings);
    res.writeHead(200, {
  'Content-Type': 'text/html' });
  res.end(greeting);
});

app.get("/testdb", function(req,res) {
m.MongoClient.connect(dbUrl, {db : {native_parser: false, server: {socketOptions: {connectTimeoutMS: 500}}}},
    function(err, db) {
        if (err) {
            res.end("could not connect");
            w.info(err);
            return;
        }
        res.end("connected");
});
}
);

app.get("/test", function(req,res){
    if (!knockoutCollection) {
        res.end("No knockoutCollection");
        return;
       }
    else {
    knockoutCollection.find().toArray(function(err, items) {
        if (err) {
            res.end("There was an error");
            w.log("error","Error finding", err);
        }
        res.end("success");
    });
    }
});

app.get("/knockout",function(req,res){
    res.render('knockout',{'greeting':randElement(greetings)});
});

app.post("/knockout/save",function(req,res){
    var cat = req.body.cat;
    if (cat) {
        knockoutCollection.insert({cat:cat},{w:1}, function(err, result) {
            if (err) {
                res.json({txt:err});
            }
            else {
                res.json({txt:"Successfully saved category " + cat});
            }
        });
    }
    else {
        res.json({txt:"Error saving category " + cat});
    }
});

app.get("/knockout/load",function(req,res){
    var val = req.query.x;
    knockoutCollection.find().toArray(function(err, items) {
        if (err) {
            res.json({txt:'error'});
            return;
        }
        res.json(items);
    });

});

app.post("/knockout/del",function(req,res){
    var selector;
    if (req.body.id) {
        selector = {"_id":ObjectID(req.body.id)};
    }
    knockoutCollection.remove(selector,{w:1},function(err,result){
        if (err) {
            res.json({txt:err});
        }
        else {
            res.json({txt:result + " category documents have been deleted."});
        }
    });
});

var greetings = ["Hello","こんにちは","夜露死苦","你好","Guten morgen"];

function randElement(ary) {
        return ary[Math.floor(Math.random() * ary.length)];
    }

app.listen(mainAppPort);

//meteor app
process.env['MONGO_URL'] = dbUrl;
process.env['ROOT_URL']=baseUrl + '/meteor'
process.env['PORT']=meteorPort;
require("./ar_man/main.js");
<commit_msg>added some logging<commit_after>var config = require('./config.js');
var u = require('./util_modules/utils.js')({seed:100});
var express = require('express');
var app = express();
var hb = require('express3-handlebars');
var http = require("http");
var bodyparser = require("body-parser");
var m = require("mongodb");
var ObjectID = m.ObjectID;
var mConfig = config.mongo;
var PROD = config.isPROD;
var w = require('winston');
var dbUrl = mConfig.url;
var knockoutCollection;
var todos;
var baseUrl = mConfig.baseUrl;
var http = require('http');
var basePort = parseInt(mConfig.basePort);
var meteorPort = basePort+1;
var mainAppPort = basePort+2;

var httpProxy = require('http-proxy');
var proxy = httpProxy.createProxy();

var proxyServer = http.createServer(function(req, res) {
    w.info(req.url);
    var u = req.url.substring(0,5);
    if (u != "/mete" && u != "/sock" && u != "/0546" && u != "/1c62") {
      proxy.web(req, res, {
        target: baseUrl +':' + mainAppPort
     });
    }
    else {
      proxy.web(req, res, {
        target: baseUrl + ':' + meteorPort}, function(e) {
            w.info(e);
            }
      );
    }
});

proxyServer.listen(basePort);

w.add(w.transports.File, { filename: './error.log' });

w.info(dbUrl);

m.MongoClient.connect(dbUrl, {db : {native_parser: false, server:
	{socketOptions: {connectTimeoutMS: 500}}}},
    function(err, db) {
        if (err) {
            w.info(err);
            w.info("could not connect");
            return;
        }
        w.info("connected");
        knockoutCollection = db.collection("knockout");
        todos = db.collection('todos');
        patients = db.collection('patients');
});

app.engine('hbs', hb({extname:'hbs',defaultLayout:"empty.hbs",
	helpers: {
		list: function(items,options) {
		  var out = "
";
		  for(var i=0, l=items.length; i<l; i++) {
			out = out + "
" + options.fn(items[i]) + "
";
		  }
		  return out + "
";
		}}

}));

app.set('view engine', 'hbs');
app.use(bodyparser());
app.use("/",express.static(__dirname + "/public_html"));

app.get('/ben', function(req,res){
    patients.find().toArray(function(err, items) {
        if (err) {
            res.render('ben',{err: 'error'});
            return;
        }
        else {
            res.render('ben',{patients: items});
        }
    });
});

app.get('/benmock', function(req,res){
	var seed = req.param('seed');
	if (seed) {
		u.seed = seed;
	}
	//u.seed = req.seed;
	res.render('ben',{patients: u.getPatientList()});
});

app.get('/todo', function(req,res){
    res.render('todo',{});
});

app.get('/todo/load', function(req,res){

});

app.get('/todo/:uid?',function(req,res){
    var uid = req.params.uid;
    todos.findOne({uid:uid},{w:1},function(err,result) {
        if (err) {
            w.info("error");
            res.end("error");
            return;
        }
        console.log(result);
        if (!result) {
            todos.insert({'uid':uid},{w:1},function(err,result){
                if (err) {
                    res.end("err creating user");
                    return;
                }
                res.end("created new user");
                return;
            });
        }
    });
});

app.get('/greetings', function(req, res){
  res.render('index',{greeting:randElement(greetings),title:req.url});
});

app.get("/request",function(req,res){
    var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    req.connection.socket.remoteAddress;

    var greeting = randElement(greetings);
    res.writeHead(200, {
  'Content-Type': 'text/html' });
  res.end(greeting);
});

app.get("/testdb", function(req,res) {
m.MongoClient.connect(dbUrl, {db : {native_parser: false, server: {socketOptions: {connectTimeoutMS: 500}}}},
    function(err, db) {
        if (err) {
            res.end("could not connect");
            w.info(err);
            return;
        }
        res.end("connected");
});
}
);

app.get("/test", function(req,res){
    if (!knockoutCollection) {
        res.end("No knockoutCollection");
        return;
       }
    else {
    knockoutCollection.find().toArray(function(err, items) {
        if (err) {
            res.end("There was an error");
            w.log("error","Error finding", err);
        }
        res.end("success");
    });
    }
});

app.get("/knockout",function(req,res){
    res.render('knockout',{'greeting':randElement(greetings)});
});

app.post("/knockout/save",function(req,res){
    var cat = req.body.cat;
    if (cat) {
        knockoutCollection.insert({cat:cat},{w:1}, function(err, result) {
            if (err) {
                res.json({txt:err});
            }
            else {
                res.json({txt:"Successfully saved category " + cat});
            }
        });
    }
    else {
        res.json({txt:"Error saving category " + cat});
    }
});

app.get("/knockout/load",function(req,res){
    var val = req.query.x;
    knockoutCollection.find().toArray(function(err, items) {
        if (err) {
            res.json({txt:'error'});
            return;
        }
        res.json(items);
    });

});

app.post("/knockout/del",function(req,res){
    var selector;
    if (req.body.id) {
        selector = {"_id":ObjectID(req.body.id)};
    }
    knockoutCollection.remove(selector,{w:1},function(err,result){
        if (err) {
            res.json({txt:err});
        }
        else {
            res.json({txt:result + " category documents have been deleted."});
        }
    });
});

var greetings = ["Hello","こんにちは","夜露死苦","你好","Guten morgen"];

function randElement(ary) {
        return ary[Math.floor(Math.random() * ary.length)];
    }

app.listen(mainAppPort);

//meteor app
process.env['MONGO_URL'] = dbUrl;
process.env['ROOT_URL']=baseUrl + '/meteor'
process.env['PORT']=meteorPort;
require("./ar_man/main.js");

w.info('server started');
<|endoftext|>


<commit_before>/**
 * Dependencies
 */
var _              = require('lodash')
var avatar         = require('./lib/controllers/account/avatar')
var bodyParser     = require('body-parser')
var compress       = require('compression')
var cookieParser   = require('cookie-parser')
var csp            = require('helmet-csp')
var csrf           = require('csurf')
var error          = require('./lib/error')
var errorHandler   = require('errorhandler')()
var express        = require('express')
var favicon        = require('serve-favicon')
var hbs            = require('hbs').__express
var logger         = require('morgan')
var methodOverride = require('method-override')
var moment         = require('moment')
var mongoose       = require('mongoose')
var passport       = require('passport')
var redis          = require('./lib/redis')
var session        = require('express-session')
var utils          = require('./lib/utils')
var validator      = require('express-validator')
var yodel          = require('./lib/yodel')
var RedisStore     = require('connect-redis')(session)

/**
 * Create Express server
 */
var app = module.exports = express()

require('./lib/helpers/flash')(app)
require('./lib/helpers/returnWith')(app)
require('./lib/helpers/handleMongooseError')(app)
require('./lib/helpers/resolveUrl')(app)
require('./lib/helpers/render')(app)

/**
 * Configuration
 */
var config = app.config = require('./lib/config')
var mincer = require('./lib/mincer')

/**
 * Redis
 */
redis.$.on('connect', function() {
  console.log('✔ Redis connection established.')
})
redis.$.on('error', function() {
  console.error('✗ Unable to connect to Redis.')
})

/**
 * Mongoose config
 */
require('./lib/mongo')

/**
 * FTP setup
 */
app.ftp = require('./lib/ftp')

/**
 * TeamSpeak RPC config
 */
yodel.connect(config.teamspeak.yodel)
yodel.on('connect', function() {
  console.log('✔ TeamSpeak RPC connection established.')
})
yodel.on('end', function() {
  console.error('✗ TeamSpeak RPC connection lost.')
})
yodel.on('error', function(err) {
  if(err.code === 'ECONNREFUSED')
    console.error('✗ Unable to connect to TeamSpeak RPC server.')
  else
    console.error('✗ TeamSpeak RPC error:', err)
})

/**
 * moment
 */
moment.locale('de')
app.moment = moment

/**
 * Express configuration
 */

// General setup
// --------------------------------
if(app.get('env') === 'production')
  app.enable('trust proxy')
if(app.get('env') === 'development')
  app.locals.pretty = true
app.disable('x-powered-by')
app.set('port', _.parseInt(config.port) + (_.parseInt(process.env.NODE_APP_INSTANCE) || 0))
app.set('views', __dirname + '/views')
app.set('view engine', 'jade')
app.engine('html', hbs)
app.engine('txt', hbs)
app.use(mincer.assets())
app.use(compress())
app.use(favicon(__dirname + '/public/favicon.ico'))
app.use(logger(process.env.LOGFORMAT || 'dev', {
  skip: function(req) { return /apple-touch-icon|browserconfig|favicon|mstile|robots|assets/.test(req.originalUrl.split('/')[1]) }
}))
app.use(cookieParser())
app.use(bodyParser.json())
app.use(bodyParser.json({ type: 'application/csp-report' }))
app.use(bodyParser.urlencoded({ extended: true }))
app.use(validator())
app.use(methodOverride())
app.use('/api', require('./lib/controllers/api'))

// Session
// --------------------------------
app.use(session({
  name: 'sid',
  resave: false,
  saveUninitialized: true,
  secret: config.secrets.session,
  genid: function() { return '' + new mongoose.Types.ObjectId },
  store: new RedisStore({
    client: redis.$,
    prefix: redis.prefix + 'sess:'
  })
}))

// CSP
// --------------------------------
app.use(csp({
  directives: {
    defaultSrc: ["'self'", 'https://serve.gamekeller.net', 'https://' + config.assetHost],
    scriptSrc: ["'self'", "'unsafe-inline'", 'https://www.google-analytics.com', 'https://nectar.ninja', 'https://api.tumblr.com', 'https://' + config.assetHost],
    styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com', 'https://' + config.assetHost],
    imgSrc: ["'self'", 'data:', 'https://0.gravatar.com', 'https://camo.gamekeller.net', 'https://www.google-analytics.com', 'https://' + config.assetHost],
    fontSrc: ["'self'", 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://' + config.assetHost],
    connectSrc: ["'self'", 'https://www.reddit.com', 'https://nectar.ninja'],
    reportUri: '/report-csp-violation'
  }
}))
app.post('/report-csp-violation', function(req, res) {
  if(req.headers['content-type'] !== 'application/csp-report')
    return res.status(400).end()

  console.error('CSP Violation:', JSON.stringify(req.body, null, 2))
  res.status(200).end()
})

// Passport
// --------------------------------
app.use(passport.initialize())
app.use(passport.session())
require('./lib/helpers/auth')(app)

// CSRF
// --------------------------------
avatar.upload(app)
app.use(csrf())

// Mailer
// --------------------------------
var Mailer = require('./lib/mailer')
app.mailer = new Mailer(_.bind(app.render, app))

// Asset configuration
// --------------------------------
if(app.get('env') === 'development')
  app.use('/assets', mincer.createServer())

if(app.get('env') === 'development' || process.env.SIMULATE_PRODUCTION)
  app.use(express.static(__dirname + '/public'))

// Session updates
// --------------------------------
app.use(function(req, res, next) {
  var now  = Date.now()
  var sess = req.session

  sess.userAgent = req.headers['user-agent']
  sess.ipAddress = req.ip
  sess.usedAt    = now

  if(!sess.createdAt)
    sess.createdAt = now

  if(req.isAuthenticated() && req.session.remember)
    req.session.cookie.maxAge = 7 * 24 * 60 * 60 * 1000

  sess.save(next)
})

// Locals
// --------------------------------
app.use(function(req, res, next) {
  var user = req.user

  _.assign(res.locals, {
    req: req,
    config: config,
    user: user,
    moment: moment,
    url: req.resolveUrl.bind(req),
    utils: utils
  })

  utils.navItemIsActive = function(item) {
    return item.active || (_.isRegExp(item.view) && item.view.test(res.activeView)) || item.view === res.activeView
  }

  // Navigation items
  var nav = res.locals.nav = {
    main: [],
    right: []
  }

  if(user) {
    nav.main.push(
      { href: '/members', name: 'Mitglieder', view: 'members' }
    )

    if(user.admin)
      nav.main.push({ href: '/admin', name: 'Administration', view: /^admin/ })

    nav.right.push(
      { href: '/' + user.username, content: utils.avatarElement(user, 40, 'mini-avatar') + ' ' + user.username },
      { href: '/account', content: '', title: 'Einstellungen' },
      { href: '/logout', form: true, content: '<button type="submit" class="btn btn-link" title="Ausloggen"></button>' }
    )
  } else
    nav.right.push(
      { href: '/login', name: 'Login' },
      { href: '/signup', name: 'Registrieren' }
    )

  // Return path
  var route = req.path.split('/')[1]

  if(req.method === 'POST' || /login|logout|signup|session|forgot/i.test(route) || app.get('env') === 'development' && /apple-touch-icon|browserconfig|favicon|mstile|robots|assets/.test(route))
    return next()

  req.session.returnTo = req.path

  next()
})

/**
 * Routes
 */
app.use(require('./lib/controllers/common'))

// Authentication
app.use('/signup', require('./lib/controllers/auth/signup'))
app.use('/login', require('./lib/controllers/auth/login'))
app.use('/logout', require('./lib/controllers/auth/logout'))
app.use('/forgot', require('./lib/controllers/auth/forgot'))

app.use('/account', require('./lib/controllers/account'))
app.use('/session', require('./lib/controllers/session'))
app.use('/admin', require('./lib/controllers/admin'))
app.use(require('./lib/controllers/profile'))

// 404
app.use(function(req, res, next) {
  return next(new error.NotFound)
})

// Error handler
// --------------------------------
app.use(function(err, req, res, next) {
  if(err.code === 'EBADCSRFTOKEN')
    err = new error.Forbidden('Invalid CSRF token')

  if(!(err instanceof error.HTTPError))
    if(app.get('env') === 'production') {
      console.error(err.stack)
      err = new error.HTTPError(500)
    } else {
      return errorHandler.apply(null, Array.prototype.slice.call(arguments))
    }

  var isApi = req.url.indexOf('/api') === 0

  res.status(err.status)

  if(req.accepts('html') && !isApi)
    res.type('html').send(err.toHTMLString())
  else if(req.accepts('json'))
    res.json(err)
  else
    res.type('txt').send(err.toString())
})

/**
 * Start the server
 */
if(!module.parent)
  app.listen(app.get('port'), function() {
    console.log('✔ Express server listening on port %d in %s mode.', app.get('port'), app.get('env'))
  })<commit_msg>CSP: add userContentHost to permitted image sources<commit_after>/**
 * Dependencies
 */
var _              = require('lodash')
var avatar         = require('./lib/controllers/account/avatar')
var bodyParser     = require('body-parser')
var compress       = require('compression')
var cookieParser   = require('cookie-parser')
var csp            = require('helmet-csp')
var csrf           = require('csurf')
var error          = require('./lib/error')
var errorHandler   = require('errorhandler')()
var express        = require('express')
var favicon        = require('serve-favicon')
var hbs            = require('hbs').__express
var logger         = require('morgan')
var methodOverride = require('method-override')
var moment         = require('moment')
var mongoose       = require('mongoose')
var passport       = require('passport')
var redis          = require('./lib/redis')
var session        = require('express-session')
var utils          = require('./lib/utils')
var validator      = require('express-validator')
var yodel          = require('./lib/yodel')
var RedisStore     = require('connect-redis')(session)

/**
 * Create Express server
 */
var app = module.exports = express()

require('./lib/helpers/flash')(app)
require('./lib/helpers/returnWith')(app)
require('./lib/helpers/handleMongooseError')(app)
require('./lib/helpers/resolveUrl')(app)
require('./lib/helpers/render')(app)

/**
 * Configuration
 */
var config = app.config = require('./lib/config')
var mincer = require('./lib/mincer')

/**
 * Redis
 */
redis.$.on('connect', function() {
  console.log('✔ Redis connection established.')
})
redis.$.on('error', function() {
  console.error('✗ Unable to connect to Redis.')
})

/**
 * Mongoose config
 */
require('./lib/mongo')

/**
 * FTP setup
 */
app.ftp = require('./lib/ftp')

/**
 * TeamSpeak RPC config
 */
yodel.connect(config.teamspeak.yodel)
yodel.on('connect', function() {
  console.log('✔ TeamSpeak RPC connection established.')
})
yodel.on('end', function() {
  console.error('✗ TeamSpeak RPC connection lost.')
})
yodel.on('error', function(err) {
  if(err.code === 'ECONNREFUSED')
    console.error('✗ Unable to connect to TeamSpeak RPC server.')
  else
    console.error('✗ TeamSpeak RPC error:', err)
})

/**
 * moment
 */
moment.locale('de')
app.moment = moment

/**
 * Express configuration
 */

// General setup
// --------------------------------
if(app.get('env') === 'production')
  app.enable('trust proxy')
if(app.get('env') === 'development')
  app.locals.pretty = true
app.disable('x-powered-by')
app.set('port', _.parseInt(config.port) + (_.parseInt(process.env.NODE_APP_INSTANCE) || 0))
app.set('views', __dirname + '/views')
app.set('view engine', 'jade')
app.engine('html', hbs)
app.engine('txt', hbs)
app.use(mincer.assets())
app.use(compress())
app.use(favicon(__dirname + '/public/favicon.ico'))
app.use(logger(process.env.LOGFORMAT || 'dev', {
  skip: function(req) { return /apple-touch-icon|browserconfig|favicon|mstile|robots|assets/.test(req.originalUrl.split('/')[1]) }
}))
app.use(cookieParser())
app.use(bodyParser.json())
app.use(bodyParser.json({ type: 'application/csp-report' }))
app.use(bodyParser.urlencoded({ extended: true }))
app.use(validator())
app.use(methodOverride())
app.use('/api', require('./lib/controllers/api'))

// Session
// --------------------------------
app.use(session({
  name: 'sid',
  resave: false,
  saveUninitialized: true,
  secret: config.secrets.session,
  genid: function() { return '' + new mongoose.Types.ObjectId },
  store: new RedisStore({
    client: redis.$,
    prefix: redis.prefix + 'sess:'
  })
}))

// CSP
// --------------------------------
app.use(csp({
  directives: {
    defaultSrc: ["'self'", 'https://serve.gamekeller.net', 'https://' + config.assetHost],
    scriptSrc: ["'self'", "'unsafe-inline'", 'https://www.google-analytics.com', 'https://nectar.ninja', 'https://api.tumblr.com', 'https://' + config.assetHost],
    styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com', 'https://' + config.assetHost],
    imgSrc: ["'self'", 'data:', 'https://0.gravatar.com', 'https://camo.gamekeller.net', 'https://www.google-analytics.com', 'https://' + config.assetHost, 'https://' + config.userContentHost],
    fontSrc: ["'self'", 'https://fonts.googleapis.com', 'https://fonts.gstatic.com', 'https://' + config.assetHost],
    connectSrc: ["'self'", 'https://www.reddit.com', 'https://nectar.ninja'],
    reportUri: '/report-csp-violation'
  }
}))
app.post('/report-csp-violation', function(req, res) {
  if(req.headers['content-type'] !== 'application/csp-report')
    return res.status(400).end()

  console.error('CSP Violation:', JSON.stringify(req.body, null, 2))
  res.status(200).end()
})

// Passport
// --------------------------------
app.use(passport.initialize())
app.use(passport.session())
require('./lib/helpers/auth')(app)

// CSRF
// --------------------------------
avatar.upload(app)
app.use(csrf())

// Mailer
// --------------------------------
var Mailer = require('./lib/mailer')
app.mailer = new Mailer(_.bind(app.render, app))

// Asset configuration
// --------------------------------
if(app.get('env') === 'development')
  app.use('/assets', mincer.createServer())

if(app.get('env') === 'development' || process.env.SIMULATE_PRODUCTION)
  app.use(express.static(__dirname + '/public'))

// Session updates
// --------------------------------
app.use(function(req, res, next) {
  var now  = Date.now()
  var sess = req.session

  sess.userAgent = req.headers['user-agent']
  sess.ipAddress = req.ip
  sess.usedAt    = now

  if(!sess.createdAt)
    sess.createdAt = now

  if(req.isAuthenticated() && req.session.remember)
    req.session.cookie.maxAge = 7 * 24 * 60 * 60 * 1000

  sess.save(next)
})

// Locals
// --------------------------------
app.use(function(req, res, next) {
  var user = req.user

  _.assign(res.locals, {
    req: req,
    config: config,
    user: user,
    moment: moment,
    url: req.resolveUrl.bind(req),
    utils: utils
  })

  utils.navItemIsActive = function(item) {
    return item.active || (_.isRegExp(item.view) && item.view.test(res.activeView)) || item.view === res.activeView
  }

  // Navigation items
  var nav = res.locals.nav = {
    main: [],
    right: []
  }

  if(user) {
    nav.main.push(
      { href: '/members', name: 'Mitglieder', view: 'members' }
    )

    if(user.admin)
      nav.main.push({ href: '/admin', name: 'Administration', view: /^admin/ })

    nav.right.push(
      { href: '/' + user.username, content: utils.avatarElement(user, 40, 'mini-avatar') + ' ' + user.username },
      { href: '/account', content: '', title: 'Einstellungen' },
      { href: '/logout', form: true, content: '<button type="submit" class="btn btn-link" title="Ausloggen"></button>' }
    )
  } else
    nav.right.push(
      { href: '/login', name: 'Login' },
      { href: '/signup', name: 'Registrieren' }
    )

  // Return path
  var route = req.path.split('/')[1]

  if(req.method === 'POST' || /login|logout|signup|session|forgot/i.test(route) || app.get('env') === 'development' && /apple-touch-icon|browserconfig|favicon|mstile|robots|assets/.test(route))
    return next()

  req.session.returnTo = req.path

  next()
})

/**
 * Routes
 */
app.use(require('./lib/controllers/common'))

// Authentication
app.use('/signup', require('./lib/controllers/auth/signup'))
app.use('/login', require('./lib/controllers/auth/login'))
app.use('/logout', require('./lib/controllers/auth/logout'))
app.use('/forgot', require('./lib/controllers/auth/forgot'))

app.use('/account', require('./lib/controllers/account'))
app.use('/session', require('./lib/controllers/session'))
app.use('/admin', require('./lib/controllers/admin'))
app.use(require('./lib/controllers/profile'))

// 404
app.use(function(req, res, next) {
  return next(new error.NotFound)
})

// Error handler
// --------------------------------
app.use(function(err, req, res, next) {
  if(err.code === 'EBADCSRFTOKEN')
    err = new error.Forbidden('Invalid CSRF token')

  if(!(err instanceof error.HTTPError))
    if(app.get('env') === 'production') {
      console.error(err.stack)
      err = new error.HTTPError(500)
    } else {
      return errorHandler.apply(null, Array.prototype.slice.call(arguments))
    }

  var isApi = req.url.indexOf('/api') === 0

  res.status(err.status)

  if(req.accepts('html') && !isApi)
    res.type('html').send(err.toHTMLString())
  else if(req.accepts('json'))
    res.json(err)
  else
    res.type('txt').send(err.toString())
})

/**
 * Start the server
 */
if(!module.parent)
  app.listen(app.get('port'), function() {
    console.log('✔ Express server listening on port %d in %s mode.', app.get('port'), app.get('env'))
  })<|endoftext|>


<commit_before>var application_root = __dirname,
    express = require("express"),
    path = require("path"),
    mongoose = require('mongoose'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    morgan = require('morgan');

mongoose.Promise = require('bluebird');

var app = express();

// database
console.log("Connecting to db...");
mongoose.connect(process.env.MONGO);

// config
app.use(bodyParser.json());
app.use(methodOverride());
app.use(express.static(path.join(application_root, "public")));
app.use(clientErrorHandler);
app.use(errorHandler);
app.use(morgan('combined'));

var Schema = mongoose.Schema; //Schema.ObjectId

// Schemas

var Advisors = new Schema({
    name: { type: String, required: [true, "Must have a name"] },
    status: {
        type: String,
        required: [true, "Must have a status"],
        enum: ["Busy", "Available", "Unavailable"] ,
        default: "Unavailable"
      },
    turnOverRate: { type: Number },
    avgPerHour: { type: Number },
    modified: { type: Date, default: Date.now }
});

var AdvisorModel = mongoose.model('Advisor', Advisors);

var Students = new Schema({
    name: {
      type: String,
      required: [true, "Must have a student name"],
      min: 2,
      max: 50
    },
    phoneNumber: {
      type: Number,
      validate: function(phoneNumber) { return phoneNumber.length == 10 }
    },
    studenId: { type: Number, required: [true, "Student ID is required"] },
    modified: { type: Date, default: Date.now }
});

// Appoinments Model
var Appointment = new Schema({
    description: {
      type: String,
      required: [true, 'Description required'],
      min: 5,
      max: 100
    },
    student: [Students],
    advisorId: {
      type: String,
      required: [true, 'Advisor required for appoinment'],
      validate: {
          validator: function(advisorId) {
            var promise = AdvisorModel.findById(advisorId).exec();
            promise.then(function(advisor) {
              return advisor.status == 'Available' || advisor.satus == 'Busy';
            })
            .catch(function(err) {
              return false;
           })},
          message : '{VALUE} is not a valid advisor!'
        }
    },
    state: {
      type: String,
      enum: ['Waiting', 'In Progress', 'done'],
      default: 'Waiting',
    },
    type: {
        type: String,
        enum: ['Advising', 'Drop', 'Other'],
        required: 'Wrong Appointment Type or No appoinment Type'
    },
    extraInfo: { type: String },
    position: { type: Number, default: -1 },
    modified: { type: Date, default: Date.now }
});

var AppointmentModel = mongoose.model('Appointment', Appointment);

/* Appoinment Document
{
  "description": "I need to DROP",
  "type": "Drop",
  "student": [{
    "name": "Leeroy Jenkins",
    "studenId": 10005959
  }],
  "advisorId" : "5833af88321f5f26ccd9231b",
  "extraInfo": "Hey, where is Dr. Beckers office?"
}
*/

/* Advisor Document
    {
        "name": "Barach",
        "status": "Busy"
    }
*/

// Queue stuff
console.log("Initializing queue...");
var queue = [];

var promise = AppointmentModel.find().exec()

promise.then(function(appoinments) {
  console.log("Saved queue");
  console.log(appoinments);
  for(var i = 0; i < appoinments.length; i++) {
    var pos = appoinments[i].position;
    queue[pos] = appoinments[i];
  }
})
.catch(function(err) {
  console.log(err);
  console.log("Could not recover stored state");
});

function dequeue_app(idx) {
  if(queue.length == 1) {
    queue = [];
  }
  else {
    for(var i = idx; i < queue.length - 1; i++) {
      queue[i] = queue[i + 1];
      queue[i].position = i;
    }
  }
}

// function move(to_pos, from_pos) {
//   if(idx < 0) return;
//   for(var i = to_pos; i < queue.length; i++) {
//     var temp = queue[to_pos];
//     queue[to_pos] = queue[from_pos];

//   }
// }

// REST api

app.get('/api', function (req, res) {
  res.send('Kiosk API is running');
});

// POST to CREATE
app.post('/api/appointments', function (req, res) {
  var appoinment = new AppointmentModel({
    description: req.body.description,
    student: req.body.student,
    advisorId: req.body.advisorId,
    type: req.body.type,
    extraInfo: req.body.extraInfo,
    position: queue.length + 1
  });

  appoinment.save(function (err) {
    if (!err) {
      queue.push(appoinment);
      console.log("Place in queue");
      console.log(queue[queue.length - 1].position)
      return res.send(appoinment)
    } else {
      return res.send(err)
    }
  });
});

// PUT to UPDATE

// Bulk update
app.put('/api/appointments', function (req, res) {
    var i, len = 0;
    console.log("is Array req.body.appoinments");
    console.log(Array.isArray(req.body.appoinments));
    console.log("PUT: (appoinments)");
    console.log(req.body.appoinments);
    if (Array.isArray(req.body.appoinments)) {
        len = req.body.appoinments.length;
    }
    for (i = 0; i < len; i++) {
        console.log("UPDATE appoinment by id:");
        for (var id in req.body.appoinments[i]) {
            console.log(id);
        }
        AppointmentModel.update({ "_id": id }, req.body.appoinments[i][id], function (err, numAffected) {
            if (err) {
                console.log("Error on update");
                console.log(err);
                return res.send(err);
            } else {
                console.log("updated num: " + numAffected);
                return res.send(req.body.appoinments)
            }
        });
    }
});

// Single update
app.put('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    appoinment.description = req.body.description;
    appoinment.student = req.body.student;
    appoinment.advisor = req.body.advisor;
    appoinment.type = req.body.type;
    appoinment.extraInfo = req.body.extraInfo;
    return appoinment.save(function (err) {
      if (!err) {
        console.log("updated");
        return res.send(appoinment);
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

// Update Appointment state
app.put('/api/appointments/:id/state', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    appoinment.state = req.body.state;
    return appoinment.save(function (err) {
      if (!err) {
        console.log("updated");
        return res.send(appoinment);
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

// GET to READ

// List Appoinments
app.get('/api/appointments', function (req, res) {
  return res.send(queue);
});

// Single appoinment
app.get('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    if (!err) {
      return res.send(appoinment);
    } else {
      return res.send(err);
    }
  });
});

// DELETE to DESTROY

// Bulk destroy all appoinments
app.delete('/api/appointments', function (req, res) {
  AppointmentModel.remove(function (err) {
    if (!err) {
      console.log("removed");
      queue = [];
      return res.send(queue);
    } else {
      return res.send(err);
    }
  });
});

// remove a single appoinment
app.delete('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    return appoinment.remove(function (err) {
      if (!err) {
        for(var i = 0; i < queue.length; i++) {
          if(req.param.id === appoinment._id) {
            dequeue_app(i);
          }
        }
        console.log("removed");
        return res.send('');
      } else {
        return res.send(err);
      }
    });
  });
});

// Get next up
app.get('/api/appointments/next', function (req, res) {
  return res.send(queue[0]);
});

// Remove next up
app.delete('/api/appointments/next', function (req, res) {
  console.log('Removing -->' + queue[0]._id)
  var promise = AppointmentModel.findById(queue[0]._id).exec();
  promise.then(function(appointment) {
    return appointment.remove()
  })
  .then(function(appointment) {
    console.log("dequeuing")
    dequeue_app();
    res.send(queue)
  })
  .catch(function(err) {
    console.log(err);
  })
});

// Add Advisor
app.post('/api/advisors', function (req, res) {
  var advisor;
  console.log("POST: ");
  console.log(req.body);
  advisor = new AdvisorModel({
    name: req.body.name,
    status: req.body.status
  });
  advisor.save(function (err) {
    if (!err) {
      console.log("created");
      return res.send(advisor);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
});

// Update Advisor
app.put('/api/advisors/:id', function (req, res) {
  var promise = AdvisorModel.findById(req.params.id).exec();
  promise.then(function(advisor) {
    advisor.name = req.body.name
    advisor.status = req.body.status;
    return advisor.save();
  })
  .then(function(advisor) {
    console.log("Advisor Saved!");
    return res.send(advisor);
  })
  .catch(function(err) {
    console.log(err);
    return res.send(err);
  })
});

// Update Advisor Status
app.put('/api/advisors/:id/status', function (req, res) {
  var promise = AdvisorModel.findById(req.params.id).exec();
  promise.then(function(advisor) {
    advisor.status = req.body.status;
    return advisor.save();
  })
  .then(function(advisor) {
    console.log("Advisor Updated!");
    return res.send(advisor);
  })
  .catch(function(err) {
    console.log(err);
    return res.send(err);
  })
});

// Get Advisors
app.get('/api/advisors', function (req, res) {
 return AdvisorModel.find(function (err, advisors) {
    if (!err) {
      console.log("Getting Advisors");
      return res.send(advisors);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
});

// Get Advisor by id
app.get('/api/advisors/:id', function (req, res) {
   return AdvisorModel.findById(req.params.id, function (err, advisor) {
    if (!err) {
      return res.send(advisor);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
})

// Delete Advsior
app.delete('/api/advisors/:id', function (req, res) {
  return AdvisorModel.findById(req.params.id, function (err, advisor) {
    return advisor.remove(function (err) {
      if (!err) {
        console.log("removed");
        return res.send('');
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

// launch server
app.listen(4242);
console.log("Listening on 4242...");
<commit_msg>fixed position bug and update bug<commit_after>var application_root = __dirname,
    express = require("express"),
    path = require("path"),
    mongoose = require('mongoose'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    morgan = require('morgan');

mongoose.Promise = require('bluebird');

var app = express();

// database
console.log("Connecting to db...");
mongoose.connect(process.env.MONGO);

// config
app.use(bodyParser.json());
app.use(methodOverride());
app.use(express.static(path.join(application_root, "public")));
app.use(clientErrorHandler);
app.use(errorHandler);
app.use(morgan('combined'));

var Schema = mongoose.Schema; //Schema.ObjectId

// Schemas

var Advisors = new Schema({
    name: { type: String, required: [true, "Must have a name"] },
    status: {
        type: String,
        required: [true, "Must have a status"],
        enum: ["Busy", "Available", "Unavailable"] ,
        default: "Unavailable"
      },
    turnOverRate: { type: Number },
    avgPerHour: { type: Number },
    modified: { type: Date, default: Date.now }
});

var AdvisorModel = mongoose.model('Advisor', Advisors);

var Students = new Schema({
    name: {
      type: String,
      required: [true, "Must have a student name"],
      min: 2,
      max: 50
    },
    phoneNumber: {
      type: Number,
      validate: function(phoneNumber) { return phoneNumber.length == 10 }
    },
    studenId: { type: Number, required: [true, "Student ID is required"] },
    modified: { type: Date, default: Date.now }
});

// Appoinments Model
var Appointment = new Schema({
    description: {
      type: String,
      required: [true, 'Description required'],
      min: 5,
      max: 100
    },
    student: [Students],
    advisorId: {
      type: String,
      required: [true, 'Advisor required for appoinment'],
      validate: {
          validator: function(advisorId) {
            var promise = AdvisorModel.findById(advisorId).exec();
            promise.then(function(advisor) {
              return advisor.status == 'Available' || advisor.satus == 'Busy';
            })
            .catch(function(err) {
              return false;
           })},
          message : '{VALUE} is not a valid advisor!'
        }
    },
    state: {
      type: String,
      enum: ['Waiting', 'In Progress', 'done'],
      default: 'Waiting',
    },
    type: {
        type: String,
        enum: ['Advising', 'Drop', 'Other'],
        required: 'Wrong Appointment Type or No appoinment Type'
    },
    extraInfo: { type: String },
    position: { type: Number, default: -1 },
    modified: { type: Date, default: Date.now }
});

var AppointmentModel = mongoose.model('Appointment', Appointment);

/* Appoinment Document
{
  "description": "I need to DROP",
  "type": "Drop",
  "student": [{
    "name": "Leeroy Jenkins",
    "studenId": 10005959
  }],
  "advisorId" : "5833af88321f5f26ccd9231b",
  "extraInfo": "Hey, where is Dr. Beckers office?"
}
*/

/* Advisor Document
    {
        "name": "Barach",
        "status": "Busy"
    }
*/

// Queue stuff
console.log("Initializing queue...");
var queue = [];

var promise = AppointmentModel.find().exec()

promise.then(function(appoinments) {
  console.log("Saved queue");
  console.log(appoinments);
  for(var i = 0; i < appoinments.length; i++) {
    var pos = appoinments[i].position;
    queue[pos] = appoinments[i];
  }
})
.catch(function(err) {
  console.log(err);
  console.log("Could not recover stored state");
});

function dequeue_app(idx) {
  if(queue.length == 1) {
    queue = [];
  }
  else {
    for(var i = idx; i < queue.length - 1; i++) {
      queue[i] = queue[i + 1];
      queue[i].position = i;
    }
  }
}

// function move(to_pos, from_pos) {
//   if(idx < 0) return;
//   for(var i = to_pos; i < queue.length; i++) {
//     var temp = queue[to_pos];
//     queue[to_pos] = queue[from_pos];

//   }
// }

// REST api

app.get('/api', function (req, res) {
  res.send('Kiosk API is running');
});

// POST to CREATE
app.post('/api/appointments', function (req, res) {
  var appoinment = new AppointmentModel({
    description: req.body.description,
    student: req.body.student,
    advisorId: req.body.advisorId,
    type: req.body.type,
    extraInfo: req.body.extraInfo,
    position: queue.length
  });

  appoinment.save(function (err) {
    if (!err) {
      queue.push(appoinment);
      console.log("Place in queue");
      console.log(queue[queue.length - 1].position)
      return res.send(appoinment)
    } else {
      return res.send(err)
    }
  });
});

// PUT to UPDATE

// Bulk update
// app.put('/api/appointments', function (req, res) {
//     var i, len = 0;
//     console.log("is Array req.body.appoinments");
//     console.log(Array.isArray(req.body.appoinments));
//     console.log("PUT: (appoinments)");
//     console.log(req.body.appoinments);
//     if (Array.isArray(req.body.appoinments)) {
//         len = req.body.appoinments.length;
//     }
//     for (i = 0; i < len; i++) {
//         console.log("UPDATE appoinment by id:");
//         for (var id in req.body.appoinments[i]) {
//             console.log(id);
//         }
//         AppointmentModel.update({ "_id": id }, req.body.appoinments[i][id], function (err, numAffected) {
//             if (err) {
//                 console.log("Error on update");
//                 console.log(err);
//                 return res.send(err);
//             } else {
//                 console.log("updated num: " + numAffected);
//                 return res.send(req.body.appoinments)
//             }
//         });
//     }
// });

// Single update
app.put('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    appoinment.description = req.body.description;
    appoinment.student = req.body.student;
    appoinment.advisor = req.body.advisor;
    appoinment.type = req.body.type;
    appoinment.extraInfo = req.body.extraInfo;
    return appoinment.save(function (err) {
      if (!err) {
        console.log("updated");
        queue[appoinment.position] = appoinment;
        return res.send(appoinment);
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

// Update Appointment state
app.put('/api/appointments/:id/state', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    appoinment.state = req.body.state;
    return appoinment.save(function (err) {
      if (!err) {
        console.log("updated");
        queue[appoinment.position] = appoinment;
        return res.send(appoinment);
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

// GET to READ

// List Appoinments
app.get('/api/appointments', function (req, res) {
  return res.send(queue);
});

// Single appoinment
app.get('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    if (!err) {
      return res.send(appoinment);
    } else {
      return res.send(err);
    }
  });
});

// DELETE to DESTROY

// Bulk destroy all appoinments
app.delete('/api/appointments', function (req, res) {
  AppointmentModel.remove(function (err) {
    if (!err) {
      console.log("removed");
      queue = [];
      return res.send(queue);
    } else {
      return res.send(err);
    }
  });
});

// remove a single appoinment
app.delete('/api/appointments/:id', function (req, res) {
  return AppointmentModel.findById(req.params.id, function (err, appoinment) {
    return appoinment.remove(function (err) {
      if (!err) {
        dequeue_app(appoinment.position);
        console.log("removed");
        return res.send('');
      } else {
        return res.send(err);
      }
    });
  });
});

// Get next up
app.get('/api/appointments/next', function (req, res) {
  console.log(queue[0])
  return res.send(queue[0]);
});

// Remove next up
app.delete('/api/appointments/next', function (req, res) {
  console.log('Removing -->' + queue[0]._id)
  var promise = AppointmentModel.findById(queue[0]._id).exec();
  promise.then(function(appointment) {
    return appointment.remove()
  })
  .then(function(appointment) {
    console.log("dequeuing")
    dequeue_app();
    res.send(queue)
  })
  .catch(function(err) {
    console.log(err);
  })
});

// Add Advisor
app.post('/api/advisors', function (req, res) {
  var advisor;
  console.log("POST: ");
  console.log(req.body);
  advisor = new AdvisorModel({
    name: req.body.name,
    status: req.body.status
  });
  advisor.save(function (err) {
    if (!err) {
      console.log("created");
      return res.send(advisor);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
});

// Update Advisor
app.put('/api/advisors/:id', function (req, res) {
  var promise = AdvisorModel.findById(req.params.id).exec();
  promise.then(function(advisor) {
    advisor.name = req.body.name
    advisor.status = req.body.status;
    return advisor.save();
  })
  .then(function(advisor) {
    console.log("Advisor Saved!");
    return res.send(advisor);
  })
  .catch(function(err) {
    console.log(err);
    return res.send(err);
  })
});

// Update Advisor Status
app.put('/api/advisors/:id/status', function (req, res) {
  var promise = AdvisorModel.findById(req.params.id).exec();
  promise.then(function(advisor) {
    advisor.status = req.body.status;
    return advisor.save();
  })
  .then(function(advisor) {
    console.log("Advisor Updated!");
    return res.send(advisor);
  })
  .catch(function(err) {
    console.log(err);
    return res.send(err);
  })
});

// Get Advisors
app.get('/api/advisors', function (req, res) {
 return AdvisorModel.find(function (err, advisors) {
    if (!err) {
      console.log("Getting Advisors");
      return res.send(advisors);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
});

// Get Advisor by id
app.get('/api/advisors/:id', function (req, res) {
   return AdvisorModel.findById(req.params.id, function (err, advisor) {
    if (!err) {
      return res.send(advisor);
    } else {
      console.log(err);
      return res.send(err);
    }
  });
})

// Delete Advsior
app.delete('/api/advisors/:id', function (req, res) {
  return AdvisorModel.findById(req.params.id, function (err, advisor) {
    return advisor.remove(function (err) {
      if (!err) {
        console.log("removed");
        return res.send('');
      } else {
        console.log(err);
        return res.send(err);
      }
    });
  });
});

function clientErrorHandler (err, req, res, next) {
  if (req.xhr) {
    res.status(500).send({ error: 'Something failed!' })
  } else {
    next(err)
  }
}

function errorHandler (err, req, res, next) {
  res.status(500)
  res.render('error', { error: err })
}

// launch server
app.listen(4242);
console.log("Listening on 4242...");
<|endoftext|>


<commit_before>$(function() {
    // Our global object
    window.irc = {};

    // socket.io init
    var socket = io.connect('http://localhost');

    // MODELS & COLLECTIONS
    // ====================
    var Message = Backbone.Model.extend({
        // expected properties:
        // - sender
        // - text
    });

    var Stream = Backbone.Collection.extend({
        model: Message
    });

    var Person = Backbone.Model.extend({
    });

    var Participants = Backbone.Collection.extend({
        model: Person
    });

    var Frame = Backbone.Model.extend({
        // expected properties:
        // - name
        defaults: {'type': 'channel'},
        initialize: function() {
            this.stream = new Stream;
            this.participants = new Participants;
        },

        setActive: function() {
            console.log('Setting ' + this.get('name') + ' as the active frame.');
            // More stuff will go here
        },

        part: function() {
            console.log('Leaving ' + this.get('name'));
            this.destroy();
        }

    });

    var FrameList = Backbone.Collection.extend({
        model: Frame,

        getByName: function(name) {
            return this.detect(function(frame) {
                return frame.get('name') === name;
            });
        }

    });

    window.frames = new FrameList;

    // VIEWS
    // =====
    var MessageView = Backbone.View.extend({
        tmpl: $('#message-tmpl').html(),
    	render: function() {
            var context = {
                sender: this.model.get('sender'),
                text: this.model.get('text')
            };
            var html = Mustache.to_html(this.tmpl, context);
            $(this.el).html(html);
            return this;
    	}
    });

    var FrameView = Backbone.View.extend({
        el: $('.frame'),
        // to track scroll position
        position: {},

        initialize: function() {
            // frame.bind('add', this.focus, this);
            _.bindAll(this);
        },

    	addMessage: function(message, single) {
            // Expensive -- only do this on single message additions
            if (single) {
                var position = $('.output').scrollTop();
                atBottom = $('.output')[0].scrollHeight - position
                           == $('.output').innerHeight();
                var position = this.$('.output').scrollTop();
            }
            var view = new MessageView({model: message});
            $('.output').append(view.render().el);
            // Scroll to bottom on new message if already at bottom
            if (atBottom) {
                $('.output').scrollTop(position + 100);
            }
    	},

        addNick: function(person) {
            // TODO: Use a template here!
            this.$('.nicks').append('
' + person.get('opStatus') + person.get('nick') + '
');
        },

        // Switch focus to a different frame
        focus: function(frame) {
            // Save scroll position for current frame
            if (this.focused) {
                this.position[this.focused.get('name')] = this.$('.output').scrollTop();
            }
            this.focused = frame;
            this.$('.output').empty();
            this.$('.nicks').empty();

            var self = this;
            frame.stream.each(function(message) {
                self.addMessage(message, false);
            });
            frame.participants.each(this.addNick);
            if (frame.get('name') == 'status')
                this.$('.nicks').hide();
            else
                this.$('.nicks').show();
            if (frame.get('type') == 'channel')
                this.el.addClass('channel');
            else
                this.el.removeClass('channel');

            this.$('.output').scrollTop(this.position[frame.get('name')] || 0);

            // Only the selected frame should send messages
            frames.each(function(frm) {
                frm.stream.unbind('add');
                frm.participants.unbind('add');
            });
            frame.stream.bind('add', this.addMessage, this);
            frame.participants.bind('add', this.addNick, this);
        },

        updateNicks: function(model, nicks) {
            console.log('Nicks rendered');
        }
    });

    var FrameTabView = Backbone.View.extend({
        tagName: 'li',
        tmpl: $('#tab-tmpl').html(),

        initialize: function() {
            this.model.bind('destroy', this.close, this);
            this.render();
        },

        events: {
            'click': 'setActive',
            'click .close-frame': 'part'
        },

        part: function() {
            socket.emit('part', this.model.get('name'));
        },

        close: function() {
            $(this.el).remove();
        },

        // Set as active tab; focus window on frame
        setActive: function() {
            console.log('View setting active status');
            $(this.el).addClass('active')
                .siblings().removeClass('active');
            irc.frameWindow.focus(this.model);
        },

        render: function() {
            console.log(this.model);
            var self = this;
            var context = {
                text: this.model.get('name'),
                type: this.model.get('type'),
                isStatus: function() {
                    return self.model.get('type') == 'status';
                }
            };
            var html = Mustache.to_html(this.tmpl, context);
            $(this.el).html(html);
            return this;
        }

    });

    var AppView = Backbone.View.extend({
        el: $('#content'),
        testFrames: $('#sidebar .frames'),
        frameList: $('header .frames'),

        initialize: function() {
            frames.bind('add', this.addTab, this);
            this.input = this.$('#prime-input');
            this.render();
        },

        events: {
            'keypress #prime-input': 'parseInput',
        },

        addTab: function(frame) {
            var tab = new FrameTabView({model: frame});
            this.frameList.append(tab.el);
            tab.setActive();
        },

        joinChannel: function(name) {
            socket.emit('join', name);
        },

        parseInput: function(e) {
            if (e.keyCode != 13) return;
            var frame = frameWindow.focused,
                input = this.input.val();

            if (input.indexOf('/') === 0) {
                console.log('IRC command detected -- sending to server');
                socket.emit('command', input.substr(1));
            } else {
                socket.emit('say', {
                    target: frame.get('name'),
                    message: input
                });
                frame.stream.add({sender: me.get('nick'), text: input});
            }

            this.input.val('');
        },

        render: function() {
            // Dynamically assign height
            this.el.show();

            $(window).resize(function() {
                sizeContent($('.frame .output'));
                sizeContent($('.frame .nicks'));
            });
        }

    });

    var ConnectView = Backbone.View.extend({
        el: $('#connect'),
        events: {
            'click .btn': 'connect',
            'keypress': 'connectOnEnter'
        },

        initialize: function() {
            _.bindAll(this);
            this.render();
        },

        render: function() {
            this.el.modal({backdrop: true, show: true});
        },

        connectOnEnter: function(e) {
            if (e.keyCode != 13) return;
            this.connect();
        },

        connect: function(e) {
            e && e.preventDefault();

            var connectInfo = {
                nick: $('#connect-nick').val(),
                server: $('#connect-server').val(),
                channels: $('#connect-channels').val().split(' ')
            };

            socket.emit('connect', connectInfo);
            $('#connect').modal('hide');

            irc.me = new Person({nick: connectInfo.nick});

            irc.frameWindow = new FrameView;
            irc.app = new AppView;
            // Create the status "frame"
            frames.add({name: 'status', type: 'status'});

            sizeContent($('.frame .output'));
            sizeContent($('.frame .nicks'));
        }

    });

    var connect = new ConnectView;

    // Set output window to full height, minus other elements
    function sizeContent(sel) {
        var newHeight = $('html').height() - $('header').outerHeight(true) -
                        $('#prime-input').outerHeight(true) -
                        (sel.outerHeight(true) - sel.height()) - 10;
                        // (10 = #content padding)
        sel.height(newHeight);
    }

    // VERY TEMPORARY -- JUST FOR TESTING
    $('#sidebar .frames li').click(function() {
        var name = $(this).text();
        irc.app.joinChannel(name);
    });

    socket.on('message', function(msg) {
        frame = frames.getByName(msg.to);
        if (frame) {
        	frame.stream.add({sender: msg.from, text: msg.text});
        }
    });

    socket.on('motd', function(data) {
        data.motd.split('\n').forEach(function(line) {
            frames.getByName('status').stream.add({sender: '', text: line});
        });
    });

    socket.on('join', function(data) {
        console.log('Join event received for ' + data.channel + ' - ' + data.nick);
        if (data.nick == irc.me.get('nick')) {
            frames.add({name: data.channel});
        }
    });

    socket.on('part', function(data) {
        console.log('Part event received for ' + data.channel + ' - ' + data.nick);
        if (data.nick == irc.me.get('nick')) {
            frames.getByName(data.channel).part();
        }
    });

    socket.on('names', function(data) {
        frame = frames.getByName(data.channel);
        console.log(data);
        for (var nick in data.nicks) {
            frame.participants.add({nick: nick, opStatus: data.nicks[nick]});
        }
    });

});<commit_msg>Fix focusWindow ref; apply focus to first input ofof login dialog.<commit_after>$(function() {
    // Our global object
    window.irc = {};

    // socket.io init
    var socket = io.connect('http://localhost');

    // MODELS & COLLECTIONS
    // ====================
    var Message = Backbone.Model.extend({
        // expected properties:
        // - sender
        // - text
    });

    var Stream = Backbone.Collection.extend({
        model: Message
    });

    var Person = Backbone.Model.extend({
    });

    var Participants = Backbone.Collection.extend({
        model: Person
    });

    var Frame = Backbone.Model.extend({
        // expected properties:
        // - name
        defaults: {'type': 'channel'},
        initialize: function() {
            this.stream = new Stream;
            this.participants = new Participants;
        },

        setActive: function() {
            console.log('Setting ' + this.get('name') + ' as the active frame.');
            // More stuff will go here
        },

        part: function() {
            console.log('Leaving ' + this.get('name'));
            this.destroy();
        }

    });

    var FrameList = Backbone.Collection.extend({
        model: Frame,

        getByName: function(name) {
            return this.detect(function(frame) {
                return frame.get('name') === name;
            });
        }

    });

    // hoisted to window for now, for ease of debugging
    window.frames = new FrameList;

    // VIEWS
    // =====
    var MessageView = Backbone.View.extend({
        tmpl: $('#message-tmpl').html(),
    	render: function() {
            var context = {
                sender: this.model.get('sender'),
                text: this.model.get('text')
            };
            var html = Mustache.to_html(this.tmpl, context);
            $(this.el).html(html);
            return this;
    	}
    });

    var FrameView = Backbone.View.extend({
        el: $('.frame'),
        // to track scroll position
        position: {},

        initialize: function() {
            // frame.bind('add', this.focus, this);
            _.bindAll(this);
        },

    	addMessage: function(message, single) {
            // Expensive -- only do this on single message additions
            if (single) {
                var position = $('.output').scrollTop();
                atBottom = $('.output')[0].scrollHeight - position
                           == $('.output').innerHeight();
                var position = this.$('.output').scrollTop();
            }
            var view = new MessageView({model: message});
            $('.output').append(view.render().el);
            // Scroll to bottom on new message if already at bottom
            if (atBottom) {
                $('.output').scrollTop(position + 100);
            }
    	},

        addNick: function(person) {
            // TODO: Use a template here!
            this.$('.nicks').append('
' + person.get('opStatus') + person.get('nick') + '
');
        },

        // Switch focus to a different frame
        focus: function(frame) {
            // Save scroll position for current frame
            if (this.focused) {
                this.position[this.focused.get('name')] = this.$('.output').scrollTop();
            }
            this.focused = frame;
            this.$('.output').empty();
            this.$('.nicks').empty();

            var self = this;
            frame.stream.each(function(message) {
                self.addMessage(message, false);
            });
            frame.participants.each(this.addNick);
            if (frame.get('name') == 'status')
                this.$('.nicks').hide();
            else
                this.$('.nicks').show();
            if (frame.get('type') == 'channel')
                this.el.addClass('channel');
            else
                this.el.removeClass('channel');

            this.$('.output').scrollTop(this.position[frame.get('name')] || 0);

            // Only the selected frame should send messages
            frames.each(function(frm) {
                frm.stream.unbind('add');
                frm.participants.unbind('add');
            });
            frame.stream.bind('add', this.addMessage, this);
            frame.participants.bind('add', this.addNick, this);
        },

        updateNicks: function(model, nicks) {
            console.log('Nicks rendered');
        }
    });

    var FrameTabView = Backbone.View.extend({
        tagName: 'li',
        tmpl: $('#tab-tmpl').html(),

        initialize: function() {
            this.model.bind('destroy', this.close, this);
            this.render();
        },

        events: {
            'click': 'setActive',
            'click .close-frame': 'part'
        },

        part: function() {
            socket.emit('part', this.model.get('name'));
        },

        close: function() {
            $(this.el).remove();
        },

        // Set as active tab; focus window on frame
        setActive: function() {
            console.log('View setting active status');
            $(this.el).addClass('active')
                .siblings().removeClass('active');
            irc.frameWindow.focus(this.model);
        },

        render: function() {
            console.log(this.model);
            var self = this;
            var context = {
                text: this.model.get('name'),
                type: this.model.get('type'),
                isStatus: function() {
                    return self.model.get('type') == 'status';
                }
            };
            var html = Mustache.to_html(this.tmpl, context);
            $(this.el).html(html);
            return this;
        }

    });

    var AppView = Backbone.View.extend({
        el: $('#content'),
        testFrames: $('#sidebar .frames'),
        frameList: $('header .frames'),

        initialize: function() {
            frames.bind('add', this.addTab, this);
            this.input = this.$('#prime-input');
            this.render();
        },

        events: {
            'keypress #prime-input': 'parseInput',
        },

        addTab: function(frame) {
            var tab = new FrameTabView({model: frame});
            this.frameList.append(tab.el);
            tab.setActive();
        },

        joinChannel: function(name) {
            socket.emit('join', name);
        },

        parseInput: function(e) {
            if (e.keyCode != 13) return;
            var frame = irc.frameWindow.focused,
                input = this.input.val();

            if (input.indexOf('/') === 0) {
                console.log('IRC command detected -- sending to server');
                socket.emit('command', input.substr(1));
            } else {
                socket.emit('say', {
                    target: frame.get('name'),
                    message: input
                });
                frame.stream.add({sender: me.get('nick'), text: input});
            }

            this.input.val('');
        },

        render: function() {
            // Dynamically assign height
            this.el.show();

            $(window).resize(function() {
                sizeContent($('.frame .output'));
                sizeContent($('.frame .nicks'));
            });
        }

    });

    var ConnectView = Backbone.View.extend({
        el: $('#connect'),
        events: {
            'click .btn': 'connect',
            'keypress': 'connectOnEnter'
        },

        initialize: function() {
            _.bindAll(this);
            this.render();
        },

        render: function() {
            this.el.modal({backdrop: true, show: true});
            $('#connect-nick').focus();
        },

        connectOnEnter: function(e) {
            if (e.keyCode != 13) return;
            this.connect();
        },

        connect: function(e) {
            e && e.preventDefault();

            var connectInfo = {
                nick: $('#connect-nick').val(),
                server: $('#connect-server').val(),
                channels: $('#connect-channels').val().split(' ')
            };

            socket.emit('connect', connectInfo);
            $('#connect').modal('hide');

            irc.me = new Person({nick: connectInfo.nick});

            irc.frameWindow = new FrameView;
            irc.app = new AppView;
            // Create the status "frame"
            frames.add({name: 'status', type: 'status'});

            sizeContent($('.frame .output'));
            sizeContent($('.frame .nicks'));
        }

    });

    var connect = new ConnectView;

    // Set output window to full height, minus other elements
    function sizeContent(sel) {
        var newHeight = $('html').height() - $('header').outerHeight(true) -
                        $('#prime-input').outerHeight(true) -
                        (sel.outerHeight(true) - sel.height()) - 10;
                        // (10 = #content padding)
        sel.height(newHeight);
    }

    // VERY TEMPORARY -- JUST FOR TESTING
    $('#sidebar .frames li').click(function() {
        var name = $(this).text();
        irc.app.joinChannel(name);
    });

    socket.on('message', function(msg) {
        frame = frames.getByName(msg.to);
        if (frame) {
        	frame.stream.add({sender: msg.from, text: msg.text});
        }
    });

    socket.on('motd', function(data) {
        data.motd.split('\n').forEach(function(line) {
            frames.getByName('status').stream.add({sender: '', text: line});
        });
    });

    socket.on('join', function(data) {
        console.log('Join event received for ' + data.channel + ' - ' + data.nick);
        if (data.nick == irc.me.get('nick')) {
            frames.add({name: data.channel});
        }
    });

    socket.on('part', function(data) {
        console.log('Part event received for ' + data.channel + ' - ' + data.nick);
        if (data.nick == irc.me.get('nick')) {
            frames.getByName(data.channel).part();
        }
    });

    socket.on('names', function(data) {
        frame = frames.getByName(data.channel);
        console.log(data);
        for (var nick in data.nicks) {
            frame.participants.add({nick: nick, opStatus: data.nicks[nick]});
        }
    });

});<|endoftext|>