<commit_before>// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
// %APPJET%: import("etherpad.admin.plugins");
/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// requires: easysync2.Changeset
// requires: top
// requires: plugins
// requires: undefined
var linestylefilter = {};

linestylefilter.ATTRIB_CLASSES = {
  'bold': 'tag:b',
  'italic': 'tag:i',
  'underline': 'tag:u',
  'strikethrough': 'tag:s'
};

linestylefilter.getAuthorClassName = function(author)
{
  return "author-" + author.replace(/[^a-y0-9]/g, function(c)
  {
    if (c == ".") return "-";
    return 'z' + c.charCodeAt(0) + 'z';
  });
};

// lineLength is without newline; aline includes newline,
// but may be falsy if lineLength == 0
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
{

  var plugins_;
  if (typeof(plugins) != 'undefined')
  {
    plugins_ = plugins;
  }
  else
  {
    plugins_ = parent.parent.plugins;
  }

  if (lineLength == 0) return textAndClassFunc;

  var nextAfterAuthorColors = textAndClassFunc;

  var authorColorFunc = (function()
  {
    var lineEnd = lineLength;
    var curIndex = 0;
    var extraClasses;
    var leftInAuthor;

    function attribsToClasses(attribs)
    {
      var classes = '';
      Changeset.eachAttribNumber(attribs, function(n)
      {
        var key = apool.getAttribKey(n);
        if (key)
        {
          var value = apool.getAttribValue(n);
          if (value)
          {
            if (key == 'author')
            {
              classes += ' ' + linestylefilter.getAuthorClassName(value);
            }
            else if (key == 'list')
            {
              classes += ' list:' + value;
            }
            else if (linestylefilter.ATTRIB_CLASSES[key])
            {
              classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
            }
            else
            {
              classes += plugins_.callHookStr("aceAttribsToClasses", {
                linestylefilter: linestylefilter,
                key: key,
                value: value
              }, " ", " ", "");
            }
          }
        }
      });
      return classes.substring(1);
    }

    var attributionIter = Changeset.opIterator(aline);
    var nextOp, nextOpClasses;

    function goNextOp()
    {
      nextOp = attributionIter.next();
      nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
    }
    goNextOp();

    function nextClasses()
    {
      if (curIndex < lineEnd)
      {
        extraClasses = nextOpClasses;
        leftInAuthor = nextOp.chars;
        goNextOp();
        while (nextOp.opcode && nextOpClasses == extraClasses)
        {
          leftInAuthor += nextOp.chars;
          goNextOp();
        }
      }
    }
    nextClasses();

    return function(txt, cls)
    {
      while (txt.length > 0)
      {
        if (leftInAuthor <= 0)
        {
          // prevent infinite loop if something funny's going on
          return nextAfterAuthorColors(txt, cls);
        }
        var spanSize = txt.length;
        if (spanSize > leftInAuthor)
        {
          spanSize = leftInAuthor;
        }
        var curTxt = txt.substring(0, spanSize);
        txt = txt.substring(spanSize);
        nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
        curIndex += spanSize;
        leftInAuthor -= spanSize;
        if (leftInAuthor == 0)
        {
          nextClasses();
        }
      }
    };
  })();
  return authorColorFunc;
};

linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
{
  var at = /@/g;
  at.lastIndex = 0;
  var splitPoints = null;
  var execResult;
  while ((execResult = at.exec(lineText)))
  {
    if (!splitPoints)
    {
      splitPoints = [];
    }
    splitPoints.push(execResult.index);
  }

  if (!splitPoints) return textAndClassFunc;

  return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
};

linestylefilter.getRegexpFilter = function(regExp, tag)
{
  return function(lineText, textAndClassFunc)
  {
    regExp.lastIndex = 0;
    var regExpMatchs = null;
    var splitPoints = null;
    var execResult;
    while ((execResult = regExp.exec(lineText)))
    {
      if (!regExpMatchs)
      {
        regExpMatchs = [];
        splitPoints = [];
      }
      var startIndex = execResult.index;
      var regExpMatch = execResult[0];
      regExpMatchs.push([startIndex, regExpMatch]);
      splitPoints.push(startIndex, startIndex + regExpMatch.length);
    }

    if (!regExpMatchs) return textAndClassFunc;

    function regExpMatchForIndex(idx)
    {
      for (var k = 0; k < regExpMatchs.length; k++)
      {
        var u = regExpMatchs[k];
        if (idx >= u[0] && idx < u[0] + u[1].length)
        {
          return u[1];
        }
      }
      return false;
    }

    var handleRegExpMatchsAfterSplit = (function()
    {
      var curIndex = 0;
      return function(txt, cls)
      {
        var txtlen = txt.length;
        var newCls = cls;
        var regExpMatch = regExpMatchForIndex(curIndex);
        if (regExpMatch)
        {
          newCls += " " + tag + ":" + regExpMatch;
        }
        textAndClassFunc(txt, newCls);
        curIndex += txtlen;
      };
    })();

    return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
  };
};

linestylefilter.REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')');
linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g');
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
linestylefilter.REGEX_URL, 'url');

linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt)
{
  var nextPointIndex = 0;
  var idx = 0;

  // don't split at 0
  while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0)
  {
    nextPointIndex++;
  }

  function spanHandler(txt, cls)
  {
    if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length)
    {
      func(txt, cls);
      idx += txt.length;
    }
    else
    {
      var splitPoints = splitPointsOpt;
      var pointLocInSpan = splitPoints[nextPointIndex] - idx;
      var txtlen = txt.length;
      if (pointLocInSpan >= txtlen)
      {
        func(txt, cls);
        idx += txt.length;
        if (pointLocInSpan == txtlen)
        {
          nextPointIndex++;
        }
      }
      else
      {
        if (pointLocInSpan > 0)
        {
          func(txt.substring(0, pointLocInSpan), cls);
          idx += pointLocInSpan;
        }
        nextPointIndex++;
        // recurse
        spanHandler(txt.substring(pointLocInSpan), cls);
      }
    }
  }
  return spanHandler;
};

linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
{
  var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);

  var plugins_;
  if (typeof(plugins) != 'undefined')
  {
    plugins_ = plugins;
  }
  else
  {
    plugins_ = parent.parent.plugins;
  }

  var hookFilters = plugins_.callHook("aceGetFilterStack", {
    linestylefilter: linestylefilter,
    browser: browser
  });
  hookFilters.map(function(hookFilter)
  {
    func = hookFilter(lineText, func);
  });

  if (browser !== undefined && browser.msie)
  {
    // IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com.
    // We then normalize it back to text with no angle brackets.  It's weird.  So always
    // break spans at an "at" sign.
    func = linestylefilter.getAtSignSplitterFilter(
    lineText, func);
  }
  return func;
};

// domLineObj is like that returned by domline.createDomLine
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
{
  // remove final newline from text if any
  var text = textLine;
  if (text.slice(-1) == '\n')
  {
    text = text.substring(0, text.length - 1);
  }

  function textAndClassFunc(tokenText, tokenClass)
  {
    domLineObj.appendSpan(tokenText, tokenClass);
  }

  var func = linestylefilter.getFilterStack(text, textAndClassFunc);
  func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
  func(text, '');
};
<commit_msg>Follow Link in Pad - https://github.com/Pita/etherpad-lite/issues/143 -- Note that this only supports links beginning in www.  We also support https:// etc. so we should encourage users to properly urls.  For example: beta.etherpad.org would not be detected by this.  <commit_after>// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
// %APPJET%: import("etherpad.admin.plugins");
/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// requires: easysync2.Changeset
// requires: top
// requires: plugins
// requires: undefined
var linestylefilter = {};

linestylefilter.ATTRIB_CLASSES = {
  'bold': 'tag:b',
  'italic': 'tag:i',
  'underline': 'tag:u',
  'strikethrough': 'tag:s'
};

linestylefilter.getAuthorClassName = function(author)
{
  return "author-" + author.replace(/[^a-y0-9]/g, function(c)
  {
    if (c == ".") return "-";
    return 'z' + c.charCodeAt(0) + 'z';
  });
};

// lineLength is without newline; aline includes newline,
// but may be falsy if lineLength == 0
linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool)
{

  var plugins_;
  if (typeof(plugins) != 'undefined')
  {
    plugins_ = plugins;
  }
  else
  {
    plugins_ = parent.parent.plugins;
  }

  if (lineLength == 0) return textAndClassFunc;

  var nextAfterAuthorColors = textAndClassFunc;

  var authorColorFunc = (function()
  {
    var lineEnd = lineLength;
    var curIndex = 0;
    var extraClasses;
    var leftInAuthor;

    function attribsToClasses(attribs)
    {
      var classes = '';
      Changeset.eachAttribNumber(attribs, function(n)
      {
        var key = apool.getAttribKey(n);
        if (key)
        {
          var value = apool.getAttribValue(n);
          if (value)
          {
            if (key == 'author')
            {
              classes += ' ' + linestylefilter.getAuthorClassName(value);
            }
            else if (key == 'list')
            {
              classes += ' list:' + value;
            }
            else if (linestylefilter.ATTRIB_CLASSES[key])
            {
              classes += ' ' + linestylefilter.ATTRIB_CLASSES[key];
            }
            else
            {
              classes += plugins_.callHookStr("aceAttribsToClasses", {
                linestylefilter: linestylefilter,
                key: key,
                value: value
              }, " ", " ", "");
            }
          }
        }
      });
      return classes.substring(1);
    }

    var attributionIter = Changeset.opIterator(aline);
    var nextOp, nextOpClasses;

    function goNextOp()
    {
      nextOp = attributionIter.next();
      nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs));
    }
    goNextOp();

    function nextClasses()
    {
      if (curIndex < lineEnd)
      {
        extraClasses = nextOpClasses;
        leftInAuthor = nextOp.chars;
        goNextOp();
        while (nextOp.opcode && nextOpClasses == extraClasses)
        {
          leftInAuthor += nextOp.chars;
          goNextOp();
        }
      }
    }
    nextClasses();

    return function(txt, cls)
    {
      while (txt.length > 0)
      {
        if (leftInAuthor <= 0)
        {
          // prevent infinite loop if something funny's going on
          return nextAfterAuthorColors(txt, cls);
        }
        var spanSize = txt.length;
        if (spanSize > leftInAuthor)
        {
          spanSize = leftInAuthor;
        }
        var curTxt = txt.substring(0, spanSize);
        txt = txt.substring(spanSize);
        nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses);
        curIndex += spanSize;
        leftInAuthor -= spanSize;
        if (leftInAuthor == 0)
        {
          nextClasses();
        }
      }
    };
  })();
  return authorColorFunc;
};

linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc)
{
  var at = /@/g;
  at.lastIndex = 0;
  var splitPoints = null;
  var execResult;
  while ((execResult = at.exec(lineText)))
  {
    if (!splitPoints)
    {
      splitPoints = [];
    }
    splitPoints.push(execResult.index);
  }

  if (!splitPoints) return textAndClassFunc;

  return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints);
};

linestylefilter.getRegexpFilter = function(regExp, tag)
{
  return function(lineText, textAndClassFunc)
  {
    regExp.lastIndex = 0;
    var regExpMatchs = null;
    var splitPoints = null;
    var execResult;
    while ((execResult = regExp.exec(lineText)))
    {
      if (!regExpMatchs)
      {
        regExpMatchs = [];
        splitPoints = [];
      }
      var startIndex = execResult.index;
      var regExpMatch = execResult[0];
      regExpMatchs.push([startIndex, regExpMatch]);
      splitPoints.push(startIndex, startIndex + regExpMatch.length);
    }

    if (!regExpMatchs) return textAndClassFunc;

    function regExpMatchForIndex(idx)
    {
      for (var k = 0; k < regExpMatchs.length; k++)
      {
        var u = regExpMatchs[k];
        if (idx >= u[0] && idx < u[0] + u[1].length)
        {
          return u[1];
        }
      }
      return false;
    }

    var handleRegExpMatchsAfterSplit = (function()
    {
      var curIndex = 0;
      return function(txt, cls)
      {
        var txtlen = txt.length;
        var newCls = cls;
        var regExpMatch = regExpMatchForIndex(curIndex);
        if (regExpMatch)
        {
          newCls += " " + tag + ":" + regExpMatch;
        }
        textAndClassFunc(txt, newCls);
        curIndex += txtlen;
      };
    })();

    return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints);
  };
};

linestylefilter.REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')');
linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:|www.)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g');
linestylefilter.getURLFilter = linestylefilter.getRegexpFilter(
linestylefilter.REGEX_URL, 'url');

linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt)
{
  var nextPointIndex = 0;
  var idx = 0;

  // don't split at 0
  while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0)
  {
    nextPointIndex++;
  }

  function spanHandler(txt, cls)
  {
    if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length)
    {
      func(txt, cls);
      idx += txt.length;
    }
    else
    {
      var splitPoints = splitPointsOpt;
      var pointLocInSpan = splitPoints[nextPointIndex] - idx;
      var txtlen = txt.length;
      if (pointLocInSpan >= txtlen)
      {
        func(txt, cls);
        idx += txt.length;
        if (pointLocInSpan == txtlen)
        {
          nextPointIndex++;
        }
      }
      else
      {
        if (pointLocInSpan > 0)
        {
          func(txt.substring(0, pointLocInSpan), cls);
          idx += pointLocInSpan;
        }
        nextPointIndex++;
        // recurse
        spanHandler(txt.substring(pointLocInSpan), cls);
      }
    }
  }
  return spanHandler;
};

linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser)
{
  var func = linestylefilter.getURLFilter(lineText, textAndClassFunc);

  var plugins_;
  if (typeof(plugins) != 'undefined')
  {
    plugins_ = plugins;
  }
  else
  {
    plugins_ = parent.parent.plugins;
  }

  var hookFilters = plugins_.callHook("aceGetFilterStack", {
    linestylefilter: linestylefilter,
    browser: browser
  });
  hookFilters.map(function(hookFilter)
  {
    func = hookFilter(lineText, func);
  });

  if (browser !== undefined && browser.msie)
  {
    // IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com.
    // We then normalize it back to text with no angle brackets.  It's weird.  So always
    // break spans at an "at" sign.
    func = linestylefilter.getAtSignSplitterFilter(
    lineText, func);
  }
  return func;
};

// domLineObj is like that returned by domline.createDomLine
linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj)
{
  // remove final newline from text if any
  var text = textLine;
  if (text.slice(-1) == '\n')
  {
    text = text.substring(0, text.length - 1);
  }

  function textAndClassFunc(tokenText, tokenClass)
  {
    domLineObj.appendSpan(tokenText, tokenClass);
  }

  var func = linestylefilter.getFilterStack(text, textAndClassFunc);
  func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool);
  func(text, '');
};
<|endoftext|>


<commit_before>/*
* This will be a rewrite using OOJS ideas
* Flashcards are made up of decks.
* Decks contain a stack of cards.
* Each card has a front and a back.
* Each card can be correct, incorrect, or skipped.
*
*/

var Flashcards = function(){

    var self = this;

    // define an array of decks
    this.decks = [];

    // DOM elements
    this.elements = {
        // flashcards
        flashcardContainer: document.getElementById('flashcard-container'),
        flashcard: document.getElementById('flashcard'),
        front: document.getElementById('front'),
        back: document.getElementById('back'),
        frontText: front.getElementsByTagName('p')[0],
        backText: back.getElementsByTagName('p')[0],

        // progress bar
        progressBar: document.getElementById('progress-bar'),
        correctProgress: document.getElementById('correct-progress'),
        incorrectProgress: document.getElementById('incorrect-progress'),
        skippedProgress: document.getElementById('skipped-progress'),

        // dropdown to select decks
        deckSelect: document.getElementById('deck-select'),

        // buttons
        correctButton: document.getElementById('correct'),
        incorrectButton: document.getElementById('incorrect'),
        skipButton: document.getElementById('skip')
    };

    // copy of currently selected deck/cards
    this.current = {
        deckID: null,
        cards: null,
        card: null,
        totalCards: null,
        correct: 0,
        incorrect: 0,
        skipped: 0
    };

    // Fisher-Yates (aka Knuth) Shuffle
    // Courtesy of https://bost.ocks.org/mike/shuffle/
    var shuffle = function(array){
        var m = array.length, t, i;

        // While there remain elements to shuffle...
        while (m) {

            // Pick a remaining element...
            i = Math.floor(Math.random() * m--);

            // And swap it with the current element.
            t = array[m];
            array[m] = array[i];
            array[i] = t;
        }

        return array;
    }

    // add new decks
    this.addDeck = function(title){

        console.log('self.decks.length: ', self.decks.length);

        self.decks.push({
            title: title,
            id: self.decks.length,
            cards: [],
            statuses: {
                correct: 0,
                incorrect: 0,
                skipped: 0
            }
        });
    };

    // add new cards to a deck
    this.addCard = function(deckID, front, back){

        console.log('self.decks[' + deckID + '].cards.length: ', self.decks[deckID].cards.length);

        var cards = self.decks[deckID].cards;
        var id = cards.length;

        cards.push({
                id: id,
                front: front,
                back: back,
                status: null
        });
    };

    // shuffle cards in a deck
    this.shuffleDeck = function(deckID){
        var cards = self.decks[deckID].cards;
        cards = shuffle(cards);
        return cards;
    };

    // load a new deck into the current deck
    this.loadDeck = function(){
        // load first deck into current deck
        self.current.deckID = 0;
        self.current.cards = self.decks[0].cards;
        self.current.totalCards = self.decks[0].cards.length;
    };

    // enable deck selection dropdown
    this.enableDeckSelection = function(){

        var decks = self.decks;

        // iterate through each deck
        for(var i = 0; i < decks.length; i++){

            // create a new option element with necessary values
            var option = document.createElement('option');
            option.setAttribute('value', decks[i].id);
            option.text = decks[i].title;

            // append it to the select dropdown
            self.elements.deckSelect.appendChild(option);
        }

        // create an onchange event to switch selected deck
        self.elements.deckSelect.addEventListener('change', function(){

            var deckID = this.value;
            console.log('deckID selected: ', deckID);

            // shuffle the new deck and load the first card from it
            self.current.cards = self.shuffleDeck(deckID);
            self.loadCard();

            // set the progress back to 0
            self.updateProgress();
        });
    };

    // load text on front and back of cards
    this.loadCard = function(){

        // if there are cards remaining
        if(self.current.cards.length > 0){
            // take the first item from the array of
            // current cards
            self.current.card = self.current.cards.shift();

            // place the text from the current card and
            // place it into the front and back
            self.elements.frontText.innerText = self.current.card.front;
            self.elements.backText.innerText = self.current.card.back;
        }
        else {
            self.current.card = {};
            self.current.cards = [];
            self.elements.frontText.innerText = 'Congratulations! You\'ve finished the deck!';
            self.elements.backText.innerText = 'You didn\'t believe me did you? =P';
        }
    };

    // enables and disables card flipping
    this.enableFlipping = function(){

        self.elements.flashcardContainer.addEventListener("mouseover", function(){

            // add class to flip card
            flashcard.className = 'flipped';
        });

        // add event for mouse exit
        self.elements.flashcardContainer.addEventListener("mouseout", function(){

            // remove class to unflip
            flashcard.removeAttribute('class');
        });
    };

    // enables the correct, incorrect, and skip buttons
    this.enableButtons = function(){

        // add events for button presses
        self.elements.correctButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            // update progress
            self.updateProgress(button);

            // load the next card
            self.loadCard();
        });

        self.elements.incorrectButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            // update progress
            self.updateProgress(button);

            // load the next card
            self.loadCard();
        });

        self.elements.skipButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            // update progress
            self.updateProgress(button);

            console.log('self.current.cards.length: ', self.current.cards.length);

            // load the next card
            self.loadCard();
        });
    };

    // updates the progress bar
    this.updateProgress = function(button){

        if(button){

            // get curront card's status
            var status = self.current.card.status;

            console.log('button selected: ', button);
            console.log('current card status: ', status);

            // make changes based on button
            switch(button){
                case 'correct':
                    if(status === 'incorrect'){
                        self.current.incorrect--;
                    }
                    else if(status === 'skipped'){
                        self.current.skipped--;
                    }
                    self.current.correct++;

                    // update card status
                    self.current.card.status = 'correct';

                    break;
                case 'incorrect':
                    if(status === 'skipped'){
                        self.current.skipped--;
                    }
                    self.current.incorrect++;

                    // update card status
                    self.current.card.status = 'incorrect';

                    // move card to back of deck
                    self.current.cards.push(self.current.card);
                    break;
                case 'skip':
                    if(status === 'incorrect'){
                        self.current.incorrect--;
                    }
                    self.current.skipped++;

                    // update card status
                    self.current.card.status = 'skipped';

                    // move card to back of deck
                    self.current.cards.push(self.current.card);
                    break;
                default:
                    break;
            }

            // prevent overflow of correct + incorrect + skipped > totalcards
            if(self.current.correct + self.current.incorrect + self.current.skipped <= self.current.totalCards){
                self.elements.correctProgress.style.width = (self.current.correct / self.current.totalCards) * 100 + '%';
                self.elements.incorrectProgress.style.width = (self.current.incorrect / self.current.totalCards) * 100 + '%';
                self.elements.skippedProgress.style.width = (self.current.skipped / self.current.totalCards) * 100 + '%';
            }
        }
        else {
                self.elements.correctProgress.style.width = '0%';
                self.elements.incorrectProgress.style.width = '0%';
                self.elements.skippedProgress.style.width = '0%';
                self.current.correct = 0;
                self.current.incorrect = 0;
                self.current.skipped = 0;
        }
    };
};

