<commit_before>OpenSpending = "OpenSpending" in window ? OpenSpending : {};

(function ($) {

OpenSpending.Treemap = function (elem, context, state) {
  var self = this;

  var resources = [
                 OpenSpending.scriptRoot + "/widgets/treemap/js/thejit-2.min.js",
                 OpenSpending.scriptRoot + "/widgets/treemap/css/treemap.css"
                 ];

  if ($.browser.msie) {
    resources.push(OpenSpending.scriptRoot + "/widgets/treemap/js/excanvas.js");
  }

  self.context = _.extend({
    click: function(node) {
      if (node.data.link) {
        var link = context.embed ? node.data.link + '?embed=true' : node.data.link;
        document.location.href = link;
      }
    },
    hasClick: function(node) {
      return node.data.link !== undefined;
    },
    createLabel: function(widget, domElement, node) {
      if ((node.data.value/self.total)>0.03) {
        domElement.innerHTML = "

" + OpenSpending.Utils.formatAmountWithCommas(node.data.value,0,self.currency) + "

" + node.name + "

";
      }
    }
  }, context);
  this.state = state;

  this.configure = function(endConfigure) {
    self.$qb.empty();
    var qb = new OpenSpending.Widgets.QueryBuilder(
      self.$qb, self.update, endConfigure, self.context, [
            {
              variable: 'drilldown',
              label: 'Tiles:',
              type: 'select',
              single: true,
              'default': self.state.drilldown,
              help: 'The sum for each member of this dimension will be presented as a tile on the treemap.'
            },
            {
              variable: 'year',
              label: 'Year:',
              type: 'slider',
              'dimension': 'time',
              'attribute': 'year',
              'default': self.state.year,
              help: 'Filter by year.'
            },
            {
              variable: 'cuts',
              label: 'Filters:',
              type: 'cuts',
              'default': self.state.cuts,
              help: 'Limit the set of data to display.'
            }
          ]
    );
  };

  this.update = function(state) {
    self.$e.empty();
    self.state = state;
    self.state.cuts = self.state.cuts || {};

    var cuts = [];
    for (var field in self.state.cuts) {
      cuts.push(field + ':' + self.state.cuts[field]);
    }

    if (self.state.year) {
      cuts.push('time.year:' + self.state.year);
    }

    if (typeof self.context.member !== 'undefined' && typeof self.context.dimension !== 'undefined') {
      cuts.push(self.context.dimension + ':' + self.context.member);
    }

    if (self.state.drilldown) {
      self.aggregator = new OpenSpending.Aggregator({
        siteUrl: self.context.siteUrl,
        dataset: self.context.dataset,
        drilldowns: [self.state.drilldown],
        cuts: cuts,
        rootNodeLabel: 'Total',
        callback: function(data) {
          self.setDataFromAggregator(this.dataset, this.drilldowns[0], data);
          self.draw();
        }
      });
    }
  };

  this.getDownloadURL = function() {
    return self.aggregator.getCSVURL();
  };

  this.serialize = function() {
    return self.state;
  };

  this.init = function () {
    self.$e = elem;
    self.$e.before('

');
    self.$qb = elem.prev();
    self.$e.addClass("treemap-widget");
    self.update(self.state);
  };

  this.setDataFromAggregator = function (dataset, dimension, data) {
    var needsColorization = true;
    self.total = data.amount;
    console.log(data);
    self.currency = data.currency;
    self.data = {children: _.map(data.children, function(item) {
      if (item.color)
        needsColorization = false;
      return {
        children: [],
        id: item.id,
        name: item.label || item.name,
        data: {
            value: item.amount,
            $area: item.amount,
            title: item.label || item.name,
            link: item.html_url,
            name: item.name,
            $color: item.color || '#333333'
          }
        };
    })};

    if (needsColorization) {
      this.autoColorize();
    }
  };

  this.autoColorize = function() {
    var nodes = self.data.children.length;
    var colors = OpenSpending.Utils.getColorPalette(nodes);
    for (var i = 0; i < nodes; i++) {
      self.data.children[i].data.$color = colors[i];
    }
  };

  this.draw = function () {
    if (!self.data.children.length) {
      $(self.$e).hide();
      return;
    }
    $(self.$e).show();
    self.tm = new $jit.TM.Squarified({
        injectInto: self.$e.prop('id'),
        levelsToShow: 1,
        titleHeight: 0,
        animate: true,
        transition: $jit.Trans.Expo.easeOut,

        offset: 2,
        Label: {
          type: 'HTML',
          size: 12,
          family: 'Tahoma, Verdana, Arial',
          color: '#DDE7F0'
          },
        Node: {
          color: '#243448',
          CanvasStyles: {
            shadowBlur: 0,
            shadowColor: '#000'
          }
        },
        Events: {
          enable: true,
          onClick: function(node) {
            if(node) {
                self.context.click(node);
            }
          },
          onRightClick: function() {
            self.tm.out();
          },
          onMouseEnter: function(node, eventInfo) {
            if(node) {
              if (!self.context.hasClick(node)) {
                self.$e.find('#'+node.id).css('cursor', 'default');
              }
              node.setCanvasStyle('shadowBlur', 8);
              node.orig_color = node.getData('color');
              node.setData('color', '#A3B3C7');
              self.tm.fx.plotNode(node, self.tm.canvas);
            }
          },
          onMouseLeave: function(node) {
            if(node) {
              node.removeData('color');
              node.removeCanvasStyle('shadowBlur');
              node.setData('color', node.orig_color);
              self.tm.plot();
            }
          }
        },
        duration: 1000,
        Tips: {
          enable: true,
          type: 'Native',
          offsetX: 20,
          offsetY: 20,
          onShow: function(tip, node, isLeaf, domElement) {
            var html = '
' + node.name +
                ': ' + OpenSpending.Utils.formatAmount(node.data.value) +
                '

';
            var data = node.data;
            tip.innerHTML = html;
          }
        },
        //Implement this method for retrieving a requested
        //subtree that has as root a node with id = nodeId,
        //and level as depth. This method could also make a server-side
        //call for the requested subtree. When completed, the onComplete
        //callback method should be called.
        request: function(nodeId, level, onComplete){
          // var tree = eval('(' + json + ')');
          var tree = json;
          var subtree = $jit.json.getSubtree(tree, nodeId);
          $jit.json.prune(subtree, 1);
          onComplete.onComplete(nodeId, subtree);
        },
        //Add the name of the node in the corresponding label
        //This method is called once, on label creation and only for DOM labels.
        onCreateLabel: function(domElement, node){
          self.context.createLabel(self, domElement, node);
        }
    });
    self.tm.loadJSON(this.data);
    self.tm.refresh();
  };

  // The rest of this function is suitable for copypasta into other
  // plugins: load all scripts we need, and return a promise object
  // that will fire when the class is ready
  var dfd = $.Deferred();
  dfd.done(function(that) {that.init();});

  if (!window.treemapWidgetLoaded) {
    yepnope({
      load: resources,
      complete: function() {
        window.treemapWidgetLoaded = true;
        dfd.resolve(self);
      }
    });
  } else {
    dfd.resolve(self);
  }

  return dfd.promise();
};

})(jQuery);

<commit_msg>Enable multiple drilldowns in treemap <commit_after>OpenSpending = "OpenSpending" in window ? OpenSpending : {};

(function ($) {

OpenSpending.Treemap = function (elem, context, state) {
  var self = this;

  var resources = [
                 OpenSpending.scriptRoot + "/widgets/treemap/js/thejit-2.min.js",
                 OpenSpending.scriptRoot + "/widgets/treemap/css/treemap.css"
                 ];

  if ($.browser.msie) {
    resources.push(OpenSpending.scriptRoot + "/widgets/treemap/js/excanvas.js");
  }

  self.context = _.extend({
    click: function(node) {
      if (node.data.link) {
        var link = context.embed ? node.data.link + '?embed=true' : node.data.link;
        document.location.href = link;
      }
    },
    hasClick: function(node) {
      return node.data.link !== undefined;
    },
    createLabel: function(widget, domElement, node) {
      if ((node.data.value/self.total)>0.03) {
        domElement.innerHTML = "

" + OpenSpending.Utils.formatAmountWithCommas(node.data.value,0,self.currency) + "

" + node.name + "

";
      }
    }
  }, context);
  this.state = state;

  this.configure = function(endConfigure) {
    self.$qb.empty();
    var qb = new OpenSpending.Widgets.QueryBuilder(
      self.$qb, self.update, endConfigure, self.context, [
            {
              variable: 'drilldowns',
              label: 'Tiles:',
              type: 'select',
              'default': self.state.drilldowns,
              help: 'Each selected dimension will display as an additional level of tiles for the treemap.'
            },
            {
              variable: 'year',
              label: 'Year:',
              type: 'slider',
              'dimension': 'time',
              'attribute': 'year',
              'default': self.state.year,
              help: 'Filter by year.'
            },
            {
              variable: 'cuts',
              label: 'Filters:',
              type: 'cuts',
              'default': self.state.cuts,
              help: 'Limit the set of data to display.'
            }
          ]
    );
  };

  this.update = function(state) {
    self.state = state;
    self.state.drilldowns = self.state.drilldowns || [self.state.drilldown];
    self.state.cuts = self.state.cuts || {};

    var cuts = [];
    for (var field in self.state.cuts) {
      cuts.push(field + ':' + self.state.cuts[field]);
    }

    if (self.state.year) {
      cuts.push('time.year:' + self.state.year);
    }

    if (typeof self.context.member !== 'undefined' && typeof self.context.dimension !== 'undefined') {
      cuts.push(self.context.dimension + ':' + self.context.member);
    }

    //console.log(self.state);
    if (self.state.drilldowns) {
      self.aggregator = new OpenSpending.Aggregator({
        siteUrl: self.context.siteUrl,
        dataset: self.context.dataset,
        drilldowns: self.state.drilldowns,
        cuts: cuts,
        rootNodeLabel: 'Total',
        callback: function(data) {
          self.setDataFromAggregator(this.dataset, data);
        }
      });
    }
  };

  this.getDownloadURL = function() {
    return self.aggregator.getCSVURL();
  };

  this.serialize = function() {
    return self.state;
  };

  this.init = function () {
    self.$e = elem;
    self.$e.before('

');
    self.$qb = elem.prev();
    self.$e.addClass("treemap-widget");
    self.update(self.state);
  };

  this.setDataFromAggregator = function (dataset, data) {
    console.log(data);
    self.currency = data.currency;
    self.level = 0;
    self.setNode(data);
  };

  this.setNode = function (node) {
    var needsColorization = true;
    self.total = node.amount;
    self.data = {children: _.map(node.children, function(item) {
      if (item.color)
        needsColorization = false;
      return {
        children: [],
        id: item.id,
        name: item.label || item.name,
        data: {
            node: item,
            value: item.amount,
            $area: item.amount,
            title: item.label || item.name,
            link: item.html_url,
            name: item.name,
            $color: item.color || '#333333'
          }
        };
    })};

    if (needsColorization) {
      this.autoColorize();
    }
    self.draw();

  };

  this.drilldown = function(tile) {
    if (!tile.data.node.children.length) {
      self.context.click(tile);
    } else {
      self.setNode(tile.data.node);
    }
  };

  this.autoColorize = function() {
    var nodes = self.data.children.length;
    var colors = OpenSpending.Utils.getColorPalette(nodes);
    for (var i = 0; i < nodes; i++) {
      self.data.children[i].data.$color = colors[i];
    }
  };

  this.draw = function () {
    self.$e.empty();
    if (!self.data.children.length) {
      $(self.$e).hide();
      return;
    }
    $(self.$e).show();
    self.tm = new $jit.TM.Squarified({
        injectInto: self.$e.prop('id'),
        levelsToShow: 1,
        titleHeight: 0,
        animate: true,
        transition: $jit.Trans.Expo.easeOut,

        offset: 2,
        Label: {
          type: 'HTML',
          size: 12,
          family: 'Tahoma, Verdana, Arial',
          color: '#DDE7F0'
          },
        Node: {
          color: '#243448',
          CanvasStyles: {
            shadowBlur: 0,
            shadowColor: '#000'
          }
        },
        Events: {
          enable: true,
          onClick: function(node) {
            if(node) {
              self.drilldown(node);
            }
          },
          onRightClick: function() {
            self.tm.out();
          },
          onMouseEnter: function(node, eventInfo) {
            if(node) {
              if (!self.context.hasClick(node)) {
                self.$e.find('#'+node.id).css('cursor', 'default');
              }
              node.setCanvasStyle('shadowBlur', 8);
              node.orig_color = node.getData('color');
              node.setData('color', '#A3B3C7');
              self.tm.fx.plotNode(node, self.tm.canvas);
            }
          },
          onMouseLeave: function(node) {
            if(node) {
              node.removeData('color');
              node.removeCanvasStyle('shadowBlur');
              node.setData('color', node.orig_color);
              self.tm.plot();
            }
          }
        },
        duration: 1000,
        Tips: {
          enable: true,
          type: 'Native',
          offsetX: 20,
          offsetY: 20,
          onShow: function(tip, node, isLeaf, domElement) {
            var html = '
' + node.name +
                ': ' + OpenSpending.Utils.formatAmount(node.data.value) +
                '

';
            var data = node.data;
            tip.innerHTML = html;
          }
        },
        //Implement this method for retrieving a requested
        //subtree that has as root a node with id = nodeId,
        //and level as depth. This method could also make a server-side
        //call for the requested subtree. When completed, the onComplete
        //callback method should be called.
        request: function(nodeId, level, onComplete){
          // var tree = eval('(' + json + ')');
          var tree = json;
          var subtree = $jit.json.getSubtree(tree, nodeId);
          $jit.json.prune(subtree, 1);
          onComplete.onComplete(nodeId, subtree);
        },
        //Add the name of the node in the corresponding label
        //This method is called once, on label creation and only for DOM labels.
        onCreateLabel: function(domElement, node){
          self.context.createLabel(self, domElement, node);
        }
    });
    self.tm.loadJSON(this.data);
    self.tm.refresh();
  };

  // The rest of this function is suitable for copypasta into other
  // plugins: load all scripts we need, and return a promise object
  // that will fire when the class is ready
  var dfd = $.Deferred();
  dfd.done(function(that) {that.init();});

  if (!window.treemapWidgetLoaded) {
    yepnope({
      load: resources,
      complete: function() {
        window.treemapWidgetLoaded = true;
        dfd.resolve(self);
      }
    });
  } else {
    dfd.resolve(self);
  }

  return dfd.promise();
};

})(jQuery);

<|endoftext|>


<commit_before>
var manualSendResults = true;
var order = Math.round(Math.random());
//alert(order);
//var shuffleSequence = [mySeqNC,mySeqCN][order];
var shuffleSequence = seq("endSurvey");

var defaults = [
  "Separator", {
    transfer: "keypress",
    normalMessage: "Please wait for the next statement.",
    errorMessage: "Wrong. Please wait for the next statement."
  },
  "AcceptabilityJudgment", {countsForProgressBar: false,
    as: ["1", "2", "3", "4", "5", "6", "7"],
    presentAsScale: true,
    instructions: "Use number keys or click boxes to answer.",
    leftComment: "(bad)", rightComment: "(good)",
	countsForProgressBar: true
  },
  "Question", {
    as: ["yes","no"],
    randomOrder: false,
	presentHorizontally: true,
	autoFirstChar: true
  },
   "PracticeQuestion", {
    as: ["yes","no"],
    html: "
Practice
",
    randomOrder: false,
	presentHorizontally: true,
	autoFirstChar: true,
	hideProgressBar: true,
	countsForProgressBar: false
  },
  "Message", {
    hideProgressBar: true
  },
  "Form", {
    hideProgressBar: true,
    continueOnReturn: true
  }

];

var code = Math.floor(Math.random()*1000000);
var completionMessage ='
Thank you for your participation!

This study was designed to assess how quickly and accurately human users respond to statements in plain English (e.g. "John sees a letter") versus a restricted computer-readable form of English (e.g. "The person John sees the letter l1"). Your responses will help us design computer-readable languages that can still be quickly and accurately understood by human users.

Your code is:
'+code.toString()+'
Be sure to enter this code on the Amazon Mechanical Turk site so that you can be rewarded for completing this HIT.
';
var completionErrorMessage = "The transmission of the results failed!  Please contact erin.g.zaroukian.ctr@mail.mil with your participation code: " + code.toString();

function uniqueMD5() {
    // Time zone.
    var s = "" + new Date().getTimezoneOffset() + ':';

    // Plugins.
    var plugins = [
        "Java",
        "QuickTime",
        "DevalVR",
        "Shockwave",
        "Flash",
        "Windows Media Player",
        "Silverlight",
        "VLC Player"
    ];
    for (var i = 0; i < plugins.length; ++i) {
        var v = PluginDetect.getVersion(plugins[i]);
        if (v) s += plugins[i] + ':' + v;
    }

    // Whether or not cookies are turned on.
    createCookie("TEST", "TEST", 0.01); // Keep it for 0.01 days.
    if (readCookie("TEST") == "TEST")
        s += "C";

    // Screen dimensions and color depth.
    var width = screen.width ? screen.width : 1;
    var height = screen.height ? screen.height : 1;
    var colorDepth = screen.colorDepth ? screen.colorDepth : 1;
    s += width + ':' + height + ':' + colorDepth;

    return b64_md5(s);
}

var items = [
	["intro","Form", {html: {include: "survey.html"}}
	],
	["intro","Message", {html: {include: "intro.html"}}
	],
	["toPractice","Message", {html: {include: "intro2.html"}}
	],
	["endPractice","Message", {html: {include: "introEnd.html"}}
	],
	["toSUS","Message", {html: "
You will now be asked a series of questions about the sentence-diagram system you just used.
"}
	],
	["endSurvey","Form", {html: {include: "endSurvey.html"}}
	],
	["endSurvey", "Question", {
		q: code.toString(),
		as: ['done','done'],
		timeout: 1
		}
	],
	["endSurvey", "__SendResults__", { }],
	["endSurvey","Message", {html: { '
Thank you for your participation!

This study was designed to assess how quickly and accurately human users respond to statements in plain English (e.g. "John sees a letter") versus a restricted computer-readable form of English (e.g. "The person John sees the letter l1"). Your responses will help us design computer-readable languages that can still be quickly and accurately understood by human users.

Your code is:
'+code.toString()+'
Be sure to enter this code on the Amazon Mechanical Turk site so that you can be rewarded for completing this HIT.
';},
							transfer: null }
	],

	["break","Message", {html: "
You are now halfway through the experiment.

Feel free to take a break.

The second half will the much like the first half, but the sentences you read will be in a different form.
"}
	],
	["conf", "AcceptabilityJudgment", {
		s: "",
		q: "How confident are you in the response you just gave?",
		leftComment: "not confident at all?", rightComment: "very confident?",
		countsForProgressBar: true
	}],
	["confPractice", "AcceptabilityJudgment", {
		hideProgressBar: true,
		s: "",
		q: "How confident are you in the response you just gave?",
		leftComment: "not confident at all?", rightComment: "very confident?"
	}],
	["sep", "Message", {
		html: "
Remember to respond to the diagrams as quickly and as accurately as possible.
",
		countsForProgressBar: false,
		hideProgressBar: false
	}],
	["sepPractice", "ClickSeparator", {
		html: "
Remember to respond to the diagrams as quickly and as accurately as possible.
",
		normalMessage: "
Correct!
",
		errorMessage: "
Incorrect!
 ",
		hideProgressBar: true,
		countsForProgressBar: false
	}]

];

items = items.concat(newTestItems);
items = items.concat(newSUSItems);

//alert(items);
<commit_msg>Test end<commit_after>
var manualSendResults = true;
var order = Math.round(Math.random());
//alert(order);
//var shuffleSequence = [mySeqNC,mySeqCN][order];
var shuffleSequence = seq("endSurvey");

var defaults = [
  "Separator", {
    transfer: "keypress",
    normalMessage: "Please wait for the next statement.",
    errorMessage: "Wrong. Please wait for the next statement."
  },
  "AcceptabilityJudgment", {countsForProgressBar: false,
    as: ["1", "2", "3", "4", "5", "6", "7"],
    presentAsScale: true,
    instructions: "Use number keys or click boxes to answer.",
    leftComment: "(bad)", rightComment: "(good)",
	countsForProgressBar: true
  },
  "Question", {
    as: ["yes","no"],
    randomOrder: false,
	presentHorizontally: true,
	autoFirstChar: true
  },
   "PracticeQuestion", {
    as: ["yes","no"],
    html: "
Practice
",
    randomOrder: false,
	presentHorizontally: true,
	autoFirstChar: true,
	hideProgressBar: true,
	countsForProgressBar: false
  },
  "Message", {
    hideProgressBar: true
  },
  "Form", {
    hideProgressBar: true,
    continueOnReturn: true
  }

];

var code = Math.floor(Math.random()*1000000);
var completionMessage ='
Thank you for your participation!

This study was designed to assess how quickly and accurately human users respond to statements in plain English (e.g. "John sees a letter") versus a restricted computer-readable form of English (e.g. "The person John sees the letter l1"). Your responses will help us design computer-readable languages that can still be quickly and accurately understood by human users.

Your code is:
'+code.toString()+'
Be sure to enter this code on the Amazon Mechanical Turk site so that you can be rewarded for completing this HIT.
';
var completionErrorMessage = "The transmission of the results failed!  Please contact erin.g.zaroukian.ctr@mail.mil with your participation code: " + code.toString();

function uniqueMD5() {
    // Time zone.
    var s = "" + new Date().getTimezoneOffset() + ':';

    // Plugins.
    var plugins = [
        "Java",
        "QuickTime",
        "DevalVR",
        "Shockwave",
        "Flash",
        "Windows Media Player",
        "Silverlight",
        "VLC Player"
    ];
    for (var i = 0; i < plugins.length; ++i) {
        var v = PluginDetect.getVersion(plugins[i]);
        if (v) s += plugins[i] + ':' + v;
    }

    // Whether or not cookies are turned on.
    createCookie("TEST", "TEST", 0.01); // Keep it for 0.01 days.
    if (readCookie("TEST") == "TEST")
        s += "C";

    // Screen dimensions and color depth.
    var width = screen.width ? screen.width : 1;
    var height = screen.height ? screen.height : 1;
    var colorDepth = screen.colorDepth ? screen.colorDepth : 1;
    s += width + ':' + height + ':' + colorDepth;

    return b64_md5(s);
}

var items = [
	["intro","Form", {html: {include: "survey.html"}}
	],
	["intro","Message", {html: {include: "intro.html"}}
	],
	["toPractice","Message", {html: {include: "intro2.html"}}
	],
	["endPractice","Message", {html: {include: "introEnd.html"}}
	],
	["toSUS","Message", {html: "
You will now be asked a series of questions about the sentence-diagram system you just used.
"}
	],
	["endSurvey","Form", {html: {include: "endSurvey.html"}}
	],
	["endSurvey", "Question", {
		q: code.toString(),
		as: ['done','done'],
		timeout: 1
		}
	],
	["endSurvey", "__SendResults__", { }],
	["endSurvey","Message", {html: '
Thank you for your participation!

This study was designed to assess how quickly and accurately human users respond to statements in plain English (e.g. "John sees a letter") versus a restricted computer-readable form of English (e.g. "The person John sees the letter l1"). Your responses will help us design computer-readable languages that can still be quickly and accurately understood by human users.

Your code is:
'+code.toString()+'
Be sure to enter this code on the Amazon Mechanical Turk site so that you can be rewarded for completing this HIT.
',
							transfer: null }
	],

	["break","Message", {html: "
You are now halfway through the experiment.

Feel free to take a break.

The second half will the much like the first half, but the sentences you read will be in a different form.
"}
	],
	["conf", "AcceptabilityJudgment", {
		s: "",
		q: "How confident are you in the response you just gave?",
		leftComment: "not confident at all?", rightComment: "very confident?",
		countsForProgressBar: true
	}],
	["confPractice", "AcceptabilityJudgment", {
		hideProgressBar: true,
		s: "",
		q: "How confident are you in the response you just gave?",
		leftComment: "not confident at all?", rightComment: "very confident?"
	}],
	["sep", "Message", {
		html: "
Remember to respond to the diagrams as quickly and as accurately as possible.
",
		countsForProgressBar: false,
		hideProgressBar: false
	}],
	["sepPractice", "ClickSeparator", {
		html: "
Remember to respond to the diagrams as quickly and as accurately as possible.
",
		normalMessage: "
Correct!
",
		errorMessage: "
Incorrect!
 ",
		hideProgressBar: true,
		countsForProgressBar: false
	}]

];

items = items.concat(newTestItems);
items = items.concat(newSUSItems);

//alert(items);
<|endoftext|>


<commit_before>(function () {
    var defaults = {
        tooltip: {
            animate: true,
            opacity: 0.85,
            showTime: 300,
            hideTime: 500,
            distance: 5
        }
    };

    function render(data, layer, options) {

        var clearHideTimer = function () {
            clearTimeout(this.tooltip.hideTimer);
        };

        var changeOpacity = function (opacity, delay) {
            if(this.options.tooltip.animate) {
                this.tooltipElement
                    .transition().duration(delay)
                        .style('opacity', opacity);
            } else {
                this.tooltipElement.style('opacity', opacity);
            }
        };

        // return the centroid x, y coordinates relative to the svg container
        var getCentroid = function (element) {
            var bbox = element.getBoundingClientRect();
            var clientRect = element.getBoundingClientRect();

            return [bbox.left + bbox.width/2, bbox.top + bbox.height/2];
        };

        function getPosition(options) {

        }

        var positionTooltip = function (d) {
            var pointOrCentroid = function () {
                return d3.event.target.tagName === 'path' ? getCentroid(d3.event.target) : d3.mouse(this.container.node());
            };
            var xScale = this.xScale;
            var yScale = this.yScale;
            var plotLeft = this.options.chart.plotLeft;
            var plotWidth = this.options.chart.plotWidth;
            var plotTop = this.options.chart.plotTop;
            var plotHeight = this.options.chart.plotHeight;
            var distance = this.options.tooltip.distance;
            var width = parseFloat(this.tooltipElement.style('width'));
            var height = parseFloat(this.tooltipElement.style('height'));
            var pointX = xScale ? xScale(d.x) : pointOrCentroid()[0];
            var pointY = yScale ? yScale(d.y) : pointOrCentroid()[1];
            var alignedRight;

            var clampPosition = function (pos) {
                // Check outside plot area (left)
                if (pos.x < plotLeft) {
                    pos.x = plotLeft + distance;
                }

                // Check outside plot area (right)
                if (pos.x + width > plotLeft + plotWidth) {
                    pos.x -= (pos.x + width) - (plotLeft + plotWidth);
                    // Don't overlap point
                    pos.y = plotTop + pointY - (height + distance);
                    alignedRight = true;
                }

                // Check outside the plot area (top)
                if (pos.y < plotTop) {
                    pos.y = plotTop + distance;

                    // Don't overlap point
                    if (alignedRight && pointY >= pos.y && pointY <= pos.y + height) {
                        pos.y = pointY + plotTop + distance;
                    }
                }

                // Check outside the plot area (bottom)
                if (pos.y + height > plotTop + plotHeight) {
                    pos.y = Math.max(plotTop, plotTop + plotHeight - (height + distance));
                }

                return pos;
            };

            var positioner = {
                'vertical': function verticalPositioner() {
                    var pos = {
                        x: plotLeft + pointX - (distance + width),
                        y: plotTop + pointY - (distance + height)
                    };

                    return clampPosition(pos);
                },

                'horizontal': function horizontalPositioner() {
                    var pos = {
                        x: plotLeft + pointY - (distance + width),
                        y: plotTop + pointX - (distance + height)
                    };

                    return clampPosition(pos);
                }
            };

            return options.chart.rotatedFrame ? positioner.horizontal() : positioner.vertical();

        };

        var onMouseOver = function (d) {
            show.call(this, d);
        };

        var onMouseOut = function () {
            changeOpacity.call(this, 0, this.options.tooltip.hideTime);
        };

        var getTooltipText = function (d) {
            function match() {
                var params = Array.prototype.slice.call(arguments);
                var list = params[0];
                var rest = params.slice(1);

                var response = _.map(list, function(fn) { return fn.apply(this, rest); }).concat([_.noop]);

                return _.first(_.select(response));
            }
            var options = this.options.tooltip;
            var formatters = [
                function (d) { return options.formatter ? _.partial(options.formatter, d) : null; },
                function (d) { return d.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d) : null; },
                function (d) { return d.data && d.data.hasOwnProperty('x') ? _.partial(function (d) { return 'x: ' + d.x + '
' + 'y: ' + d.y; }, d.data) : null; },
                function (d) { return d.hasOwnProperty('value') ? _.partial(function (d) { return d.value; }, d) : null;  },
                function () { return function () { return 'NA'; }; }
            ];

            return match(formatters, d)();
        };

        var show = function (d) {
            clearHideTimer.call(this);

            this.tooltipElement.select('.text').html(getTooltipText.call(this, d));

            var pos = positionTooltip.call(this, d);

            this.tooltipElement
                .style('top', pos.y + 'px')
                .style('left', pos.x + 'px');

            changeOpacity.call(this, this.options.tooltip.opacity, this.options.tooltip.showTime);
        };

        this.tooltipElement = this.container
            .style('position', 'relative')
            .selectAll('.nw-tooltip').data([1]);

        this.tooltipElement
            .enter().append('div')
            .attr('class', 'nw-tooltip')
            .style('opacity', 0)
            .append('div')
                .attr('class', 'text');

        this.svg.selectAll('.tooltip-tracker')
            .on('mouseover.tooltip', onMouseOver.bind(this))
            .on('mouseout.tooltip',  onMouseOut.bind(this));
    }

    render.defaults = defaults;

    /**
    * Adds a tooltip on hover to all other visualizations in the Contour instance.
    *
    * Although not strictly required, this visualization does not appear unless there are one or more additional visualizations in this Contour instance for which to show the tooltips.
    *
    * ### Example:
    *
    *     new Contour({el: '.myChart'})
    *           .cartesian()
    *           .line([2, 4, 3, 5, 7])
    *           .tooltip()
    *           .render();
    *
    * @name tooltip(data, options)
    * @param {object|array} data Ignored!
    * @param {object} options Configuration options particular to this visualization that override the defaults.
    * @api public
    *
    */
    Contour.export('tooltip', render);

})();

<commit_msg>Changed the default tooltip formatters<commit_after>(function () {
    var defaults = {
        tooltip: {
            animate: true,
            opacity: 0.85,
            showTime: 300,
            hideTime: 500,
            distance: 5
        }
    };

    function render(data, layer, options) {

        var clearHideTimer = function () {
            clearTimeout(this.tooltip.hideTimer);
        };

        var changeOpacity = function (opacity, delay) {
            if(this.options.tooltip.animate) {
                this.tooltipElement
                    .transition().duration(delay)
                        .style('opacity', opacity);
            } else {
                this.tooltipElement.style('opacity', opacity);
            }
        };

        // return the centroid x, y coordinates relative to the svg container
        var getCentroid = function (element) {
            var bbox = element.getBoundingClientRect();
            var clientRect = element.getBoundingClientRect();

            return [bbox.left + bbox.width/2, bbox.top + bbox.height/2];
        };

        function getPosition(options) {

        }

        var positionTooltip = function (d) {
            var pointOrCentroid = function () {
                return d3.event.target.tagName === 'path' ? getCentroid(d3.event.target) : d3.mouse(this.container.node());
            };
            var xScale = this.xScale;
            var yScale = this.yScale;
            var plotLeft = this.options.chart.plotLeft;
            var plotWidth = this.options.chart.plotWidth;
            var plotTop = this.options.chart.plotTop;
            var plotHeight = this.options.chart.plotHeight;
            var distance = this.options.tooltip.distance;
            var width = parseFloat(this.tooltipElement.style('width'));
            var height = parseFloat(this.tooltipElement.style('height'));
            var pointX = xScale ? xScale(d.x) : pointOrCentroid()[0];
            var pointY = yScale ? yScale(d.y) : pointOrCentroid()[1];
            var alignedRight;

            var clampPosition = function (pos) {
                // Check outside plot area (left)
                if (pos.x < plotLeft) {
                    pos.x = plotLeft + distance;
                }

                // Check outside plot area (right)
                if (pos.x + width > plotLeft + plotWidth) {
                    pos.x -= (pos.x + width) - (plotLeft + plotWidth);
                    // Don't overlap point
                    pos.y = plotTop + pointY - (height + distance);
                    alignedRight = true;
                }

                // Check outside the plot area (top)
                if (pos.y < plotTop) {
                    pos.y = plotTop + distance;

                    // Don't overlap point
                    if (alignedRight && pointY >= pos.y && pointY <= pos.y + height) {
                        pos.y = pointY + plotTop + distance;
                    }
                }

                // Check outside the plot area (bottom)
                if (pos.y + height > plotTop + plotHeight) {
                    pos.y = Math.max(plotTop, plotTop + plotHeight - (height + distance));
                }

                return pos;
            };

            var positioner = {
                'vertical': function verticalPositioner() {
                    var pos = {
                        x: plotLeft + pointX - (distance + width),
                        y: plotTop + pointY - (distance + height)
                    };

                    return clampPosition(pos);
                },

                'horizontal': function horizontalPositioner() {
                    var pos = {
                        x: plotLeft + pointY - (distance + width),
                        y: plotTop + pointX - (distance + height)
                    };

                    return clampPosition(pos);
                }
            };

            return options.chart.rotatedFrame ? positioner.horizontal() : positioner.vertical();

        };

        var onMouseOver = function (d) {
            show.call(this, d);
        };

        var onMouseOut = function () {
            changeOpacity.call(this, 0, this.options.tooltip.hideTime);
        };

        var getTooltipText = function (d) {
            function match() {
                var params = Array.prototype.slice.call(arguments);
                var list = params[0];
                var rest = params.slice(1);

                var response = _.map(list, function(fn) { return fn.apply(this, rest); }).concat([_.noop]);

                return _.first(_.select(response));
            }
            var options = this.options.tooltip;
            var formatters = [
                function (d) { return options.formatter ? _.partial(options.formatter, d) : null; },
                function (d) { return d.hasOwnProperty('x') ? _.partial(function (d) { return d.x + '
' + d.y; }, d) : null; },
                function (d) { return d.data && d.data.hasOwnProperty('x') ? _.partial(function (d) { return d.x + '
' + d.y; }, d.data) : null; },
                function (d) { return d.hasOwnProperty('value') ? _.partial(function (d) { return d.value; }, d) : null;  },
                function () { return function () { return 'NA'; }; }
            ];

            return match(formatters, d)();
        };

        var show = function (d) {
            clearHideTimer.call(this);

            this.tooltipElement.select('.text').html(getTooltipText.call(this, d));

            var pos = positionTooltip.call(this, d);

            this.tooltipElement
                .style('top', pos.y + 'px')
                .style('left', pos.x + 'px');

            changeOpacity.call(this, this.options.tooltip.opacity, this.options.tooltip.showTime);
        };

        this.tooltipElement = this.container
            .style('position', 'relative')
            .selectAll('.nw-tooltip').data([1]);

        this.tooltipElement
            .enter().append('div')
            .attr('class', 'nw-tooltip')
            .style('opacity', 0)
            .append('div')
                .attr('class', 'text');

        this.svg.selectAll('.tooltip-tracker')
            .on('mouseover.tooltip', onMouseOver.bind(this))
            .on('mouseout.tooltip',  onMouseOut.bind(this));
    }

    render.defaults = defaults;

    /**
    * Adds a tooltip on hover to all other visualizations in the Contour instance.
    *
    * Although not strictly required, this visualization does not appear unless there are one or more additional visualizations in this Contour instance for which to show the tooltips.
    *
    * ### Example:
    *
    *     new Contour({el: '.myChart'})
    *           .cartesian()
    *           .line([2, 4, 3, 5, 7])
    *           .tooltip()
    *           .render();
    *
    * @name tooltip(data, options)
    * @param {object|array} data Ignored!
    * @param {object} options Configuration options particular to this visualization that override the defaults.
    * @api public
    *
    */
    Contour.export('tooltip', render);

})();

<|endoftext|>


<commit_before>var Cardeck = require('../index.js');
var Deck = Cardeck.Deck;
var Card = Cardeck.Card;

describe('Testing Jasmine is well installed and configured', function() {
    it('True is true :)', function() {
        expect(true).toBeTrue();
    });
});

describe('Testing Card constructor', function() {
    it('Card default constructor (no parameters)', function() {
        var testCard = new Card();

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(0);
        expect(testCard.type).toBe('default');
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor, only value specified', function() {
        var value = 5;
        var testCard = new Card(value);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe('default');
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor, only value and type specified', function() {
        var value = 5,
            type = 'Hearts';
        var testCard = new Card(value, type);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe(type);
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor with all parameters specified', function() {
        var value = 5,
            type = 'Hearts',
            imgPath = './img/my-img.jpg';
        var testCard = new Card(value, type, imgPath);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe(type);
        expect(testCard.imgPath).toBe(imgPath);
    });
});

describe('Testing Card methods', function() {
    it('Method setValue', function() {
        var testCard = new Card();
        var value = 8;

        testCard.setValue(value);

        expect(testCard.value).toBe(value);
    });

    it('Method getValue', function() {
        var value = 10;
        var testCard = new Card(value);

        expect(testCard.getValue()).toBe(value);
    });

    it('Method setType', function() {
        var testCard = new Card();
        var type = 'Aces';

        testCard.setType(type);

        expect(testCard.type).toBe(type);
    });

    it('Method getType', function() {
        var type = 'Diamonds';
        var testCard = new Card(null, type);

        expect(testCard.getType()).toBe(type);
    });

    it('Method setImgPath', function() {
        var testCard = new Card();
        var imgPath = './img/card-img.png';

        testCard.setImgPath(imgPath);

        expect(testCard.imgPath).toBe(imgPath);
    });

    it('Method getImgPath', function() {
        var imgPath = './assets/3-hearts.png';
        var testCard = new Card(null, null, imgPath);

        expect(testCard.getImgPath()).toBe(imgPath);
    });
});

describe('Testing Deck methods', function () {
    it('Deck default constructor', function () {
        var testDeck = new Deck();

        expect(testDeck).toHaveString('name');
        expect(testDeck).toHaveArray('cards');
        expect(testDeck).toHaveNumber('size');

        expect(testDeck.name).toBe('default');
        expect(testDeck.cards).toBeEmptyArray();
        expect(testDeck.size).toBe(0);
    });

    it('Deck constructor with parameter', function () {
        var name = 'My First Deck';
        var testDeck = new Deck(name);

        expect(testDeck).toHaveString('name');
        expect(testDeck).toHaveArray('cards');
        expect(testDeck).toHaveNumber('size');

        expect(testDeck.name).toBe(name);
        expect(testDeck.cards).toBeEmptyArray();
        expect(testDeck.size).toBe(0);
    });

    it('Method getName', function() {
        var name = 'This is my deck';
        var testDeck = new Deck(name);

        expect(testDeck.getName()).toBe(name);
    });

    it('Method setName', function() {
        var name = 'The deck';
        var testDeck = new Deck();

        testDeck.setName(name);

        expect(testDeck.name).toBe(name);
    });

    // TODO: Add more than one card to decks on following test cases.

    // it('Method addCardToTop with a generic object parameter', function() {
    //     var testDeck = new Deck();
    //     var obj = {
    //         value: 3,
    //         type: 'Hearts',
    //         imgPath: 'img/card.jpg'
    //     };

    //     testDeck.addCardToTop(obj);

    //     var objAsCard = new Card(obj.value, obj.type, obj.imgPath);

    //     expect(testDeck.cards[0]).toEqual(objAsCard);
    // });

    // it('Method addCardToTop with a card parameter', function () {
    //     var testDeck = new Deck();
    //     var card = new Card(2, 'Clubs', 'my-card.jpg');

    //     testDeck.addCardToTop(card);

    //     expect(testDeck.cards[0]).toBe(card);
    // });
});<commit_msg>Added test cases for 'draw' methods<commit_after>var Cardeck = require('../index.js');
var Deck = Cardeck.Deck;
var Card = Cardeck.Card;

describe('Testing Jasmine is well installed and configured', function() {
    it('True is true :)', function() {
        expect(true).toBeTrue();
    });
});

describe('Testing Card constructor', function() {
    it('Card default constructor (no parameters)', function() {
        var testCard = new Card();

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(0);
        expect(testCard.type).toBe('default');
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor, only value specified', function() {
        var value = 5;
        var testCard = new Card(value);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe('default');
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor, only value and type specified', function() {
        var value = 5,
            type = 'Hearts';
        var testCard = new Card(value, type);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe(type);
        expect(testCard.imgPath).toBe('./card.png');
    });

    it('Card constructor with all parameters specified', function() {
        var value = 5,
            type = 'Hearts',
            imgPath = './img/my-img.jpg';
        var testCard = new Card(value, type, imgPath);

        expect(testCard).toHaveNumber('value');
        expect(testCard).toHaveString('type');
        expect(testCard).toHaveString('imgPath');

        expect(testCard.value).toBe(value);
        expect(testCard.type).toBe(type);
        expect(testCard.imgPath).toBe(imgPath);
    });
});

describe('Testing Card methods', function() {
    it('Method setValue', function() {
        var testCard = new Card();
        var value = 8;

        testCard.setValue(value);

        expect(testCard.value).toBe(value);
    });

    it('Method getValue', function() {
        var value = 10;
        var testCard = new Card(value);

        expect(testCard.getValue()).toBe(value);
    });

    it('Method setType', function() {
        var testCard = new Card();
        var type = 'Aces';

        testCard.setType(type);

        expect(testCard.type).toBe(type);
    });

    it('Method getType', function() {
        var type = 'Diamonds';
        var testCard = new Card(null, type);

        expect(testCard.getType()).toBe(type);
    });

    it('Method setImgPath', function() {
        var testCard = new Card();
        var imgPath = './img/card-img.png';

        testCard.setImgPath(imgPath);

        expect(testCard.imgPath).toBe(imgPath);
    });

    it('Method getImgPath', function() {
        var imgPath = './assets/3-hearts.png';
        var testCard = new Card(null, null, imgPath);

        expect(testCard.getImgPath()).toBe(imgPath);
    });
});

describe('Testing Deck methods', function () {
    it('Deck default constructor', function () {
        var testDeck = new Deck();

        expect(testDeck).toHaveString('name');
        expect(testDeck).toHaveArray('cards');
        expect(testDeck).toHaveNumber('size');

        expect(testDeck.name).toBe('default');
        expect(testDeck.cards).toBeEmptyArray();
        expect(testDeck.size).toBe(0);
    });

    it('Deck constructor with parameter', function () {
        var name = 'My First Deck';
        var testDeck = new Deck(name);

        expect(testDeck).toHaveString('name');
        expect(testDeck).toHaveArray('cards');
        expect(testDeck).toHaveNumber('size');

        expect(testDeck.name).toBe(name);
        expect(testDeck.cards).toBeEmptyArray();
        expect(testDeck.size).toBe(0);
    });

    it('Method getName', function() {
        var name = 'This is my deck';
        var testDeck = new Deck(name);

        expect(testDeck.getName()).toBe(name);
    });

    it('Method setName', function() {
        var name = 'The deck';
        var testDeck = new Deck();

        testDeck.setName(name);

        expect(testDeck.name).toBe(name);
    });

    it('Method addCardToTop', function() {
        var testDeck = new Deck();
        var cardsObjs = [
            {
                value: 3,
                type: 'Hearts',
                imgPath: 'img/card1.jpg'
            },
            {
                value: 2,
                type: 'Aces',
                imgPath: 'img/card2.jpg'
            },
            {
                value: 7,
                type: 'Diamonds',
                imgPath: 'img/card3.jpg'
            }
        ];

        cardsObjs.forEach(function(card) {
            testDeck.addCardToTop(card);
        }, this);

        var objsAsCard = [
            new Card(cardsObjs[0].value, cardsObjs[0].type, cardsObjs[0].imgPath),
            new Card(cardsObjs[1].value, cardsObjs[1].type, cardsObjs[1].imgPath),
            new Card(cardsObjs[2].value, cardsObjs[2].type, cardsObjs[2].imgPath)
        ];

        expect(testDeck.size).toEqual(objsAsCard.length);
        expect(testDeck.cards[0]).toEqual(objsAsCard[2]);
        expect(testDeck.cards[1]).toEqual(objsAsCard[1]);
        expect(testDeck.cards[2]).toEqual(objsAsCard[0]);
    });

    it('Method addCardToBottom', function() {
        var testDeck = new Deck();
        var cardsObjs = [
            {
                value: 'A',
                type: 'Clubs',
                imgPath: 'img/card1.jpg'
            },
            {
                value: 'J',
                type: 'Hearts',
                imgPath: 'img/card2.jpg'
            },
            {
                value: 4,
                type: 'Clubs',
                imgPath: 'img/card3.jpg'
            }
        ];

        cardsObjs.forEach(function(card) {
            testDeck.addCardToBottom(card);
        }, this);

        var objsAsCard = [
            new Card(cardsObjs[0].value, cardsObjs[0].type, cardsObjs[0].imgPath),
            new Card(cardsObjs[1].value, cardsObjs[1].type, cardsObjs[1].imgPath),
            new Card(cardsObjs[2].value, cardsObjs[2].type, cardsObjs[2].imgPath)
        ];

        expect(testDeck.size).toEqual(objsAsCard.length);
        expect(testDeck.cards[0]).toEqual(objsAsCard[0]);
        expect(testDeck.cards[1]).toEqual(objsAsCard[1]);
        expect(testDeck.cards[2]).toEqual(objsAsCard[2]);
    });

    it('Method drawCardFromTop', function() {
        var testDeck = new Deck();
        var cardsObjs = [
            {
                value: 6,
                type: 'Hearts',
                imgPath: 'img/card1.jpg'
            },
            {
                value: 9,
                type: 'Clubs',
                imgPath: 'img/card2.jpg'
            },
            {
                value: 10,
                type: 'Aces',
                imgPath: 'img/card3.jpg'
            }
        ];

        cardsObjs.forEach(function(card) {
            testDeck.addCardToBottom(card);
        }, this);

        var objsAsCard = [
            new Card(cardsObjs[0].value, cardsObjs[0].type, cardsObjs[0].imgPath),
            new Card(cardsObjs[1].value, cardsObjs[1].type, cardsObjs[1].imgPath),
            new Card(cardsObjs[2].value, cardsObjs[2].type, cardsObjs[2].imgPath)
        ];

        expect(testDeck.size).toEqual(objsAsCard.length);
        expect(testDeck.drawCardFromTop()).toEqual(objsAsCard[0]);
        expect(testDeck.drawCardFromTop()).toEqual(objsAsCard[1]);
        expect(testDeck.drawCardFromTop()).toEqual(objsAsCard[2]);
        expect(testDeck.size).toEqual(0);
    });

    it('Method drawCardFromBottom', function() {
        var testDeck = new Deck();
        var cardsObjs = [
            {
                value: 6,
                type: 'Hearts',
                imgPath: 'img/card1.jpg'
            },
            {
                value: 9,
                type: 'Clubs',
                imgPath: 'img/card2.jpg'
            },
            {
                value: 10,
                type: 'Aces',
                imgPath: 'img/card3.jpg'
            }
        ];

        cardsObjs.forEach(function(card) {
            testDeck.addCardToBottom(card);
        }, this);

        var objsAsCard = [
            new Card(cardsObjs[0].value, cardsObjs[0].type, cardsObjs[0].imgPath),
            new Card(cardsObjs[1].value, cardsObjs[1].type, cardsObjs[1].imgPath),
            new Card(cardsObjs[2].value, cardsObjs[2].type, cardsObjs[2].imgPath)
        ];

        expect(testDeck.size).toEqual(objsAsCard.length);
        expect(testDeck.drawCardFromBottom()).toEqual(objsAsCard[2]);
        expect(testDeck.drawCardFromBottom()).toEqual(objsAsCard[1]);
        expect(testDeck.drawCardFromBottom()).toEqual(objsAsCard[0]);
        expect(testDeck.size).toEqual(0);
    });

});<|endoftext|>


<commit_before>import { atob } from 'Base64';
import { PrivateAPI } from '@r/private';

import Session from '../../app/models/Session';
import makeSessionFromData from './makeSessionFromData';
import setSessionCookies from './setSessionCookies';
import * as sessionActions from '../../app/actions/session';

export default async (ctx, dispatch, apiOptions) => {
  // try to create a session from the existing cookie
  // if the session is malformed somehow, the catch will trigger when trying
  // to access it
  const tokenCookie = ctx.cookies.get('token');
  if (!tokenCookie) {
    return;
  }

  const sessionData = JSON.parse(atob(tokenCookie));
  let session = new Session(sessionData);

  // if the session is invalid, try to use the refresh token to grab a new
  // session.
  if (!session.isValid) {
    const data = await PrivateAPI.refreshToken(apiOptions, sessionData.refreshToken);
    session = makeSessionFromData({ ...data, refresh_token: sessionData.refreshToken });

    // don't forget to set the cookies with the new session, or the session
    // will remain invalid the next time the page is fetched
    setSessionCookies(ctx, session);
  }

  // push the session into the store
  dispatch(sessionActions.setSession(session));
};
<commit_msg>Add logic to convert reddit dot com cookies<commit_after>import { atob } from 'Base64';
import { PrivateAPI } from '@r/private';

import Session from '../../app/models/Session';
import makeSessionFromData from './makeSessionFromData';
import setSessionCookies from './setSessionCookies';
import * as sessionActions from '../../app/actions/session';

export default async (ctx, dispatch, apiOptions) => {
  // try to create a session from the existing cookie
  // if the session is malformed somehow, the catch will trigger when trying
  // to access it
  const tokenCookie = ctx.cookies.get('token');
  const redditSessionExists = ctx.cookies.get('reddit_session');
  if (!(tokenCookie || redditSessionExists)) {
    return;
  }

  let session;
  let sessionData;

  if (tokenCookie) {
    sessionData = JSON.parse(atob(tokenCookie));
    session = new Session(sessionData);
  } else {
    // try to convert reddit session cookie
    const cookies = ctx.headers.cookie.replace(/__cf_mob_redir=1/, '__cf_mob_redir=0').split(';');
    sessionData = await PrivateAPI.convertCookiesToAuthToken(apiOptions, cookies);
    session = new Session(sessionData);
  }

  // if the session is invalid, try to use the refresh token to grab a new
  // session.
  if (!session.isValid) {
    const data = await PrivateAPI.refreshToken(apiOptions, sessionData.refreshToken);
    session = makeSessionFromData({ ...data, refresh_token: sessionData.refreshToken });

    // don't forget to set the cookies with the new session, or the session
    // will remain invalid the next time the page is fetched
    setSessionCookies(ctx, session);
  }

  // push the session into the store
  dispatch(sessionActions.setSession(session));
};
<|endoftext|>


<commit_before>/*
 Evenup:
 Author: Matt Brunt <matt@burnthebook.co.uk>
 A way equalize the height of 'rows' of items responsively, that is, when the number of items per 'row' can change dynamically based on the size of the screen with your media queries.
 In the context of evenup - a 'row' is not necessarily a collection of items in a row container, it's best explained with an example:

 Given 12 items in a grid with the following arrangement:
 large screen	@ 4 x 3
 medium screen	@ 3 x 4
 small screen	@ 2 x 6

 Effectively, it's just a collection of 12 items:

 In foundation w/ Emmet coding it'd be something like:
 .row>small-6.medium-4.large-3.columns*12

 Now on a large screen, we want 'rows' of 4 items equal heights, but then on medium, rows of 3, on small, rows of 2

 Foundation has a built in equalizer, but that equalizes all items within a collection, so one row with shorter items will still be the same height as the largest item in the whole group.

 This plugin for jQuery solves the issue of having 'rows' of items equal heights on various screen sizes.

 Usage:

 On the wrappers around the items you want to make even, you must specify the number of items at each of your breakpoints, if not, it won't work.

 Then within that block, you assign data-evenup-item to the things you want to make the same height:

 Then you just need to run the evenup plugin like so:

 $(document).ready(function() {
 var breakpoints = { small: 0, medium: 641, large: 1025 };
 $('[data-evenup]').evenup(breakpoints);
 });

 */
(function($) {

    $.fn.evenup = function(breakpoints) {
        // loop through each one of the evenups we have
        this.each(function() {

            var options = $(this).data('options'); // data-options='{ "breakpointLabel": numItems }'

            if( ! options) return; // no options, just return

            var windowWidth = window.innerWidth || $(window).width();

            // work out where we are in respect to our breakpoints
            var currentBreakpoint = '';
            $.each(breakpoints, function( breakpointLabel, breakpointSize )
            {
                if(windowWidth >= breakpointSize)
                {
                    currentBreakpoint = breakpointLabel;
                }
            });

            // get our items to even up, count them, work out how many per 'row' for the breakpoint we're at.
            var evenupItems = $(this).find('[data-evenup-item]');
            var count = evenupItems.length;
            var perRow = options[currentBreakpoint];

            // if we have items and we have a number per row set for the breakpoint we've found
            if(evenupItems.length > 0 && perRow)
            {
                // set the height on all items to auto so it can correctly re-calculate the tallest item in a row
                evenupItems.height('auto');

                // how many rows do we have?
                rows = Math.ceil(count/perRow);

                // loop through our rows for the number per row we have at this breakpoint
                for(var i = 1; i <= rows; i++)
                {
                    // reset the row height
                    var rowHeight = 0;

                    // loop through the items in this row and assign the tallest item to rowHeight
                    for(var j = 1; j <= perRow; j++)
                    {
                        var sliceStart = (i-1)*perRow+j-1; // get the starting item for the 'row' we're in
                        var sliceEnd = (i-1)*perRow+j; // get the last item for the 'row' we're in

                        thisHeight = evenupItems.slice(sliceStart, sliceEnd).height();
                        if(thisHeight > rowHeight) rowHeight = thisHeight;
                    }

                    // set the height of the items on this row to the tallest item
                    var sliceStart = (i-1)*perRow; // get the starting item for the 'row' we're in
                    var sliceEnd = (i-1)*perRow+perRow; // get the last item for the 'row' we're in

                    evenupItems.slice(sliceStart, sliceEnd).height(rowHeight);
                }
            }
        });
    };
}(jQuery));

function evenupItems() {
    // our breakpoints we're using for the various sizes
    var breakpoints = { small: 0, medium: 641, large: 1025 };
    $('[data-evenup]').evenup(breakpoints);
}

$(document).ready(function() {
    $(document).foundation();
    $('#search').focus();

    evenupItems();

    $('#team_id').change(function(event) {
        var teamId = $(this).val();
        $('#manager_id').html('');

        $.get('/vote/get-manager/' + teamId, function(data){
            $.each(data, function(index, manager) {
                $('#manager_id').append('<option value="' + manager.id + '">' + manager.name + '</option>');
            });
        });
    });

    // For the code below... I am sorry.
    $('#star-rating').hide();
    $('.rating-star').vibrate();
    $('.rating-star').hover(
        function() { // mouse over
            $('.rating-star').removeClass('active'); // remove all active ones for now

            $(this).prevAll().addClass('active'); // add class of active to all previous ones
        },
        function() { // mouse out
            $('.rating-star').removeClass('active'); // remove all active ones for now
            $('.rating-star').each(function() { // here we make sure to re-highlight the elements up to and including the one that's already been clicked.
                if((($(this).index() + 1) <= $('#star-rating').val()) && $('#star-rating').val() != '') {
                    $(this).prevAll().addClass('active'); // highlight all previous stars
                    $(this).addClass('active'); // highlight current star
                }
            });
        }
    );
    $('.rating-star').click(function() { // when we click a star
        $('.rating-star').removeClass('active'); // remove all active stars
        $(this).prevAll().addClass('active'); // highlight all previous stars
        $(this).addClass('active'); // highlight current star
        console.log(($(this).index()+1));
        $('#star-rating').attr('value', $(this).index()+1); // update the input to the value of the star
    });

});

$(window).resize(function() {
   evenupItems();
});

// ugh, highcharts...
setTimeout(function() {
    evenupItems();
}, 1000);<commit_msg>main.js updated - shouldn't 'error' on gulp task now...<commit_after>/*
 Evenup:
 Author: Matt Brunt <matt@burnthebook.co.uk>
 A way equalize the height of 'rows' of items responsively, that is, when the number of items per 'row' can change dynamically based on the size of the screen with your media queries.
 In the context of evenup - a 'row' is not necessarily a collection of items in a row container, it's best explained with an example:

 Given 12 items in a grid with the following arrangement:
 large screen	@ 4 x 3
 medium screen	@ 3 x 4
 small screen	@ 2 x 6

 Effectively, it's just a collection of 12 items:

 In foundation w/ Emmet coding it'd be something like:
 .row>small-6.medium-4.large-3.columns*12

 Now on a large screen, we want 'rows' of 4 items equal heights, but then on medium, rows of 3, on small, rows of 2

 Foundation has a built in equalizer, but that equalizes all items within a collection, so one row with shorter items will still be the same height as the largest item in the whole group.

 This plugin for jQuery solves the issue of having 'rows' of items equal heights on various screen sizes.

 Usage:

 On the wrappers around the items you want to make even, you must specify the number of items at each of your breakpoints, if not, it won't work.

 Then within that block, you assign data-evenup-item to the things you want to make the same height:

 Then you just need to run the evenup plugin like so:

 $(document).ready(function() {
 var breakpoints = { small: 0, medium: 641, large: 1025 };
 $('[data-evenup]').evenup(breakpoints);
 });

 */
(function($) {

    $.fn.evenup = function(breakpoints) {
        // loop through each one of the evenups we have
        this.each(function() {

            var options = $(this).data('options'); // data-options='{ "breakpointLabel": numItems }'

            if( ! options) return; // no options, just return

            var windowWidth = window.innerWidth || $(window).width();

            // work out where we are in respect to our breakpoints
            var currentBreakpoint = '';
            $.each(breakpoints, function( breakpointLabel, breakpointSize )
            {
                if(windowWidth >= breakpointSize)
                {
                    currentBreakpoint = breakpointLabel;
                }
            });

            // get our items to even up, count them, work out how many per 'row' for the breakpoint we're at.
            var evenupItems = $(this).find('[data-evenup-item]');
            var count = evenupItems.length;
            var perRow = options[currentBreakpoint];

            // if we have items and we have a number per row set for the breakpoint we've found
            if(evenupItems.length > 0 && perRow)
            {
                // set the height on all items to auto so it can correctly re-calculate the tallest item in a row
                evenupItems.height('auto');

                // how many rows do we have?
                rows = Math.ceil(count/perRow);

                // loop through our rows for the number per row we have at this breakpoint
                for(var i = 1; i <= rows; i++)
                {
                    // reset the row height
                    var rowHeight = 0;
                    var sliceStart = 0;
                    var sliceEnd = 0;

                    // loop through the items in this row and assign the tallest item to rowHeight
                    for(var j = 1; j <= perRow; j++)
                    {
                        sliceStart = (i-1)*perRow+j-1; // get the starting item for the 'row' we're in
                        sliceEnd = (i-1)*perRow+j; // get the last item for the 'row' we're in

                        thisHeight = evenupItems.slice(sliceStart, sliceEnd).height();
                        if(thisHeight > rowHeight) rowHeight = thisHeight;
                    }

                    // set the height of the items on this row to the tallest item
                    sliceStart = (i-1)*perRow; // get the starting item for the 'row' we're in
                    sliceEnd = (i-1)*perRow+perRow; // get the last item for the 'row' we're in

                    evenupItems.slice(sliceStart, sliceEnd).height(rowHeight);
                }
            }
        });
    };
}(jQuery));

function evenupItems() {
    // our breakpoints we're using for the various sizes
    var breakpoints = { small: 0, medium: 641, large: 1025 };
    $('[data-evenup]').evenup(breakpoints);
}

$(document).ready(function() {
    $(document).foundation();
    $('#search').focus();

    evenupItems();

    $('#team_id').change(function(event) {
        var teamId = $(this).val();
        $('#manager_id').html('');

        $.get('/vote/get-manager/' + teamId, function(data){
            $.each(data, function(index, manager) {
                $('#manager_id').append('<option value="' + manager.id + '">' + manager.name + '</option>');
            });
        });
    });

    // For the code below... I am sorry.
    $('#star-rating').hide();
    $('.rating-star').vibrate();
    $('.rating-star').hover(
        function() { // mouse over
            $('.rating-star').removeClass('active'); // remove all active ones for now

            $(this).prevAll().addClass('active'); // add class of active to all previous ones
        },
        function() { // mouse out
            $('.rating-star').removeClass('active'); // remove all active ones for now
            $('.rating-star').each(function() { // here we make sure to re-highlight the elements up to and including the one that's already been clicked.
                if((($(this).index() + 1) <= $('#star-rating').val()) && $('#star-rating').val() !== '') {
                    $(this).prevAll().addClass('active'); // highlight all previous stars
                    $(this).addClass('active'); // highlight current star
                }
            });
        }
    );
    $('.rating-star').click(function() { // when we click a star
        $('.rating-star').removeClass('active'); // remove all active stars
        $(this).prevAll().addClass('active'); // highlight all previous stars
        $(this).addClass('active'); // highlight current star
        console.log(($(this).index()+1));
        $('#star-rating').attr('value', $(this).index()+1); // update the input to the value of the star
    });

});

$(window).resize(function() {
   evenupItems();
});

// ugh, highcharts...
setTimeout(function() {
    evenupItems();
}, 1000);<|endoftext|>


<commit_before>
var data = (localStorage.getItem('todoList')) ? JSON.parse(localStorage.getItem('todoList')):{
  todo: [],
  completed: []
};

// Remove and complete icons in SVG format
var removeSVG = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve"><rect class="noFill" width="22" height="22"/><g><g><path class="fill" d="M16.1,3.6h-1.9V3.3c0-1.3-1-2.3-2.3-2.3h-1.7C8.9,1,7.8,2,7.8,3.3v0.2H5.9c-1.3,0-2.3,1-2.3,2.3v1.3c0,0.5,0.4,0.9,0.9,1v10.5c0,1.3,1,2.3,2.3,2.3h8.5c1.3,0,2.3-1,2.3-2.3V8.2c0.5-0.1,0.9-0.5,0.9-1V5.9C18.4,4.6,17.4,3.6,16.1,3.6z M9.1,3.3c0-0.6,0.5-1.1,1.1-1.1h1.7c0.6,0,1.1,0.5,1.1,1.1v0.2H9.1V3.3z M16.3,18.7c0,0.6-0.5,1.1-1.1,1.1H6.7c-0.6,0-1.1-0.5-1.1-1.1V8.2h10.6V18.7z M17.2,7H4.8V5.9c0-0.6,0.5-1.1,1.1-1.1h10.2c0.6,0,1.1,0.5,1.1,1.1V7z"/></g><g><g><path class="fill" d="M11,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6s0.6,0.3,0.6,0.6v6.8C11.6,17.7,11.4,18,11,18z"/></g><g><path class="fill" d="M8,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6c0.4,0,0.6,0.3,0.6,0.6v6.8C8.7,17.7,8.4,18,8,18z"/></g><g><path class="fill" d="M14,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6c0.4,0,0.6,0.3,0.6,0.6v6.8C14.6,17.7,14.3,18,14,18z"/></g></g></g></svg>';
var completeSVG = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve"><rect y="0" class="noFill" width="22" height="22"/><g><path class="fill" d="M9.7,14.4L9.7,14.4c-0.2,0-0.4-0.1-0.5-0.2l-2.7-2.7c-0.3-0.3-0.3-0.8,0-1.1s0.8-0.3,1.1,0l2.1,2.1l4.8-4.8c0.3-0.3,0.8-0.3,1.1,0s0.3,0.8,0,1.1l-5.3,5.3C10.1,14.3,9.9,14.4,9.7,14.4z"/></g></svg>';

renderTodoList();

// User clicked on the add button
// If there is any text inside the item field, add that text to the todo list
document.getElementById('add').addEventListener('click', function() {
  var value = document.getElementById('item').value;
  if (value) {
    addItem(value);
  }
});

document.getElementById('item').addEventListener('keydown', function (e) {
  var value = this.value;
  if (e.code === 'Enter' && value) {
    addItem(value);
  }
});

function addItem (value) {
  addItemToDOM(value);
  document.getElementById('item').value = '';

  data.todo.push(value);
  dataObjectUpdated();
}

function renderTodoList() {
  if (!data.todo.length && !data.completed.length) return;

  for (var i = 0; i < data.todo.length; i++) {
    var value = data.todo[i];
    addItemToDOM(value);
  }

  for (var j = 0; j < data.completed.length; j++) {
    var value = data.completed[i];
    addItemToDOM(value, true);
  }
}

function dataObjectUpdated() {
  localStorage.setItem('todoList', JSON.stringify(data));
}

function removeItem() {
  var item = this.parentNode.parentNode;
  var parent = item.parentNode;
  var id = parent.id;
  var value = item.innerText;

  if (id === 'todo') {
    data.todo.splice(data.todo.indexOf(value), 1);
  } else {
    data.completed.splice(data.completed.indexOf(value), 1);
  }
  dataObjectUpdated();

  parent.removeChild(item);
}

function completeItem() {
  var item = this.parentNode.parentNode;
  var parent = item.parentNode;
  var id = parent.id;
  var value = item.innerText;

  if (id === 'todo') {
    data.todo.splice(data.todo.indexOf(value), 1);
    data.completed.push(value);
  } else {
    data.completed.splice(data.completed.indexOf(value), 1);
    data.todo.push(value);
  }
  dataObjectUpdated();

  // Check if the item should be added to the completed list or to re-added to the todo list
  var target = (id === 'todo') ? document.getElementById('completed'):document.getElementById('todo');

  parent.removeChild(item);
  target.insertBefore(item, target.childNodes[0]);
}

// Adds a new item to the todo list
function addItemToDOM(text, completed) {
  var list = (completed) ? document.getElementById('completed'):document.getElementById('todo');

  var item = document.createElement('li');
  item.innerText = text;

  var buttons = document.createElement('div');
  buttons.classList.add('buttons');

  var remove = document.createElement('button');
  remove.classList.add('remove');
  remove.innerHTML = removeSVG;

  // Add click event for removing the item
  remove.addEventListener('click', removeItem);

  var complete = document.createElement('button');
  complete.classList.add('complete');
  complete.innerHTML = completeSVG;

  // Add click event for completing the item
  complete.addEventListener('click', completeItem);

  buttons.appendChild(remove);
  buttons.appendChild(complete);
  item.appendChild(buttons);

  list.insertBefore(item, list.childNodes[0]);
}
<commit_msg>Fixed mistake for using i instead of j in for loop<commit_after>
var data = (localStorage.getItem('todoList')) ? JSON.parse(localStorage.getItem('todoList')):{
  todo: [],
  completed: []
};

// Remove and complete icons in SVG format
var removeSVG = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve"><rect class="noFill" width="22" height="22"/><g><g><path class="fill" d="M16.1,3.6h-1.9V3.3c0-1.3-1-2.3-2.3-2.3h-1.7C8.9,1,7.8,2,7.8,3.3v0.2H5.9c-1.3,0-2.3,1-2.3,2.3v1.3c0,0.5,0.4,0.9,0.9,1v10.5c0,1.3,1,2.3,2.3,2.3h8.5c1.3,0,2.3-1,2.3-2.3V8.2c0.5-0.1,0.9-0.5,0.9-1V5.9C18.4,4.6,17.4,3.6,16.1,3.6z M9.1,3.3c0-0.6,0.5-1.1,1.1-1.1h1.7c0.6,0,1.1,0.5,1.1,1.1v0.2H9.1V3.3z M16.3,18.7c0,0.6-0.5,1.1-1.1,1.1H6.7c-0.6,0-1.1-0.5-1.1-1.1V8.2h10.6V18.7z M17.2,7H4.8V5.9c0-0.6,0.5-1.1,1.1-1.1h10.2c0.6,0,1.1,0.5,1.1,1.1V7z"/></g><g><g><path class="fill" d="M11,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6s0.6,0.3,0.6,0.6v6.8C11.6,17.7,11.4,18,11,18z"/></g><g><path class="fill" d="M8,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6c0.4,0,0.6,0.3,0.6,0.6v6.8C8.7,17.7,8.4,18,8,18z"/></g><g><path class="fill" d="M14,18c-0.4,0-0.6-0.3-0.6-0.6v-6.8c0-0.4,0.3-0.6,0.6-0.6c0.4,0,0.6,0.3,0.6,0.6v6.8C14.6,17.7,14.3,18,14,18z"/></g></g></g></svg>';
var completeSVG = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 22 22" style="enable-background:new 0 0 22 22;" xml:space="preserve"><rect y="0" class="noFill" width="22" height="22"/><g><path class="fill" d="M9.7,14.4L9.7,14.4c-0.2,0-0.4-0.1-0.5-0.2l-2.7-2.7c-0.3-0.3-0.3-0.8,0-1.1s0.8-0.3,1.1,0l2.1,2.1l4.8-4.8c0.3-0.3,0.8-0.3,1.1,0s0.3,0.8,0,1.1l-5.3,5.3C10.1,14.3,9.9,14.4,9.7,14.4z"/></g></svg>';

renderTodoList();

// User clicked on the add button
// If there is any text inside the item field, add that text to the todo list
document.getElementById('add').addEventListener('click', function() {
  var value = document.getElementById('item').value;
  if (value) {
    addItem(value);
  }
});

document.getElementById('item').addEventListener('keydown', function (e) {
  var value = this.value;
  if (e.code === 'Enter' && value) {
    addItem(value);
  }
});

function addItem (value) {
  addItemToDOM(value);
  document.getElementById('item').value = '';

  data.todo.push(value);
  dataObjectUpdated();
}

function renderTodoList() {
  if (!data.todo.length && !data.completed.length) return;

  for (var i = 0; i < data.todo.length; i++) {
    var value = data.todo[i];
    addItemToDOM(value);
  }

  for (var j = 0; j < data.completed.length; j++) {
    var value = data.completed[j];
    addItemToDOM(value, true);
  }
}

function dataObjectUpdated() {
  localStorage.setItem('todoList', JSON.stringify(data));
}

function removeItem() {
  var item = this.parentNode.parentNode;
  var parent = item.parentNode;
  var id = parent.id;
  var value = item.innerText;

  if (id === 'todo') {
    data.todo.splice(data.todo.indexOf(value), 1);
  } else {
    data.completed.splice(data.completed.indexOf(value), 1);
  }
  dataObjectUpdated();

  parent.removeChild(item);
}

function completeItem() {
  var item = this.parentNode.parentNode;
  var parent = item.parentNode;
  var id = parent.id;
  var value = item.innerText;

  if (id === 'todo') {
    data.todo.splice(data.todo.indexOf(value), 1);
    data.completed.push(value);
  } else {
    data.completed.splice(data.completed.indexOf(value), 1);
    data.todo.push(value);
  }
  dataObjectUpdated();

  // Check if the item should be added to the completed list or to re-added to the todo list
  var target = (id === 'todo') ? document.getElementById('completed'):document.getElementById('todo');

  parent.removeChild(item);
  target.insertBefore(item, target.childNodes[0]);
}

// Adds a new item to the todo list
function addItemToDOM(text, completed) {
  var list = (completed) ? document.getElementById('completed'):document.getElementById('todo');

  var item = document.createElement('li');
  item.innerText = text;

  var buttons = document.createElement('div');
  buttons.classList.add('buttons');

  var remove = document.createElement('button');
  remove.classList.add('remove');
  remove.innerHTML = removeSVG;

  // Add click event for removing the item
  remove.addEventListener('click', removeItem);

  var complete = document.createElement('button');
  complete.classList.add('complete');
  complete.innerHTML = completeSVG;

  // Add click event for completing the item
  complete.addEventListener('click', completeItem);

  buttons.appendChild(remove);
  buttons.appendChild(complete);
  item.appendChild(buttons);

  list.insertBefore(item, list.childNodes[0]);
}
<|endoftext|>


<commit_before>let parkingApp = require('../parkingApp');

describe('An app used to determine where anyone can park at James Madison University', () => {

	it('can perform a smoke test', () => {
		expect(true).toBe(true);
	});

	it("throws an error if given bad input", () => {
		expect(parkingApp.bind(null)).not.toThrowError("Input must be Resident, Commuter, Visitor or Staff");
	});

	it("will list the parking lot zones for a user's status", () => {
		expect(parkingApp("Resident")).toBe("You can park in the following zones: R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14");
		expect(parkingApp("Commuter")).toBe("You can park in the following zones: C1 Lot,C2 Lot,C3 Lot,C4 Lot,C6 Lot,C7 Lot,C8 Lot,C9 Lot,C10 Lot,C11 Lot,C12 Lot,C13 Lot,C14 Lot,C15 Lot,C16 Lot,C17 Lot,C18 Lot,C19 Lot,Champions Drive Deck Level 2,Champions Drive Deck Level 3,Champions Drive Deck Level 4,Champions Drive Deck Level 5,Convo A,Convo B/E,Convo D,Convo F,Convo G,Mason Street Deck Commuter,U1 Lot,U2 Lot,U3 Lot,U4 Lot,U5 Lot,Warsaw Ave Deck Level 2 commuter,Warsaw Ave Deck Level 3,Warsaw Ave Deck Level 4,Warsaw Ave Deck Level 5,U5 Lot,Warsaw Ave Deck Level 2 commuter,Warsaw Ave Deck Level 3,Warsaw Ave Deck Level 4,Warsaw Ave Deck Level 5");
		expect(parkingApp("Visitor")).toBe("Parking Services provides visitors with parking permits at no charge during our normal business hours (7:00 AM - 5:00 PM Monday through Friday). Visitors who arrive at times other than those specified above should park in R1, R2, or R4 Lots and obtain a permit from Parking Services as soon as possible. (If a visitor receives a citation in the specified lots, they should contact Parking Services during business hours.) Visitors are not required to display permits after 3:00 PM on Friday through 7:00 AM Monday and may park in Commuter and Resident lots.");
	});
});
<commit_msg>Update parking_spec.js<commit_after>let parkingApp = require('../parkingApp');

describe('An app used to determine where anyone can park at James Madison University', () => {

	it('can perform a smoke test', () => {
		expect(true).toBe(true);
	});

	it("throws an error if given bad input", () => {
		expect(parkingApp.bind(null)).not.toThrowError("Input must be Resident, Commuter, Visitor or Staff");
		expect(parkingApp.bind(" ")).not.toThrowError("Input must be Resident, Commuter, Visitor or Staff");
		expect(parkingApp.bind("blah blah blah")).not.toThrowError("Input must be Resident, Commuter, Visitor or Staff");
	});

	it("will list the parking lot zones for a user's status", () => {
		expect(parkingApp("Resident")).toBe("You can park in the following zones: R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14");
		expect(parkingApp("Commuter")).toBe("You can park in the following zones: C1 Lot,C2 Lot,C3 Lot,C4 Lot,C6 Lot,C7 Lot,C8 Lot,C9 Lot,C10 Lot,C11 Lot,C12 Lot,C13 Lot,C14 Lot,C15 Lot,C16 Lot,C17 Lot,C18 Lot,C19 Lot,Champions Drive Deck Level 2,Champions Drive Deck Level 3,Champions Drive Deck Level 4,Champions Drive Deck Level 5,Convo A,Convo B/E,Convo D,Convo F,Convo G,Mason Street Deck Commuter,U1 Lot,U2 Lot,U3 Lot,U4 Lot,U5 Lot,Warsaw Ave Deck Level 2 commuter,Warsaw Ave Deck Level 3,Warsaw Ave Deck Level 4,Warsaw Ave Deck Level 5,U5 Lot,Warsaw Ave Deck Level 2 commuter,Warsaw Ave Deck Level 3,Warsaw Ave Deck Level 4,Warsaw Ave Deck Level 5");
		expect(parkingApp("Visitor")).toBe("Parking Services provides visitors with parking permits at no charge during our normal business hours (7:00 AM - 5:00 PM Monday through Friday). Visitors who arrive at times other than those specified above should park in R1, R2, or R4 Lots and obtain a permit from Parking Services as soon as possible. (If a visitor receives a citation in the specified lots, they should contact Parking Services during business hours.) Visitors are not required to display permits after 3:00 PM on Friday through 7:00 AM Monday and may park in Commuter and Resident lots.");
	});
});
<|endoftext|>


<commit_before>// Load in dependencies
var assert = require('assert'),
    moment = require('moment'),
    jojo = require('jojo'),
    marked = require('marked');

// Create a jojo middleware (and only use it to get articles from)
var articles = jojo({
  formatter: function (md) {
    return marked.parse(md, {langPrefix: ''});
  },
  render: false,
  author: 'Todd Wolfson',
  title: 'Todd Wolfson - Javascript Developer',
  url: 'http://twolfson.com/'
}).articles;

// Add a formatted date for each article
articles.forEach(function (article) {
  var date = article.date,
      dateStr = moment(date).format('MMMM DD, YYYY');
  article.dateStr = dateStr;
});

// Add a plain text summary for each article
var unhtml = require('unhtml');
articles.forEach(function (article) {
  if (!article.txtSummary) {
    article.txtSummary = unhtml(article.summary).trim().replace(/\n/g, ' ');
  }
});

// Calculate reading speed for each article
// DEV: Wiki says 250 words per minute so this is very generous timing
// DEV: Currently, this is 2 minutes for free will and 9 minutes for develop faster
// TODO: Put reading time in RSS
var READING_SPEED = 100; // words per minute
articles.forEach(function (article) {
  // TODO: Make this more efficient by iterating rather than splitting
  var txtContent = unhtml(article.content),
      words = txtContent.split(/\s+/g).filter(function (word) {
        // Remove strings without alphanumeric characters (e.g. `+`)
        return word.match(/[A-Za-z0-9]/);
      }),
      wordCount = words.length,
      readingTime = Math.round(wordCount / READING_SPEED);
  article.readingTime = Math.max(readingTime, 1);
});

// Construct objects to look up articles and projects by
// TODO: This is not the correct place to map relationships as projects can never read articles now =(
var articleObj = {};
articles.forEach(function (article) {
  articleObj[article.title] = article;
});
var projects = require('../lib/projects'),
    projectObj = {};
Object.getOwnPropertyNames(projects).forEach(function (namespace) {
  var projectsArr = projects[namespace];
  projectsArr.forEach(function (project) {
    projectObj[project.name] = project;
  });
});

// Find and assert all related articles, related projects
var TOP_ARTICLES = [
  articleObj['Develop faster'],
  articleObj['Sexy bash prompt']
];
TOP_ARTICLES.forEach(assert);
articles.forEach(function (article) {
  // Collect related items
  var relatedItems = [];

  // If we mistyped keys, warn yourself
  Object.getOwnPropertyNames(article).forEach(function (key) {
    if (key.match(/related/i) && !key.match(/^related(Articles|Projects)$/)) {
      console.warn('Article "' + article.name + '" has property "' + key + '" (not `relatedArticles`, `relatedProjects`)');
    }
  });

  // Find related articles
  // TODO: Prevent any related article to include the active one
  var relatedArticleTitles = article.relatedArticles;
  if (relatedArticleTitles) {
    var relatedArticles = relatedArticleTitles.map(function (title) {
      var relatedArticle = articleObj[title];
      assert(relatedArticle, 'Could not locate related article "' + title + '" for "' + article.title + '"');
      return relatedArticle;
    });
    article.relatedArticles = relatedArticles;
    relatedItems.push.apply(relatedItems, relatedArticles);
  }

  // Find related projects
  var relatedProjectNames = article.relatedProjects;
  if (relatedProjectNames) {
    var relatedProjects = relatedProjectNames.map(function (name) {
      var relatedProject = projectObj[name];
      assert(relatedProject, 'Could not locate related project "' + name + '" for "' + article.title + '"');
      return relatedProject;
    });
    article.relatedProjects = relatedProjects;
    relatedItems.push.apply(relatedItems, relatedProjects);
  }

  // If there are not enough related items, add features projects
  if (relatedItems.length < 3) {
    var topArticles = TOP_ARTICLES.filter(function (article) {
      return relatedItems.indexOf(article) === -1;
    }).slice(0, 3 - relatedItems.length);
    article.topArticles = topArticles;
    relatedItems.push.apply(relatedItems, topArticles);
  }

  // If there are not enough related items, fill in recent articles
  if (relatedItems.length < 3) {
    var recentArticles = articles.filter(function (article) {
      return relatedItems.indexOf(article) === -1;
    }).slice(0, 3 - relatedItems.length);
    article.recentArticles = recentArticles;
    relatedItems.push.apply(relatedItems, recentArticles);
  }

  // Assert as a safeguard for articles
  assert.strictEqual(relatedItems.length, 3, 'Related items is not at 3 for "' + article.title + '"');
});

// Expose the articles
module.exports = articles;
<commit_msg>Added filtering of active article<commit_after>// Load in dependencies
var assert = require('assert'),
    moment = require('moment'),
    jojo = require('jojo'),
    marked = require('marked');

// Create a jojo middleware (and only use it to get articles from)
var articles = jojo({
  formatter: function (md) {
    return marked.parse(md, {langPrefix: ''});
  },
  render: false,
  author: 'Todd Wolfson',
  title: 'Todd Wolfson - Javascript Developer',
  url: 'http://twolfson.com/'
}).articles;

// Add a formatted date for each article
articles.forEach(function (article) {
  var date = article.date,
      dateStr = moment(date).format('MMMM DD, YYYY');
  article.dateStr = dateStr;
});

// Add a plain text summary for each article
var unhtml = require('unhtml');
articles.forEach(function (article) {
  if (!article.txtSummary) {
    article.txtSummary = unhtml(article.summary).trim().replace(/\n/g, ' ');
  }
});

// Calculate reading speed for each article
// DEV: Wiki says 250 words per minute so this is very generous timing
// DEV: Currently, this is 2 minutes for free will and 9 minutes for develop faster
// TODO: Put reading time in RSS
var READING_SPEED = 100; // words per minute
articles.forEach(function (article) {
  // TODO: Make this more efficient by iterating rather than splitting
  var txtContent = unhtml(article.content),
      words = txtContent.split(/\s+/g).filter(function (word) {
        // Remove strings without alphanumeric characters (e.g. `+`)
        return word.match(/[A-Za-z0-9]/);
      }),
      wordCount = words.length,
      readingTime = Math.round(wordCount / READING_SPEED);
  article.readingTime = Math.max(readingTime, 1);
});

// Construct objects to look up articles and projects by
// TODO: This is not the correct place to map relationships as projects can never read articles now =(
var articleObj = {};
articles.forEach(function (article) {
  articleObj[article.title] = article;
});
var projects = require('../lib/projects'),
    projectObj = {};
Object.getOwnPropertyNames(projects).forEach(function (namespace) {
  var projectsArr = projects[namespace];
  projectsArr.forEach(function (project) {
    projectObj[project.name] = project;
  });
});

// Find and assert all related articles, related projects
var TOP_ARTICLES = [
  articleObj['Develop faster'],
  articleObj['Sexy bash prompt']
];
TOP_ARTICLES.forEach(assert);
articles.forEach(function (article) {
  // Collect related items
  var relatedItems = [];

  // If we mistyped keys, warn yourself
  Object.getOwnPropertyNames(article).forEach(function (key) {
    if (key.match(/related/i) && !key.match(/^related(Articles|Projects)$/)) {
      console.warn('Article "' + article.name + '" has property "' + key + '" (not `relatedArticles`, `relatedProjects`)');
    }
  });

  function isNotCurrentArticle(item) {
    return item !== article;
  }

  function isUnrelatedItem(item) {
    return relatedItems.indexOf(item) === -1;
  }

  // Find related articles
  var relatedArticleTitles = article.relatedArticles;
  if (relatedArticleTitles) {
    var relatedArticles = relatedArticleTitles.map(function (title) {
      var relatedArticle = articleObj[title];
      assert(relatedArticle, 'Could not locate related article "' + title + '" for "' + article.title + '"');
      return relatedArticle;
    }).filter(isNotCurrentArticle);
    article.relatedArticles = relatedArticles;
    relatedItems.push.apply(relatedItems, relatedArticles);
  }

  // Find related projects
  var relatedProjectNames = article.relatedProjects;
  if (relatedProjectNames) {
    var relatedProjects = relatedProjectNames.map(function (name) {
      var relatedProject = projectObj[name];
      assert(relatedProject, 'Could not locate related project "' + name + '" for "' + article.title + '"');
      return relatedProject;
    });
    article.relatedProjects = relatedProjects;
    relatedItems.push.apply(relatedItems, relatedProjects);
  }

  // If there are not enough related items, add features projects
  if (relatedItems.length < 3) {
    var topArticles = TOP_ARTICLES
                        .filter(isNotCurrentArticle)
                        .filter(isUnrelatedItem)
                        .slice(0, 3 - relatedItems.length);
    article.topArticles = topArticles;
    relatedItems.push.apply(relatedItems, topArticles);
  }

  // If there are not enough related items, fill in recent articles
  if (relatedItems.length < 3) {
    var recentArticles = articles
                          .filter(isNotCurrentArticle)
                          .filter(isUnrelatedItem)
                          .slice(0, 3 - relatedItems.length);
    article.recentArticles = recentArticles;
    relatedItems.push.apply(relatedItems, recentArticles);
  }

  // Assert as a safeguard for articles
  assert.strictEqual(relatedItems.length, 3, 'Related items is not at 3 for "' + article.title + '"');
});

// Expose the articles
module.exports = articles;
<|endoftext|>


<commit_before>/**
 * Created by djarosz on 11/6/15.
 */
/**
 * The purpose of this file is to store all functions and utilities that are used by multiple routes.
 */

function filterBody(strings) {
    return function (req, res, next) {
        var k, found = false;
        for (k in req.body) {
            if (req.body.hasOwnProperty(k)) {
                if (strings.indexOf(k) !== -1) {
                    found = true;
                } else {
                    req.body[k] = null;
                }
            }
        }
        if (found) {
            next();
        } else {
            return res.send(400, 'cannot find required information in body');
        }
    };
}

module.exports = {
    filterBody: filterBody
};<commit_msg>Add a utility that checks if a user has a certain role<commit_after>/**
 * Created by djarosz on 11/6/15.
 */
/**
 * The purpose of this file is to store all functions and utilities that are used by multiple routes.
 */

function filterBody(strings) {
    return function (req, res, next) {
        var k, found = false;
        for (k in req.body) {
            if (req.body.hasOwnProperty(k)) {
                if (strings.indexOf(k) !== -1) {
                    found = true;
                } else {
                    req.body[k] = null;
                }
            }
        }
        if (found) {
            next();
        } else {
            return res.send(400, 'cannot find required information in body');
        }
    };
}

function checkUserRole(req, role){
    if(req.session.roles != undefined && req.session.roles.indexOf(role) != -1) {
        return true;
    }
    else {
        return false;
    }
}

module.exports = {
    filterBody: filterBody,
    checkUserRole: checkUserRole
};<|endoftext|>