// MAIN

var flashcards = new Flashcards;
flashcards.addDeck('CH1 - Command Line');
flashcards.addDeck('CH2 - Package Managers');
flashcards.addDeck('CH2 - Libraries');
flashcards.addDeck('CH2 - Processes');

flashcards.addCard(0, 'Where is the file that stores bash history?', '~/.bash_history');
flashcards.addCard(0, 'Which command can concatenate files together and send the resulting combination to STDOUT (1)?', 'cat \n\nEx. cat first.txt second.txt > combined.txt');
flashcards.addCard(0, 'Which command directly modifies a file\'s content and sends the changes to STDOUT (0)?', 'sed');

flashcards.addCard(1, 'Before upgrading a package with dpkg, what may need to be done first? and what is the command?', 'The old package may need to be removed first.\n\n# dpkg -r <package-name>');
flashcards.addCard(1, 'Binary packages typically contain what type of content?', 'Subdirectories that mimic the layout of the Linux root directory (i.e. /, /etc, /usr, etc.).');
flashcards.addCard(1, 'Debian package tools combine and compile source packages to create what?', 'Debian binary packages');

flashcards.addCard(2, 'How can binary program files locate libraries?', 'Either by name alone (ex. libc.so.6) or by providing a complete path (ex. /lib/libc.so.6).\n\nNote: a library path (containing directories to search through) must be configured to use names alone.');
flashcards.addCard(2, 'How can the LD_LIBRARY_PATH environment variable be set?', 'export LD_LIBRARY_PATH=<path1>:<pathN>');
flashcards.addCard(2, 'If all dependencies are met for a program but it still fails to load due to missing dependencies, what can be done?', 'Use the ldd command on the libraries shown when running ldd on the program. Sometimes these libraries have dependencies that may be missing.');

flashcards.addCard(3, 'Because many shells include their own internal version of the kill command, what must be done to ensure the external kill command is being used?', 'Call it with a full path\n\ni.e. /bin/kill');
flashcards.addCard(3, 'By default, ps displays which processes? and how wide is the output?', 'ps displays only processes that were run from its own terminal (options -A, -e, and x can overwrite this).\n\nThe output is limited to being 80 characters wide and is truncated beyond that length (options -w and w can ovewrite this).');
flashcards.addCard(3, 'By default, top sorts entries by what? and how often does it update?', 'CPU use\n\nEvery five (5) seconds');

//flashcards.shuffleDeck(0);
flashcards.enableDeckSelection();
flashcards.loadDeck();
flashcards.loadCard();
flashcards.enableFlipping();
flashcards.enableButtons();<commit_msg>Fixed bug with card status and progress bar after finishing deck<commit_after>/*
* This will be a rewrite using OOJS ideas
* Flashcards are made up of decks.
* Decks contain a stack of cards.
* Each card has a front and a back.
* Each card can be correct, incorrect, or skipped.
*
*/

var Flashcards = function(){

    var self = this;

    // define an array of decks
    this.decks = [];

    // DOM elements
    this.elements = {
        // flashcards
        flashcardContainer: document.getElementById('flashcard-container'),
        flashcard: document.getElementById('flashcard'),
        front: document.getElementById('front'),
        back: document.getElementById('back'),
        frontText: front.getElementsByTagName('p')[0],
        backText: back.getElementsByTagName('p')[0],

        // progress bar
        progressBar: document.getElementById('progress-bar'),
        correctProgress: document.getElementById('correct-progress'),
        incorrectProgress: document.getElementById('incorrect-progress'),
        skippedProgress: document.getElementById('skipped-progress'),

        // dropdown to select decks
        deckSelect: document.getElementById('deck-select'),

        // buttons
        correctButton: document.getElementById('correct'),
        incorrectButton: document.getElementById('incorrect'),
        skipButton: document.getElementById('skip')
    };

    // copy of currently selected deck/cards
    this.current = {
        deckID: null,
        cards: null,
        card: null,
        totalCards: null,
        correct: 0,
        incorrect: 0,
        skipped: 0
    };

    // Fisher-Yates (aka Knuth) Shuffle
    // Courtesy of https://bost.ocks.org/mike/shuffle/
    var shuffle = function(array){
        var m = array.length, t, i;

        // While there remain elements to shuffle...
        while (m) {

            // Pick a remaining element...
            i = Math.floor(Math.random() * m--);

            // And swap it with the current element.
            t = array[m];
            array[m] = array[i];
            array[i] = t;
        }

        return array;
    }

    // add new decks
    this.addDeck = function(title){

        console.log('self.decks.length: ', self.decks.length);

        self.decks.push({
            title: title,
            id: self.decks.length,
            cards: [],
            statuses: {
                correct: 0,
                incorrect: 0,
                skipped: 0
            }
        });
    };

    // add new cards to a deck
    this.addCard = function(deckID, front, back){

        console.log('self.decks[' + deckID + '].cards.length: ', self.decks[deckID].cards.length);

        var cards = self.decks[deckID].cards;
        var id = cards.length;

        cards.push({
                id: id,
                front: front,
                back: back,
                status: null
        });
    };

    // shuffle cards in a deck
    this.shuffleDeck = function(deckID){
        var cards = self.decks[deckID].cards;
        cards = shuffle(cards);
        return cards;
    };

    // load a new deck into the current deck
    this.loadDeck = function(){
        // load first deck into current deck
        self.current.deckID = 0;
        self.current.cards = self.decks[0].cards;
        self.current.totalCards = self.decks[0].cards.length;
    };

    // enable deck selection dropdown
    this.enableDeckSelection = function(){

        var decks = self.decks;

        // iterate through each deck
        for(var i = 0; i < decks.length; i++){

            // create a new option element with necessary values
            var option = document.createElement('option');
            option.setAttribute('value', decks[i].id);
            option.text = decks[i].title;

            // append it to the select dropdown
            self.elements.deckSelect.appendChild(option);
        }

        // create an onchange event to switch selected deck
        self.elements.deckSelect.addEventListener('change', function(){

            var deckID = this.value;
            console.log('deckID selected: ', deckID);

            // shuffle the new deck and load the first card from it
            self.current.cards = self.shuffleDeck(deckID);
            self.loadCard();

            // set the progress back to 0
            self.resetProgress();
        });
    };

    // load text on front and back of cards
    this.loadCard = function(){

        // if there are cards remaining
        if(self.current.cards.length > 0){
            // take the first item from the array of
            // current cards
            self.current.card = self.current.cards.shift();

            // place the text from the current card and
            // place it into the front and back
            self.elements.frontText.innerText = self.current.card.front;
            self.elements.backText.innerText = self.current.card.back;
        }
        else {
            console.log('out of cards');
            self.current.card = {};
            self.current.cards = [];
            self.elements.frontText.innerText = 'Congratulations! You\'ve finished the deck!';
            self.elements.backText.innerText = 'You didn\'t believe me did you? =P';
        }
    };

    // enables and disables card flipping
    this.enableFlipping = function(){

        self.elements.flashcardContainer.addEventListener("mouseover", function(){

            // add class to flip card
            flashcard.className = 'flipped';
        });

        // add event for mouse exit
        self.elements.flashcardContainer.addEventListener("mouseout", function(){

            // remove class to unflip
            flashcard.removeAttribute('class');
        });
    };

    // enables the correct, incorrect, and skip buttons
    this.enableButtons = function(){

        // add events for button presses
        self.elements.correctButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            if(self.current.card.status !== undefined){

                // update progress
                self.updateProgress(button);

                // load the next card
                self.loadCard();
            }
        });

        self.elements.incorrectButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            if(self.current.card.status !== undefined){

                // update progress
                self.updateProgress(button);

                // move card to back of deck
                self.current.cards.push(self.current.card);

                // load the next card
                self.loadCard();
            }
        });

        self.elements.skipButton.addEventListener("click", function(){

            // send button to get id
            var button = this.id;

            if(self.current.card.status !== undefined){

                // update progress
                self.updateProgress(button);

                // move card to back of deck
                self.current.cards.push(self.current.card);

                // load the next card
                self.loadCard();
            }
        });
    };

    // resets the progress bar
    this.resetProgress = function(){
        self.elements.correctProgress.style.width = '0%';
        self.elements.incorrectProgress.style.width = '0%';
        self.elements.skippedProgress.style.width = '0%';
        self.current.correct = 0;
        self.current.incorrect = 0;
        self.current.skipped = 0;
    };

    // updates the progress bar
    this.updateProgress = function(button){

        // get current card's status
        var status = self.current.card.status;

        console.log('current status: ', status);

        // make changes based on button
        switch(button){

            case 'correct':
                if(status === 'incorrect'){
                    self.current.incorrect--;
                    self.current.correct++;
                    self.current.card.status = 'correct';
                }
                else if(status === 'skipped'){
                    self.current.skipped--;
                    self.current.correct++;
                    self.current.card.status = 'correct';
                }
                else if(status === null){
                    self.current.correct++;
                    self.current.card.status = 'correct';
                }
                break;

            case 'incorrect':
                if(status === 'skipped'){
                    self.current.skipped--;
                    self.current.incorrect++;
                    self.current.card.status = 'incorrect';
                }
                else if(status === 'correct'){
                    self.current.correct--;
                    self.current.incorrect++;
                    self.current.card.status = 'incorrect';
                }
                else if(status === null){
                    self.current.incorrect++;
                    self.current.card.status = 'incorrect';
                }
                break;

            case 'skip':
                if(status === 'incorrect'){
                    self.current.incorrect--;
                    self.current.skipped++;
                    self.current.card.status = 'skipped';
                }
                else if(status === 'correct'){
                    self.current.correct--;
                    self.current.skipped++;
                    self.current.card.status = 'skipped';
                }
                else if(status === null){
                    self.current.skipped++;
                    self.current.card.status = 'skipped';
                }
                break;
        }

        // prevent overflow of correct + incorrect + skipped > totalcards
        console.log('self.current.totalCards: ', self.current.totalCards);
        console.log('self.current.correct: ', self.current.correct);
        console.log('self.current.incorrect: ', self.current.incorrect);
        console.log('self.current.skipped: ', self.current.skipped);

        self.elements.correctProgress.style.width = (self.current.correct / self.current.totalCards) * 100 + '%';
        self.elements.incorrectProgress.style.width = (self.current.incorrect / self.current.totalCards) * 100 + '%';
        self.elements.skippedProgress.style.width = (self.current.skipped / self.current.totalCards) * 100 + '%';
    };
};

// MAIN

var flashcards = new Flashcards;
flashcards.addDeck('CH1 - Command Line');
flashcards.addDeck('CH2 - Package Managers');
flashcards.addDeck('CH2 - Libraries');
flashcards.addDeck('CH2 - Processes');

flashcards.addCard(0, 'Where is the file that stores bash history?', '~/.bash_history');
flashcards.addCard(0, 'Which command can concatenate files together and send the resulting combination to STDOUT (1)?', 'cat \n\nEx. cat first.txt second.txt > combined.txt');
flashcards.addCard(0, 'Which command directly modifies a file\'s content and sends the changes to STDOUT (0)?', 'sed');

flashcards.addCard(1, 'Before upgrading a package with dpkg, what may need to be done first? and what is the command?', 'The old package may need to be removed first.\n\n# dpkg -r <package-name>');
flashcards.addCard(1, 'Binary packages typically contain what type of content?', 'Subdirectories that mimic the layout of the Linux root directory (i.e. /, /etc, /usr, etc.).');
flashcards.addCard(1, 'Debian package tools combine and compile source packages to create what?', 'Debian binary packages');

flashcards.addCard(2, 'How can binary program files locate libraries?', 'Either by name alone (ex. libc.so.6) or by providing a complete path (ex. /lib/libc.so.6).\n\nNote: a library path (containing directories to search through) must be configured to use names alone.');
flashcards.addCard(2, 'How can the LD_LIBRARY_PATH environment variable be set?', 'export LD_LIBRARY_PATH=<path1>:<pathN>');
flashcards.addCard(2, 'If all dependencies are met for a program but it still fails to load due to missing dependencies, what can be done?', 'Use the ldd command on the libraries shown when running ldd on the program. Sometimes these libraries have dependencies that may be missing.');

flashcards.addCard(3, 'Because many shells include their own internal version of the kill command, what must be done to ensure the external kill command is being used?', 'Call it with a full path\n\ni.e. /bin/kill');
flashcards.addCard(3, 'By default, ps displays which processes? and how wide is the output?', 'ps displays only processes that were run from its own terminal (options -A, -e, and x can overwrite this).\n\nThe output is limited to being 80 characters wide and is truncated beyond that length (options -w and w can ovewrite this).');
flashcards.addCard(3, 'By default, top sorts entries by what? and how often does it update?', 'CPU use\n\nEvery five (5) seconds');

//flashcards.shuffleDeck(0);
flashcards.enableDeckSelection();
flashcards.loadDeck();
flashcards.loadCard();
flashcards.enableFlipping();
flashcards.enableButtons();<|endoftext|>


<commit_before>/**
 * !!! This file can be found as worker.js in the quick_server folder !!!
 *
 * Provides worker oriented networking interface.
 *
 * Created by krause on 2016-06-22.
 */

export const CONFIG = {
  preDelay: 500,
  timeStart: 500,
  // has to be below 2min so the server doesn't remove the result
  timeCap: 1000*60,
  timeMinInc: 10,
  timeMulInc: 1.01,
  animation: ["⠋", "⠙", "⠸", "⠴", "⠦", "⠇"],
  // animation: ["/", "-", "\\", "|"],
  animationTime: 300,
};
export const VERSION = "0.5.0";

export class Worker {
  constructor() {
    window.addEventListener("beforeunload", () => {
      Object.keys(tokens).forEach((ref) => {
        // we probably won't read the results but
        // the server still should cancel properly
        this.cancel(ref);
      });
    });
    this._status = (req) => {};
    this._active = true;
    this._infoTitle = true;
    this._beforeTitle = null;
    this._ownTitle = null;
    this._req = 0;
    this._animationIx = 0;
    this._animationInFlight = false;
    this._starts = {};
    this._tokens = {};
    this._urls = {};
  }

  set status(status) {
    this._status = status;
  }

  get status() {
    return this._status;
  }

  set active(active) {
    this._active = active;
  }

  get active() {
    return this._active;
  }

  set infoTitle(infoTitle) {
    if(!infoTitle) {
      this.setAddTitle("");
    }
    this._infoTitle = infoTitle;
  }

  get infoTitle() {
    return this._infoTitle;
  }

  setAddTitle(addTitle) {
    if(!this._infoTitle) return;
    const curTitle = document.title;
    if(this._ownTitle && curTitle !== this._ownTitle) { // external change
      this._beforeTitle = curTitle;
    }
    if(!this._beforeTitle) {
      this._beforeTitle = curTitle;
    }
    this._ownTitle = this._beforeTitle + addTitle;
    document.title = this._ownTitle;
  } // setAddTitle

  sendRequest(url, obj, cb) {
    fetch(url, {
      "method": "POST",
      "headers": new Headers({
        "Content-Type": "application/json",
        "Content-Length": "" + obj.length,
      }),
      "body": obj,
    }).then((data) => {
      if(data.status !== 200 || !data.ok) {
        console.warn("response not okay", data);
        throw new Error("response not okay");
      }
      const ct = data.headers.get("content-type");
      if(ct && ct.includes("application/json")) {
        return data.json();
      }
      throw new TypeError("response not JSON encoded");
    }).then((data) => cb(null, data)).catch((e) => cb(e, null));
  }

  changeStatus(inc, error) {
    if(this._req < 0) return;
    if(error) {
      this._req = -1;
    } else if(inc) {
      this._req += 1;
    } else {
      this._req -= 1;
    }
    this.titleStatus();
    this._status(this._req);
  } // changeStatus

  titleStatus() {
    if(this._req <= 0) {
      this.setAddTitle("");
      return;
    }
    const anim = CONFIG.animation;
    let txt = " " + anim[this._animationIx % anim.length];
    if(this._req > 1) {
      txt += ` (${this._req}x)`;
    }
    this.setAddTitle(txt);
    if(this._infoTitle && !this._animationInFlight) {
      this._animationIx = (this._animationIx + 1) % anim.length;
      this._animationInFlight = true;
      setTimeout(() => {
        this._animationInFlight = false;
        this.titleStatus();
      }, CONFIG.animationTime);
    }
  } // titleStatus

  get_payload(data, url, cb) {
    if(!data["continue"]) {
      cb(JSON.parse(data["result"]));
      return;
    }
    const keys = data["result"];
    const res = {};
    keys.forEach((k) => {
      const obj = JSON.stringify({
        "action": "cargo",
        "token": k,
      });
      this.sendRequest(url, obj, (err, data) => {
        if(err) {
          console.warn(`Failed to retrieve cargo ${k}`);
          this.changeStatus(false, true);
          return console.warn(err);
        }
        if(k !== data["token"]) {
          console.warn(`Mismatching token ${k} !== ${data["token"]}`);
          this.changeStatus(false, true);
          return;
        }
        res[k] = data["result"];
        check_finished();
      });
    });

    const check_finished = () => {
      if(!keys.every((k) => k in res)) {
        return;
      }
      const d = keys.map((k) => res[k]).join(''); // string may be too long :(
      keys.forEach((k) => { // free memory
        res[k] = null;
      });
      cb(JSON.parse(d));
    } // check_finished
  } // get_payload

  postTask(ref) {
    setTimeout(() => {
      if(!this._starts[ref]) return;
      const s = this._starts[ref];
      const url = s["url"];
      const cb = s["cb"];
      this._starts[ref] = null;
      this.changeStatus(true, false);
      const obj = JSON.stringify({
        "action": "start",
        "payload": s["payload"],
      });
      this.sendRequest(url, obj, (err, data) => {
        if(err) {
          console.warn(`Failed to start ${ref}`);
          this.changeStatus(false, true);
          return console.warn(err);
        }
        this._cancel(ref, (err) => {
          if(err) {
            console.warn(`Failed to cancel ${ref}`);
            this.changeStatus(false, true);
            return console.warn(err);
          }
        });
        if(data["done"]) {
          this.get_payload(data, url, (d) => {
            this.execute(cb, d);
          });
        } else {
          const token = +data["token"];
          this._urls[ref] = url;
          this._tokens[ref] = token;
          this.monitor(ref, token, cb, CONFIG.timeStart);
        }
      });
    }, CONFIG.preDelay);
  } // postTask

  monitor(ref, token, cb, delay) {
    if(this._tokens[ref] !== token) {
      this.changeStatus(false, false);
      return;
    }
    const url = this._urls[ref];
    const obj = JSON.stringify({
      "action": "get",
      "token": token,
    });
    this.sendRequest(url, obj, (err, data) => {
      if(err) {
        console.warn(`Error while retrieving ${ref} token: ${token}`);
        this.changeStatus(false, true);
        return console.warn(err);
      }
      const cur_token = +data["token"];
      if(cur_token !== this._tokens[ref]) {
        // late response
        this.changeStatus(false, false);
        return;
      }
      if(cur_token !== token) {
        // wrong response
        console.warn(`Error while retrieving ${ref}`);
        this.changeStatus(false, true);
        return console.warn({
          "err": `token mismatch: ${cur_token} instead of ${token}`,
        });
      }
      if(data["done"]) {
        this._tokens[ref] = -1;
        this._urls[ref] = null;
        this.get_payload(data, url, (d) => {
          this.execute(cb, d);
        });
      } else if(data["continue"]) {
        setTimeout(() => {
          const newDelay = Math.min(
            Math.max(
              delay * CONFIG.timeMulInc, delay + CONFIG.timeMinInc
            ), CONFIG.timeCap
          );
          this.monitor(ref, token, cb, newDelay);
        }, delay);
      } else {
        this.changeStatus(false, false);
      }
    });
  } // monitor

  _cancel(ref, cb) {
    if(!(ref in this._tokens && this._tokens[ref] >= 0)) return;
    const token = this._tokens[ref];
    const url = this._urls[ref];
    const obj = JSON.stringify({
      "action": "stop",
      "token": token,
    });
    this._tokens[ref] = -1;
    this._urls[ref] = null;
    this.sendRequest(url, obj, (err, data) => {
      if(err) {
        return cb(err);
      }
      return cb(+data["token"] !== token && {
        "err": `token mismatch: ${data["token"]} instead of ${token}`,
      });
    });
  } // _cancel

  execute(cb, data) {
    let err = true;
    try {
      cb(data);
      err = false;
    } finally {
      if(err) {
        this.changeStatus(false, true);
      } else {
        this.changeStatus(false, false);
      }
    }
  } // execute

  post(ref, url, payload, cb) {
    if(!this._active) return;
    this._starts[ref] = {
      "url": url,
      "cb": cb,
      "payload": payload,
    };
    this.postTask(ref);
  } // post

  cancel(ref) {
    this._cancel(ref, (err) => {
      this.changeStatus(false, !!err);
      if(err) {
        console.warn(`Failed to cancel ${ref}`);
        return console.warn(err);
      }
    });
  } // cancel
} // Worker

export class VariablePool {
  constructor(worker, objectPath) {
    this._worker = worker;
    this._objectPath = objectPath;
    this._curQuery = {};
    this._variables = {};
    this._count = 0;
    this._timer = null;
  }

  executeQuery() {
    const query = this._curQuery;
    if(Object.keys(query).length === 0) return;
    const count = this._count;
    this._curQuery = {};
    this._count += 1;
    this._worker.post(`query${count}`, this._objectPath, {
      "query": query,
    }, (data) => {
      this._applyValues(this._variables, data, []);
    });
  }

  _queueQuery(chg) {
    if(!chg) return;
    if(this._timer !== null) return;
    this._timer = setTimeout(() => {
      this.executeQuery();
      this._timer = null;
    }, 1);
  }

  _applyValues(vars, data, path) {
    Object.keys(data).forEach((k) => {
      const d = data[k];
      if("map" in d) {
        if(!(k in vars)) {
          vars[k] = new WeakMap();
        }
        Object.keys(d.map).forEach((innerK) => {
          if(!(innerK in vars[k])) {
            vars[k][innerK] = {};
          }
          this._applyValues(vars[k][innerK], d.map[innerK], path + [k, innerK]);
        });
      } else if("value" in d) {
        vars[k] = Object.seal(new Variable(this, path + [k], true, d.value));
      } else {
        console.warn("incorrect response", d);
        throw new Error("internal error")
      }
    });
  }

  _addAction(qobj, path, type, value) {
    if(path.length >= 2) {
      const key = path[0];
      if(!(key in qobj)) {
        qobj[key] = {
          "type": "get",
          "queries": {},
        };
      } else if(!("queries" in qobj[key])) {
        qobj[key]["queries"] = {};
      }
      const kname = path[1];
      if(!(kname in qobj[key]["queries"])) {
        qobj[key]["queries"][kname] = {};
      }
      return this._addAction(
        qobj[key]["queries"][kname], path.slice(2), type, value);
    }
    const name = path[0];
    if(!(name in qobj)) {
      qobj[name] = {};
    } else {
      if(qobj[name]["type"] === "set" && type !== "set") {
        return false;
      }
    }
    if(type !== "get" && type !== "set") {
      throw new Error(`invalid type: ${type}`);
    }
    qobj[name]["type"] = type;
    if(type === "set") {
      qobj[name]["value"] = value;
    }
    return true;
  }

  addGetAction(path) {
    const chg = this._addAction(this._curQuery, path, "get", undefined);
    this._queueQuery(chg);
  }

  addSetAction(path, value) {
    const chg = this._addAction(this._curQuery, path, "set", value);
    this._queueQuery(chg);
  }

  getValue(path) {
    const full = path.slice();
    let vars = this._variables;
    while(path.length > 1) {
      const mapName = path.shift();
      const key = path.shift();
      if(!(mapName in vars)) {
        vars[mapName] = new WeakMap();
      }
      if(!(key in vars[mapName][key])) {
        vars[mapName][key] = {};
      }
      vars = vars[mapName][key];
    }
    if(!path.length) {
      throw new Error(`expected longer path: ${full}`);
    }
    const name = path.shift();
    if(!(name in vars)) {
      vars[name] = Object.seal(new Variable(this, full, false, null));
    }
    return vars[name];
  }
} // VariablePool

class Variable {
  constructor(pool, path, sync, value) {
    this._pool = pool;
    this._path = path;
    this._sync = sync;
    this._value = value;
  }

  update() {
    this._pool.addGetAction(this._path);
  }

  has() {
    if(!this._sync) {
      this.update();
    }
    return this._sync;
  }

  get value() {
    if(!this.has()) {
      throw new Error("variable not in sync");
    }
    return this._value;
  }

  set value(val) {
    this._pool.addSetAction(this._path, val);
  }
} // Variable

// *** FIXME Experimental React API for direct object access. ***
export let IOProvider = null;
export let withIO = null;
try {
  import('react').then((React) => {
    const { Provider, Consumer } = React.createContext(undefined);

    class IOProviderRaw extends React.PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          pool: null,
        };
      }

      componentWillMount() {
        this.propsToState({}, this.props);
      }

      componentWillReceiveProps(nextProps) {
        this.propsToState(this.props, nextProps);
      }

      propsToState(props, nextProps) {
        const { objectPath="/objects/" } = nextProps;
        if(props.objectPath === objectPath) return;
        this.setState({
          pool: new VariablePool(new Worker(), objectPath),
        });
      }

      render() {
        const { children } = this.props;
        const { pool } = this.state;
        if(!pool) return null;
        return React.createElement(Provider, {
          value: pool,
        }, children);
      }
    } // IOProviderRaw
    IOProvider = IOProviderRaw;

    const withIORaw = (keys, component) => {
      return React.createElement(Consumer, {}, (pool) => {
        return function IOComponent(props) {
          const newProps = Object.assign({}, props);
          keys(props).forEach((k) => {
            newProps[k] = pool.getValue([k]);
          });
          return React.createElement(component, newProps, props.children);
        };
      });
    };
    withIO = withIORaw;
  });
} catch(e) {
  console.warn("react not found", e);
}
<commit_msg>fix minor error<commit_after>/**
 * !!! This file can be found as worker.js in the quick_server folder !!!
 *
 * Provides worker oriented networking interface.
 *
 * Created by krause on 2016-06-22.
 */

export const CONFIG = {
  preDelay: 500,
  timeStart: 500,
  // has to be below 2min so the server doesn't remove the result
  timeCap: 1000*60,
  timeMinInc: 10,
  timeMulInc: 1.01,
  animation: ["⠋", "⠙", "⠸", "⠴", "⠦", "⠇"],
  // animation: ["/", "-", "\\", "|"],
  animationTime: 300,
};
export const VERSION = "0.5.0";

export class Worker {
  constructor() {
    window.addEventListener("beforeunload", () => {
      Object.keys(this._tokens).forEach((ref) => {
        // we probably won't read the results but
        // the server still should cancel properly
        this.cancel(ref);
      });
    });
    this._status = (req) => {};
    this._active = true;
    this._infoTitle = true;
    this._beforeTitle = null;
    this._ownTitle = null;
    this._req = 0;
    this._animationIx = 0;
    this._animationInFlight = false;
    this._starts = {};
    this._tokens = {};
    this._urls = {};
  }

  set status(status) {
    this._status = status;
  }

  get status() {
    return this._status;
  }

  set active(active) {
    this._active = active;
  }

  get active() {
    return this._active;
  }

  set infoTitle(infoTitle) {
    if(!infoTitle) {
      this.setAddTitle("");
    }
    this._infoTitle = infoTitle;
  }

  get infoTitle() {
    return this._infoTitle;
  }

  setAddTitle(addTitle) {
    if(!this._infoTitle) return;
    const curTitle = document.title;
    if(this._ownTitle && curTitle !== this._ownTitle) { // external change
      this._beforeTitle = curTitle;
    }
    if(!this._beforeTitle) {
      this._beforeTitle = curTitle;
    }
    this._ownTitle = this._beforeTitle + addTitle;
    document.title = this._ownTitle;
  } // setAddTitle

  sendRequest(url, obj, cb) {
    fetch(url, {
      "method": "POST",
      "headers": new Headers({
        "Content-Type": "application/json",
        "Content-Length": "" + obj.length,
      }),
      "body": obj,
    }).then((data) => {
      if(data.status !== 200 || !data.ok) {
        console.warn("response not okay", data);
        throw new Error("response not okay");
      }
      const ct = data.headers.get("content-type");
      if(ct && ct.includes("application/json")) {
        return data.json();
      }
      throw new TypeError("response not JSON encoded");
    }).then((data) => cb(null, data)).catch((e) => cb(e, null));
  }

  changeStatus(inc, error) {
    if(this._req < 0) return;
    if(error) {
      this._req = -1;
    } else if(inc) {
      this._req += 1;
    } else {
      this._req -= 1;
    }
    this.titleStatus();
    this._status(this._req);
  } // changeStatus

  titleStatus() {
    if(this._req <= 0) {
      this.setAddTitle("");
      return;
    }
    const anim = CONFIG.animation;
    let txt = " " + anim[this._animationIx % anim.length];
    if(this._req > 1) {
      txt += ` (${this._req}x)`;
    }
    this.setAddTitle(txt);
    if(this._infoTitle && !this._animationInFlight) {
      this._animationIx = (this._animationIx + 1) % anim.length;
      this._animationInFlight = true;
      setTimeout(() => {
        this._animationInFlight = false;
        this.titleStatus();
      }, CONFIG.animationTime);
    }
  } // titleStatus

  get_payload(data, url, cb) {
    if(!data["continue"]) {
      cb(JSON.parse(data["result"]));
      return;
    }
    const keys = data["result"];
    const res = {};
    keys.forEach((k) => {
      const obj = JSON.stringify({
        "action": "cargo",
        "token": k,
      });
      this.sendRequest(url, obj, (err, data) => {
        if(err) {
          console.warn(`Failed to retrieve cargo ${k}`);
          this.changeStatus(false, true);
          return console.warn(err);
        }
        if(k !== data["token"]) {
          console.warn(`Mismatching token ${k} !== ${data["token"]}`);
          this.changeStatus(false, true);
          return;
        }
        res[k] = data["result"];
        check_finished();
      });
    });

    const check_finished = () => {
      if(!keys.every((k) => k in res)) {
        return;
      }
      const d = keys.map((k) => res[k]).join(''); // string may be too long :(
      keys.forEach((k) => { // free memory
        res[k] = null;
      });
      cb(JSON.parse(d));
    } // check_finished
  } // get_payload

  postTask(ref) {
    setTimeout(() => {
      if(!this._starts[ref]) return;
      const s = this._starts[ref];
      const url = s["url"];
      const cb = s["cb"];
      this._starts[ref] = null;
      this.changeStatus(true, false);
      const obj = JSON.stringify({
        "action": "start",
        "payload": s["payload"],
      });
      this.sendRequest(url, obj, (err, data) => {
        if(err) {
          console.warn(`Failed to start ${ref}`);
          this.changeStatus(false, true);
          return console.warn(err);
        }
        this._cancel(ref, (err) => {
          if(err) {
            console.warn(`Failed to cancel ${ref}`);
            this.changeStatus(false, true);
            return console.warn(err);
          }
        });
        if(data["done"]) {
          this.get_payload(data, url, (d) => {
            this.execute(cb, d);
          });
        } else {
          const token = +data["token"];
          this._urls[ref] = url;
          this._tokens[ref] = token;
          this.monitor(ref, token, cb, CONFIG.timeStart);
        }
      });
    }, CONFIG.preDelay);
  } // postTask

  monitor(ref, token, cb, delay) {
    if(this._tokens[ref] !== token) {
      this.changeStatus(false, false);
      return;
    }
    const url = this._urls[ref];
    const obj = JSON.stringify({
      "action": "get",
      "token": token,
    });
    this.sendRequest(url, obj, (err, data) => {
      if(err) {
        console.warn(`Error while retrieving ${ref} token: ${token}`);
        this.changeStatus(false, true);
        return console.warn(err);
      }
      const cur_token = +data["token"];
      if(cur_token !== this._tokens[ref]) {
        // late response
        this.changeStatus(false, false);
        return;
      }
      if(cur_token !== token) {
        // wrong response
        console.warn(`Error while retrieving ${ref}`);
        this.changeStatus(false, true);
        return console.warn({
          "err": `token mismatch: ${cur_token} instead of ${token}`,
        });
      }
      if(data["done"]) {
        this._tokens[ref] = -1;
        this._urls[ref] = null;
        this.get_payload(data, url, (d) => {
          this.execute(cb, d);
        });
      } else if(data["continue"]) {
        setTimeout(() => {
          const newDelay = Math.min(
            Math.max(
              delay * CONFIG.timeMulInc, delay + CONFIG.timeMinInc
            ), CONFIG.timeCap
          );
          this.monitor(ref, token, cb, newDelay);
        }, delay);
      } else {
        this.changeStatus(false, false);
      }
    });
  } // monitor

  _cancel(ref, cb) {
    if(!(ref in this._tokens && this._tokens[ref] >= 0)) return;
    const token = this._tokens[ref];
    const url = this._urls[ref];
    const obj = JSON.stringify({
      "action": "stop",
      "token": token,
    });
    this._tokens[ref] = -1;
    this._urls[ref] = null;
    this.sendRequest(url, obj, (err, data) => {
      if(err) {
        return cb(err);
      }
      return cb(+data["token"] !== token && {
        "err": `token mismatch: ${data["token"]} instead of ${token}`,
      });
    });
  } // _cancel

  execute(cb, data) {
    let err = true;
    try {
      cb(data);
      err = false;
    } finally {
      if(err) {
        this.changeStatus(false, true);
      } else {
        this.changeStatus(false, false);
      }
    }
  } // execute

  post(ref, url, payload, cb) {
    if(!this._active) return;
    this._starts[ref] = {
      "url": url,
      "cb": cb,
      "payload": payload,
    };
    this.postTask(ref);
  } // post

  cancel(ref) {
    this._cancel(ref, (err) => {
      this.changeStatus(false, !!err);
      if(err) {
        console.warn(`Failed to cancel ${ref}`);
        return console.warn(err);
      }
    });
  } // cancel
} // Worker

export class VariablePool {
  constructor(worker, objectPath) {
    this._worker = worker;
    this._objectPath = objectPath;
    this._curQuery = {};
    this._variables = {};
    this._count = 0;
    this._timer = null;
  }

  executeQuery() {
    const query = this._curQuery;
    if(Object.keys(query).length === 0) return;
    const count = this._count;
    this._curQuery = {};
    this._count += 1;
    this._worker.post(`query${count}`, this._objectPath, {
      "query": query,
    }, (data) => {
      this._applyValues(this._variables, data, []);
    });
  }

  _queueQuery(chg) {
    if(!chg) return;
    if(this._timer !== null) return;
    this._timer = setTimeout(() => {
      this.executeQuery();
      this._timer = null;
    }, 1);
  }

  _applyValues(vars, data, path) {
    Object.keys(data).forEach((k) => {
      const d = data[k];
      if("map" in d) {
        if(!(k in vars)) {
          vars[k] = new WeakMap();
        }
        Object.keys(d.map).forEach((innerK) => {
          if(!(innerK in vars[k])) {
            vars[k][innerK] = {};
          }
          this._applyValues(vars[k][innerK], d.map[innerK], path + [k, innerK]);
        });
      } else if("value" in d) {
        vars[k] = Object.seal(new Variable(this, path + [k], true, d.value));
      } else {
        console.warn("incorrect response", d);
        throw new Error("internal error")
      }
    });
  }

  _addAction(qobj, path, type, value) {
    if(path.length >= 2) {
      const key = path[0];
      if(!(key in qobj)) {
        qobj[key] = {
          "type": "get",
          "queries": {},
        };
      } else if(!("queries" in qobj[key])) {
        qobj[key]["queries"] = {};
      }
      const kname = path[1];
      if(!(kname in qobj[key]["queries"])) {
        qobj[key]["queries"][kname] = {};
      }
      return this._addAction(
        qobj[key]["queries"][kname], path.slice(2), type, value);
    }
    const name = path[0];
    if(!(name in qobj)) {
      qobj[name] = {};
    } else {
      if(qobj[name]["type"] === "set" && type !== "set") {
        return false;
      }
    }
    if(type !== "get" && type !== "set") {
      throw new Error(`invalid type: ${type}`);
    }
    qobj[name]["type"] = type;
    if(type === "set") {
      qobj[name]["value"] = value;
    }
    return true;
  }

  addGetAction(path) {
    const chg = this._addAction(this._curQuery, path, "get", undefined);
    this._queueQuery(chg);
  }

  addSetAction(path, value) {
    const chg = this._addAction(this._curQuery, path, "set", value);
    this._queueQuery(chg);
  }

  getValue(path) {
    const full = path.slice();
    let vars = this._variables;
    while(path.length > 1) {
      const mapName = path.shift();
      const key = path.shift();
      if(!(mapName in vars)) {
        vars[mapName] = new WeakMap();
      }
      if(!(key in vars[mapName][key])) {
        vars[mapName][key] = {};
      }
      vars = vars[mapName][key];
    }
    if(!path.length) {
      throw new Error(`expected longer path: ${full}`);
    }
    const name = path.shift();
    if(!(name in vars)) {
      vars[name] = Object.seal(new Variable(this, full, false, null));
    }
    return vars[name];
  }
} // VariablePool

class Variable {
  constructor(pool, path, sync, value) {
    this._pool = pool;
    this._path = path;
    this._sync = sync;
    this._value = value;
  }

  update() {
    this._pool.addGetAction(this._path);
  }

  has() {
    if(!this._sync) {
      this.update();
    }
    return this._sync;
  }

  get value() {
    if(!this.has()) {
      throw new Error("variable not in sync");
    }
    return this._value;
  }

  set value(val) {
    this._pool.addSetAction(this._path, val);
  }
} // Variable

// *** FIXME Experimental React API for direct object access. ***
export let IOProvider = null;
export let withIO = null;
try {
  import('react').then((React) => {
    const { Provider, Consumer } = React.createContext(undefined);

    class IOProviderRaw extends React.PureComponent {
      constructor(props) {
        super(props);
        this.state = {
          pool: null,
        };
      }

      componentWillMount() {
        this.propsToState({}, this.props);
      }

      componentWillReceiveProps(nextProps) {
        this.propsToState(this.props, nextProps);
      }

      propsToState(props, nextProps) {
        const { objectPath="/objects/" } = nextProps;
        if(props.objectPath === objectPath) return;
        this.setState({
          pool: new VariablePool(new Worker(), objectPath),
        });
      }

      render() {
        const { children } = this.props;
        const { pool } = this.state;
        if(!pool) return null;
        return React.createElement(Provider, {
          value: pool,
        }, children);
      }
    } // IOProviderRaw
    IOProvider = IOProviderRaw;

    const withIORaw = (keys, component) => {
      return React.createElement(Consumer, {}, (pool) => {
        return function IOComponent(props) {
          const newProps = Object.assign({}, props);
          keys(props).forEach((k) => {
            newProps[k] = pool.getValue([k]);
          });
          return React.createElement(component, newProps, props.children);
        };
      });
    };
    withIO = withIORaw;
  });
} catch(e) {
  console.warn("react not found", e);
}
<|endoftext|>


<commit_before>
Meteor.methods({

    updateBasicInformation: function(message) {

        var userid = Meteor.userId;
        var users = Meteor.users;

        if (Meteor.isServer) {
            logger.info("updating user information", userid);
        }

        if (!users.findOne(userid)) {
            throw new Meteor.Error('invalid-user-data', 'user does not exist', userid);

        } else {
            users.update(userid, {
                $set: {
                    birthdate: message.birthdate,
                    weight: message.weight,
                }
            });
        }
    },
    updateActivityConfiguration: function(message) {
        var userid = Meteor.userId;
        var users = Meteor.users;

        if (Meteor.isServer) {
            logger.info("updating user activity information", userid);
        }

        if (!users.findOne(userid)) {
            throw new Meteor.Error('invalid-user-data', 'user does not exist', userid);

        } else {
            users.update(userid, {
                $set: {
                    activity_configuration: {
                        activity: {
                            id: message.id,
                            settings: {
                                amb: message.settings.amb,
                                reg: message.settings.reg,
                                rand: message.settings.rand,
                            }
                        },
                    }
                }
            });
        }
    },
    setGender: function(gender){
        if (gender === 'm' || gender === 'f'){
            var userid = Meteor.userId;
            Meteor.users.update(userid, {
                $set: {
                    gender: gender,
                }
            });
        }
    },
});

if(Meteor.isServer){
    Meteor.publish("ownUserData", function () {
        return Meteor.users.find({}, {fields: {'birthdate': 1, weight: 1, gender: 1}});
    });
}
<commit_msg>fix id<commit_after>
Meteor.methods({

    updateBasicInformation: function(message) {

        var userid = Meteor.userId;
        var users = Meteor.users;

        if (Meteor.isServer) {
            logger.info("updating user information", userid);
        }

        if (!users.findOne(userid)) {
            throw new Meteor.Error('invalid-user-data', 'user does not exist', userid);

        } else {
            users.update(userid, {
                $set: {
                    birthdate: message.birthdate,
                    weight: message.weight,
                }
            });
        }
    },
    updateActivityConfiguration: function(message) {
        var userid = Meteor.userId;
        var users = Meteor.users;

        if (Meteor.isServer) {
            logger.info("updating user activity information", userid);
        }

        if (!users.findOne(userid)) {
            throw new Meteor.Error('invalid-user-data', 'user does not exist', userid);

        } else {
            users.update(userid, {
                $set: {
                    activity_configuration: {
                        activity: {
                            id: message.id._id,
                            settings: {
                                amb: message.settings.amb,
                                reg: message.settings.reg,
                                rand: message.settings.rand,
                            }
                        },
                    }
                }
            });
        }
    },
    setGender: function(gender){
        if (gender === 'm' || gender === 'f'){
            var userid = Meteor.userId;
            Meteor.users.update(userid, {
                $set: {
                    gender: gender,
                }
            });
        }
    },
});

if(Meteor.isServer){
    Meteor.publish("ownUserData", function () {
        return Meteor.users.find({}, {fields: {'birthdate': 1, weight: 1, gender: 1}});
    });
}
<|endoftext|>


<commit_before>//import _ from 'lodash';
//import { errors } from 'mobile-json-wire-protocol';
import log from '../logger';

let commands = {}, helpers = {}, extensions = {};
/*
commands.title = async function () {
  this.assertWebviewContext();
  return this.appModel.title;
};

commands.keys = async function (value) {
  if (!this.focusedElId) {
    throw new errors.InvalidElementStateError();
  }
  await this.setValue(value, this.focusedElId);
};

commands.setGeoLocation = async function (location) {
  // TODO test this adequately once WD bug is fixed
  this.appModel.lat = location.latitude;
  this.appModel.long = location.longitude;
};

commands.getGeoLocation = async function () {
  return this.appModel.currentGeoLocation;
};
*/
commands.getPageSource = function () {
  return this.bootstrap.sendAction('source');
};
/*
commands.getOrientation = async function () {
  return this.appModel.orientation;
};

commands.setOrientation = async function (o) {
  if (!_.contains(["LANDSCAPE", "PORTRAIT"], o)) {
    throw new errors.UnknownError("Orientation must be LANDSCAPE or PORTRAIT");
  }
  this.appModel.orientation = o;
};

commands.getScreenshot = async function () {
  return this.appModel.getScreenshot();
};

*/

commands.back = function () {
  return this.bootstrap.sendAction('pressBack');
};

commands.hideKeyboard = async function () {
  let {isKeyboardShown, canCloseKeyboard} = await this.adb.isSoftKeyboardPresent();
  if (!isKeyboardShown) {
    throw new Error('Soft keyboard not present, cannot hide keyboard');
  }

  if (canCloseKeyboard) {
    return this.back();
  } else {
    log.info('Keyboard has no UI; no closing necessary');
  }
};

Object.assign(extensions, commands, helpers);
export { commands, helpers };
export default extensions;
<commit_msg>currentActivity, windowSize, logTypes<commit_after>import _ from 'lodash';
//import { errors } from 'mobile-json-wire-protocol';
import log from '../logger';

let commands = {}, helpers = {}, extensions = {};

const logTypesSupported = {
  'logcat' : 'Logs for Android applications on real device and emulators ' +
             'via ADB'
};

/*
commands.title = async function () {
  this.assertWebviewContext();
  return this.appModel.title;
};

commands.keys = async function (value) {
  if (!this.focusedElId) {
    throw new errors.InvalidElementStateError();
  }
  await this.setValue(value, this.focusedElId);
};

commands.setGeoLocation = async function (location) {
  // TODO test this adequately once WD bug is fixed
  this.appModel.lat = location.latitude;
  this.appModel.long = location.longitude;
};

commands.getGeoLocation = async function () {
  return this.appModel.currentGeoLocation;
};
*/
commands.getPageSource = function () {
  return this.bootstrap.sendAction('source');
};
/*
commands.getOrientation = async function () {
  return this.appModel.orientation;
};

commands.setOrientation = async function (o) {
  if (!_.contains(["LANDSCAPE", "PORTRAIT"], o)) {
    throw new errors.UnknownError("Orientation must be LANDSCAPE or PORTRAIT");
  }
  this.appModel.orientation = o;
};

commands.getScreenshot = async function () {
  return this.appModel.getScreenshot();
};

*/

commands.back = function () {
  return this.bootstrap.sendAction('pressBack');
};

commands.hideKeyboard = async function () {
  let {isKeyboardShown, canCloseKeyboard} = await this.adb.isSoftKeyboardPresent();
  if (!isKeyboardShown) {
    throw new Error('Soft keyboard not present, cannot hide keyboard');
  }

  if (canCloseKeyboard) {
    return this.back();
  } else {
    log.info('Keyboard has no UI; no closing necessary');
  }
};

commands.getWindowSize = function () {
  return this.bootstrap.sendAction('getDeviceSize');
};

commands.getCurrentActivity = async function () {
  return (await this.adb.getFocusedPackageAndActivity()).appActivity;
};

commands.getLogTypes = function () {
  return _.keys(logTypesSupported);
};

Object.assign(extensions, commands, helpers);
export { commands, helpers };
export default extensions;
<|endoftext|>


<commit_before>// Dependencies
var Statique = global.Statique = require("statique")
  , Utils = require("./utils")
  , JxUtils = require("jxutils")
  , Config = global.Config = require("./config.js")
  , Url = require("url")
  , Http = require("http")
  , Apis = require("./apis")
  , ipaddress = Config.ipaddress
  , port = Config.port
  ;

// Dessions
global.sessions = {};

// Attach core pages
Config.gitSite.parsed.roots.pages["/login"] = {
    url: "/core/html/login.html"
  , visible: false
  , slug: "login"
};

Config.gitSite.parsed.roots.pages["/blog"] = {
    url: "/core/html/blog.html"
  , label: "Blog"
  , order: 19
  , slug: "blog"
};

Config.gitSite.parsed.roots.pages["/admin"] = {
    url: "/core/html/admin.html"
  , label: "Admin"
  , order: 21
  , loggedIn: true
  , slug: "admin"
};

// Handle uncaught exceptions
process.on("uncaughtException", function (err) {
    console.error(err);
});

// statique config
Statique
    .server({ root: Config.gitSite.paths.ROOT })
    .setRoutes(Config.gitSite.parsed.roots.pages)
  ;

// create server
Http.createServer(function(req, res) {

    var pathName = Url.parse(req.url, true).pathname;

    // normalize path name
    if (pathName.substr(-1) !== "/") {
        pathName += "/";
    }

    var route = Statique.getRoute(pathName)
      , isBlogPost = (
            new RegExp(SITE_CONFIG.blog.url + "\/[0-9]+.*")
        ).test(pathName)
      ;

    if (route && route.url || isBlogPost) {
        Apis["handlePage:" + req.method](req, res, pathName, route, null, isBlogPost);
        return;
    }

    // serve files
    Statique.serve(req, res);
}).listen(port, ipaddress);

// print some output
console.log("Server running at http://%s:%d", ipaddress || "localhost", port);
<commit_msg>More dependencies and saving them globally<commit_after>// Dependencies
var G = global
  , Statique    = G.Statique    = require("statique")
  , Utils       = G.Utils       = require("./utils")
  , Fs          = G.Fs          = require("fs");
  , Url         = G.Url         = require("url")
  , Http        = G.Http        = require("http")
  , Config      = G.Config      = require("./config.js")
  , Apis        = G.Apis        = require("./apis")
  , Marked      = G.Marked      = require("marked")
  , Moment      = G.Moment      = require("moment")
  , Mustache    = G.Mustache    = require("mustache")
  , QueryString = G.QueryString = require("querystring")
  , Mandrill    = G.Mandrill    = require('mandrill-api/mandrill')
  , Validators  = G.Validators  = require("./apis/validators")
  , Highlight   = G.Highlight   = require("highlight.js")
  ;

// Dessions
global.sessions = {};

// Attach core pages
Config.gitSite.parsed.roots.pages["/login"] = {
    url: "/core/html/login.html"
  , visible: false
  , slug: "login"
};

Config.gitSite.parsed.roots.pages["/blog"] = {
    url: "/core/html/blog.html"
  , label: "Blog"
  , order: 19
  , slug: "blog"
};

Config.gitSite.parsed.roots.pages["/admin"] = {
    url: "/core/html/admin.html"
  , label: "Admin"
  , order: 21
  , loggedIn: true
  , slug: "admin"
};

// Handle uncaught exceptions
process.on("uncaughtException", function (err) {
    console.error(err);
});

// statique config
Statique
    .server({ root: Config.gitSite.paths.ROOT })
    .setRoutes(Config.gitSite.parsed.roots.pages)
  ;

// create server
Http.createServer(function(req, res) {

    var pathName = Url.parse(req.url, true).pathname;

    // normalize path name
    if (pathName.substr(-1) !== "/") {
        pathName += "/";
    }

    var route = Statique.getRoute(pathName)
      , isBlogPost = (
            new RegExp(SITE_CONFIG.blog.url + "\/[0-9]+.*")
        ).test(pathName)
      ;

    if (route && route.url || isBlogPost) {
        Apis["handlePage:" + req.method](req, res, pathName, route, null, isBlogPost);
        return;
    }

    // serve files
    Statique.serve(req, res);
}).listen(Config.port, Config.ipaddress);

// print some output
console.log("Server running at http://%s:%d", Config.ipaddress || "localhost", Config.port);
<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});
var latestTemp;
var latestPressure;
var latestHumidity;
var ready = false;

var Datastore = require('nedb')
  , db = new Datastore({ filename: 'data.txt', autoload: true });

barometer.begin(function(err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
	setImmediate(readData);
	setInterval(readData, 60 * 1000);
});

var readData = function () {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		latestTemp = temperature.toFixed(2);
		latestPressure = (pressure / 100).toFixed(2);
		latestHumidity = humidity.toFixed(2);
		ready = true;
		db.insert({
			time: new Date(),
			ambientTemperature: latestTemp,
			pressure: latestPressure,
			humidity: latestHumidity
		});
	});
};

app.get('/', function (req, res) {
	res.write("<meta http-equiv=\"refresh\" content=\"5\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
	if (ready) {
		res.write("Temperature: " + latestTemp + " ℃
");
		res.write("Pressure: " + latestPressure + " hPa
");
		res.write("Humidity: " + latestHumidity + "%\n");
	} else {
		res.write("No data is available currently.");
	}
	res.end("");
});

app.get('/output.csv', function (req, res) {
	db.find({}, function (err, docs) {
		for (var i = 0; i < docs.length; i++) {
			var time = docs[i].time;
			var formattedDate = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
			var formattedTime = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
			var formattedDateTime = formattedDate + " " + formattedTime; // github doesn't like long lines
			res.write(formattedDateTime + ",");
			res.write(ambientTemperature + ",");
			res.write(pressure + ",");
			res.write(humidity + "\n");
		}
	});
});

app.listen(80, function () {
	console.log('Example app listening on port 80!');
});
<commit_msg>fix reference<commit_after>var express = require('express');
var app = express();
var BME280 = require('node-bme280');
var barometer = new BME280({address: 0x77});
var latestTemp;
var latestPressure;
var latestHumidity;
var ready = false;

var Datastore = require('nedb')
  , db = new Datastore({ filename: 'data.txt', autoload: true });

barometer.begin(function(err) {
    if (err) {
        console.info('error initializing barometer', err);
        return;
    }
    console.info('barometer running');
	setImmediate(readData);
	setInterval(readData, 60 * 1000);
});

var readData = function () {
	barometer.readPressureAndTemparature(function(err, pressure, temperature, humidity) {
		latestTemp = temperature.toFixed(2);
		latestPressure = (pressure / 100).toFixed(2);
		latestHumidity = humidity.toFixed(2);
		ready = true;
		db.insert({
			time: new Date(),
			ambientTemperature: latestTemp,
			pressure: latestPressure,
			humidity: latestHumidity
		});
	});
};

app.get('/', function (req, res) {
	res.write("<meta http-equiv=\"refresh\" content=\"5\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">");
	if (ready) {
		res.write("Temperature: " + latestTemp + " ℃
");
		res.write("Pressure: " + latestPressure + " hPa
");
		res.write("Humidity: " + latestHumidity + "%\n");
	} else {
		res.write("No data is available currently.");
	}
	res.end("");
});

app.get('/output.csv', function (req, res) {
	db.find({}, function (err, docs) {
		for (var i = 0; i < docs.length; i++) {
			var time = docs[i].time;
			var formattedDate = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
			var formattedTime = time.getHours() + ":" + time.getMinutes() + ":" + time.getSeconds();
			var formattedDateTime = formattedDate + " " + formattedTime; // github doesn't like long lines
			res.write(formattedDateTime + ",");
			res.write(docs[i].ambientTemperature + ",");
			res.write(docs[i].pressure + ",");
			res.write(docs[i].humidity + "\n");
		}
	});
});

app.listen(80, function () {
	console.log('Example app listening on port 80!');
});
<|endoftext|>