<commit_before>/* This gets the data for the total people currently online and the sparkline */
(function(root){
  "use strict"
  var debug = 0;
  var debugCountLimit = 1000;
  var debugRand = function() {
    return (Math.random() * debugCountLimit) | 0;
  };

  var parseRows = function(rows) {
    var rowObj = {},
    length = rows.length;
    for(var i=0;i<length;i++){
      rowObj[rows[i][0]] = rows[i][1];
    }
    return rowObj;
  }

  if(typeof root.matrix === 'undefined'){ root.matrix = {} }

  var traffic = {
    el: false,
    points: 720,
    // FIXME(slightlyoff):
    //    since we don't have the GDS traffic middleware, be dumb: keep a
    //    trailing log of counts in memory. Would be better to store on server.
    counts: [],
    // Every half second in debug mode or every 2 minutes when charting fer
    // realz.
    interval: (debug) ? 500 : 2 * 60 * 1000,

    endpoint: function(){
      return "/realtime?ids=ga:"+matrix.settings.profileId+"&metrics=rt:activeUsers&dimensions=rt:deviceCategory&max-results=10"
    },
    historic: function(){
      return "/historic?ids=ga:"+matrix.settings.profileId+"&dimensions=ga%3AdeviceCategory,ga%3Adate,ga%3Ahour,ga%3Aminute&metrics=ga%3Asessions&start-date=2daysAgo&end-date=today&max-results=10000"
    },
    parseResponse: function(data){
      if(data && data.hasOwnProperty('rows')){
        var users = parseRows(data.rows);
        var activeUsers = parseInt(users['DESKTOP'], 10) | 0;
        var activeUsersMobile = parseInt(users['MOBILE'], 10) | 0;
        traffic.el.innerText = activeUsers;
        traffic.elMob.innerText = activeUsersMobile;

        var dataArray = helper.arrayFromObject(traffic.counts);
        if(typeof traffic.chart === 'undefined'){
          traffic.chart = new Morris.Bar({
            data: dataArray,
            element: 'traffic-count-graph',
            xkey: 'date',
            ykeys: ['desktop', 'mobile'],
            labels: ['Desktop', 'Mobile'],
            stacked: true,
            barColors: ["#1B406D", "#265C8D"],
            hideHover: 'always',
            xLabelMargin: 100,
            xLabelFormat: function(data){
              return d3.time.format("%I %p")(data.label);
            },
          });
        }
        traffic.chart.setData(dataArray);
      }
    },
    init: function(){
      traffic.el = document.getElementById('traffic-count');
      traffic.elMob = document.getElementById('traffic-count-mobile');
      traffic.graphEl = document.getElementById('traffic-count-graph');
      traffic.counts.length = traffic.points;

      // Check the traffic intermittently
      traffic.loadHistory();
      window.setInterval(traffic.loadHistory, traffic.interval*4);
      window.setInterval(traffic.reload, traffic.interval);
    },
    loadHistory: function() {
      d3.json(traffic.historic(), function(error, json) {
        if (error) return console.warn(error);
        var endDate = new Date();
        var startDate = d3.time.day.offset(endDate, -2);
        traffic.counts = window.helper.deviceMinuteIntervalResults(json.rows, 30, startDate, endDate);
        traffic.reload();
      });
    },
    reload: function(){
      var endpoint = traffic.endpoint();

      if (debug) {
        traffic.parseResponse({
          totalsForAllResults: {
            'rt:activeUsers': debugRand()
          }
        });
        return;
      }
      d3.json(endpoint, traffic.parseResponse);

    }
  };

  root.matrix.traffic = traffic;
}).call(this, this);
<commit_msg>Use innerHTML to set active user count<commit_after>/* This gets the data for the total people currently online and the sparkline */
(function(root){
  "use strict"
  var debug = 0;
  var debugCountLimit = 1000;
  var debugRand = function() {
    return (Math.random() * debugCountLimit) | 0;
  };

  var parseRows = function(rows) {
    var rowObj = {},
    length = rows.length;
    for(var i=0;i<length;i++){
      rowObj[rows[i][0]] = rows[i][1];
    }
    return rowObj;
  }

  if(typeof root.matrix === 'undefined'){ root.matrix = {} }

  var traffic = {
    el: false,
    points: 720,
    // FIXME(slightlyoff):
    //    since we don't have the GDS traffic middleware, be dumb: keep a
    //    trailing log of counts in memory. Would be better to store on server.
    counts: [],
    // Every half second in debug mode or every 2 minutes when charting fer
    // realz.
    interval: (debug) ? 500 : 2 * 60 * 1000,

    endpoint: function(){
      return "/realtime?ids=ga:"+matrix.settings.profileId+"&metrics=rt:activeUsers&dimensions=rt:deviceCategory&max-results=10"
    },
    historic: function(){
      return "/historic?ids=ga:"+matrix.settings.profileId+"&dimensions=ga%3AdeviceCategory,ga%3Adate,ga%3Ahour,ga%3Aminute&metrics=ga%3Asessions&start-date=2daysAgo&end-date=today&max-results=10000"
    },
    parseResponse: function(data){
      if(data && data.hasOwnProperty('rows')){
        var users = parseRows(data.rows);
        var activeUsers = parseInt(users['DESKTOP'], 10) | 0;
        var activeUsersMobile = parseInt(users['MOBILE'], 10) | 0;
        traffic.el.innerHTML = activeUsers;
        traffic.elMob.innerHTML = activeUsersMobile;

        var dataArray = helper.arrayFromObject(traffic.counts);
        if(typeof traffic.chart === 'undefined'){
          traffic.chart = new Morris.Bar({
            data: dataArray,
            element: 'traffic-count-graph',
            xkey: 'date',
            ykeys: ['desktop', 'mobile'],
            labels: ['Desktop', 'Mobile'],
            stacked: true,
            barColors: ["#1B406D", "#265C8D"],
            hideHover: 'always',
            xLabelMargin: 100,
            xLabelFormat: function(data){
              return d3.time.format("%I %p")(data.label);
            },
          });
        }
        traffic.chart.setData(dataArray);
      }
    },
    init: function(){
      traffic.el = document.getElementById('traffic-count');
      traffic.elMob = document.getElementById('traffic-count-mobile');
      traffic.graphEl = document.getElementById('traffic-count-graph');
      traffic.counts.length = traffic.points;

      // Check the traffic intermittently
      traffic.loadHistory();
      window.setInterval(traffic.loadHistory, traffic.interval*4);
      window.setInterval(traffic.reload, traffic.interval);
    },
    loadHistory: function() {
      d3.json(traffic.historic(), function(error, json) {
        if (error) return console.warn(error);
        var endDate = new Date();
        var startDate = d3.time.day.offset(endDate, -2);
        traffic.counts = window.helper.deviceMinuteIntervalResults(json.rows, 30, startDate, endDate);
        traffic.reload();
      });
    },
    reload: function(){
      var endpoint = traffic.endpoint();

      if (debug) {
        traffic.parseResponse({
          totalsForAllResults: {
            'rt:activeUsers': debugRand()
          }
        });
        return;
      }
      d3.json(endpoint, traffic.parseResponse);

    }
  };

  root.matrix.traffic = traffic;
}).call(this, this);
<|endoftext|>


<commit_before>var Q = require('q');
var path = require('path');
var childProcess = require('child_process');

exports.exit = function exit(code) {
  if (exports.exit.pause_on_exit) {
    exports.waitForKey(function() {
      process.exit(code);
    });
  } else {
    process.exit(code);
  }
};
exports.exit.pause_on_exit = false;

exports.fatal = function fatal(msg) {
  console.error(msg);
  if (msg.stack) console.error(msg.stack);
  exit(1);
};

exports.fixPathSlashes = function fixPathSlashes(p) {
  return exports.isWindows() ? p.replace(/\//g, '\\') : p;
};

exports.colorizeConsole = function colorizeConsole() {
  var origWarn = console.warn;
  console.warn = function() {
    var msg = [].slice.call(arguments).join(' ');
    origWarn.call(console, '\x1B[33m' + msg + '\x1B[39m');
  };
  console.error = function() {
    var msg = [].slice.call(arguments).join(' ');
    origWarn.call(console, '\x1B[31m' + msg + '\x1B[39m');
  };
};

exports.assetDirForPlatform = function assetDirForPlatform(platform) {
  if (platform === 'android') {
    return path.join('platforms', platform, 'assets','www');
  }
  return path.join('platforms', platform, 'www');
};

// Returns a promise for an object with 'stdout' and 'stderr' as keys.
exports.exec = function exec(cmd, opt_silent) {
  if (!opt_silent) {
    console.log('Running: ' + cmd);
  }
  var d = Q.defer();
  childProcess.exec(cmd, function(error, stdout, stderr) {
    if (error) {
      d.reject(error);
    } else {
      d.resolve({
        stdout: stdout.trim(),
        stderr: stderr.trim()
      });
    }
  });
  return d.promise;
};

// Returns a promise with the key as its value.
exports.waitForKey = function waitForKey(opt_prompt) {
  opt_prompt = opt_prompt || 'Press the Any Key';
  process.stdout.write(opt_prompt);
  process.stdin.resume();
  try {
    // This fails if the process is a spawned child (likely a node bug);
    process.stdin.setRawMode(true);
  } catch (e) {
  }
  process.stdin.setEncoding('utf8');
  var d = Q.defer();
  process.stdin.on('data', function cont(key) {
    if (key == '\u0003') {
      process.exit(2);
    }
    process.stdin.removeListener('data', cont);
    process.stdin.pause();
    process.stdout.write('\n');
    d.resolve(key);
  });
  return d.promise;
};

exports.isWindows = function isWindows() {
  return process.platform.slice(0, 3) == 'win';
};
<commit_msg>minor bugfix: use utils.exit() not exit()<commit_after>var Q = require('q');
var path = require('path');
var childProcess = require('child_process');

exports.exit = function exit(code) {
  if (exports.exit.pause_on_exit) {
    exports.waitForKey(function() {
      process.exit(code);
    });
  } else {
    process.exit(code);
  }
};
exports.exit.pause_on_exit = false;

exports.fatal = function fatal(msg) {
  console.error(msg);
  if (msg.stack) console.error(msg.stack);
  exports.exit(1);
};

exports.fixPathSlashes = function fixPathSlashes(p) {
  return exports.isWindows() ? p.replace(/\//g, '\\') : p;
};

exports.colorizeConsole = function colorizeConsole() {
  var origWarn = console.warn;
  console.warn = function() {
    var msg = [].slice.call(arguments).join(' ');
    origWarn.call(console, '\x1B[33m' + msg + '\x1B[39m');
  };
  console.error = function() {
    var msg = [].slice.call(arguments).join(' ');
    origWarn.call(console, '\x1B[31m' + msg + '\x1B[39m');
  };
};

exports.assetDirForPlatform = function assetDirForPlatform(platform) {
  if (platform === 'android') {
    return path.join('platforms', platform, 'assets','www');
  }
  return path.join('platforms', platform, 'www');
};

// Returns a promise for an object with 'stdout' and 'stderr' as keys.
exports.exec = function exec(cmd, opt_silent) {
  if (!opt_silent) {
    console.log('Running: ' + cmd);
  }
  var d = Q.defer();
  childProcess.exec(cmd, function(error, stdout, stderr) {
    if (error) {
      d.reject(error);
    } else {
      d.resolve({
        stdout: stdout.trim(),
        stderr: stderr.trim()
      });
    }
  });
  return d.promise;
};

// Returns a promise with the key as its value.
exports.waitForKey = function waitForKey(opt_prompt) {
  opt_prompt = opt_prompt || 'Press the Any Key';
  process.stdout.write(opt_prompt);
  process.stdin.resume();
  try {
    // This fails if the process is a spawned child (likely a node bug);
    process.stdin.setRawMode(true);
  } catch (e) {
  }
  process.stdin.setEncoding('utf8');
  var d = Q.defer();
  process.stdin.on('data', function cont(key) {
    if (key == '\u0003') {
      process.exit(2);
    }
    process.stdin.removeListener('data', cont);
    process.stdin.pause();
    process.stdout.write('\n');
    d.resolve(key);
  });
  return d.promise;
};

exports.isWindows = function isWindows() {
  return process.platform.slice(0, 3) == 'win';
};
<|endoftext|>


<commit_before>define([], function() {
    if (typeof(config.analytics) == 'undefined'
        || typeof(config.analytics.google) == 'undefined'
        || !config.analytics.google) return;

    var _gaq=[['_setAccount',config.analytics.google],['_trackPageview']];
        (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
        g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
        s.parentNode.insertBefore(g,s)}(document,'script'));
});<commit_msg>Should fix google analytics tracking. Fixes #23<commit_after>define([], function() {
    if (typeof(config.analytics) == 'undefined'
        || typeof(config.analytics.google) == 'undefined'
        || !config.analytics.google) return;
console.log('analyics tracking code', config.analytics.google)

    window._gaq = window._gaq || [];
    window._gaq.push(['_setAccount', config.analytics.google]);
    window._gaq.push(['_setAllowLinker', true]);
    window._gaq.push(['_trackPageview']);

    (function() {
        var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    })();
});<|endoftext|>


<commit_before>export function createSnapshot (doc) {
  // defaults everything to false, so no need to set
  return Object.defineProperty(doc.data(), 'id', {
    value: doc.id,
  })
}

const isObject = o => o && typeof o === 'object'

export function extractRefs (doc, oldDoc, path = '', result = [{}, {}]) {
  // must be set here because walkGet can return null or undefined
  oldDoc = oldDoc || {}
  const idDescriptor = Object.getOwnPropertyDescriptor(doc, 'id')
  if (idDescriptor && !idDescriptor.enumerable) {
    Object.defineProperty(result[0], 'id', idDescriptor)
  }
  return Object.keys(doc).reduce((tot, key) => {
    const ref = doc[key]
    // if it's a ref
    if (ref && typeof ref.isEqual === 'function') {
      tot[0][key] = oldDoc[key] || ref.path
      tot[1][path + key] = ref
    } else if (Array.isArray(ref)) {
      tot[0][key] = Array(ref.length).fill(null)
      extractRefs(ref, oldDoc[key], path + key + '.', [tot[0][key], tot[1]])
    } else if (isObject(ref)) {
      tot[0][key] = {}
      extractRefs(ref, oldDoc[key], path + key + '.', [tot[0][key], tot[1]])
    } else {
      tot[0][key] = ref
    }
    return tot
  }, result)
}

export function callOnceWithArg (fn, argFn) {
  let called
  return () => {
    if (!called) {
      called = true
      return fn(argFn())
    }
  }
}

export function walkGet (obj, path) {
  return path.split('.').reduce((target, key) => target[key], obj)
}

export function walkSet (obj, path, value) {
  // path can be a number
  const keys = ('' + path).split('.')
  const key = keys.pop()
  const target = keys.reduce((target, key) => target[key], obj)
  // global isFinite is different from Number.isFinite
  // it converts values to numbers
  if (isFinite(key)) target.splice(key, 1, value)
  else target[key] = value
}
<commit_msg>🐛 fix dates and geopoints<commit_after>export function createSnapshot (doc) {
  // defaults everything to false, so no need to set
  return Object.defineProperty(doc.data(), 'id', {
    value: doc.id,
  })
}

const isObject = o => o && typeof o === 'object'

export function extractRefs (doc, oldDoc, path = '', result = [{}, {}]) {
  // must be set here because walkGet can return null or undefined
  oldDoc = oldDoc || {}
  const idDescriptor = Object.getOwnPropertyDescriptor(doc, 'id')
  if (idDescriptor && !idDescriptor.enumerable) {
    Object.defineProperty(result[0], 'id', idDescriptor)
  }
  return Object.keys(doc).reduce((tot, key) => {
    const ref = doc[key]
    // if it's a ref
    if (ref && typeof ref.isEqual === 'function') {
      tot[0][key] = oldDoc[key] || ref.path
      tot[1][path + key] = ref
    } else if (Array.isArray(ref)) {
      tot[0][key] = Array(ref.length).fill(null)
      extractRefs(ref, oldDoc[key], path + key + '.', [tot[0][key], tot[1]])
    } else if (
      ref instanceof Date ||
      (ref.longitude && ref.latitude) // GeoPoint
    ) {
      tot[0][key] = ref
    } else if (isObject(ref)) {
      tot[0][key] = {}
      extractRefs(ref, oldDoc[key], path + key + '.', [tot[0][key], tot[1]])
    } else {
      tot[0][key] = ref
    }
    return tot
  }, result)
}

export function callOnceWithArg (fn, argFn) {
  let called
  return () => {
    if (!called) {
      called = true
      return fn(argFn())
    }
  }
}

export function walkGet (obj, path) {
  return path.split('.').reduce((target, key) => target[key], obj)
}

export function walkSet (obj, path, value) {
  // path can be a number
  const keys = ('' + path).split('.')
  const key = keys.pop()
  const target = keys.reduce((target, key) => target[key], obj)
  // global isFinite is different from Number.isFinite
  // it converts values to numbers
  if (isFinite(key)) target.splice(key, 1, value)
  else target[key] = value
}
<|endoftext|>


<commit_before>/*jshint browser: true, globalstrict: true*/
/*global angular, console*/
'use strict';

function StatsCtrl($scope, $rootScope, $route, $http, socket, resolvedData) {
  // A hack to short circuit the routechange so that the controller doesn't get
  //  reloaded on path change
  var lastRoute = $route.current;
  $scope.$on('$locationChangeSuccess', function(event) {
    // Only do it for the stats path.
    if ($route.current.$route &&
        $route.current.$route.templateUrl === 'partials/stats') {
    // if (lastRoute && $route.current.$route.templateUrl === 'partials/stats') {
      // Grab the new matchid before we stop the routechange
      var matchId = $route.current.params.id;
      // This stops the routechange from succeeding
      $route.current = lastRoute;
      $route.current.params.id = matchId;

      $http({
        method: 'GET',
        url: '/api/stats/' + matchId
      }).success(function(data) {
        $rootScope.loading = false;
        parseStats(data, true);
      });

      socket.emit('stats:subscribe', matchId);
    }
  });

  socket.on('stats:update', function (data) {
    console.log('Stat update received!');
    parseStats(data, false);
  });

  function Player(data) {
    for (var key in data) {
      this[key] = data[key];
    }
  }
  Player.prototype.sumOf = function(statistic) {
    return sumArray2(this[statistic]);
  };
  Player.prototype.perMinute = function(statistic) {
    return ratio($scope.playableTime/60, this[statistic]);
  };
  Player.prototype.fapd = function() {
    return ratio(this.deaths, this.kills, this.killassists);
  };
  Player.prototype.mostPlayedClass = function() {
      if (this.mostplayedclass.length == 1) {
        return this.mostplayedclass[0];
      }
      // Determine what class was played the most by summing the rounddurations
      //  for the according tf2class in the "mostplayedclass" array.
      var totals = [0,0,0,0,0,0,0,0,0,0];
      for (var i=0, ilen=$scope.selectedRounds.length; i<ilen; i++) {
        // mostplayedclass[r] is the id (1-9) of the tf2class
        //  that the player played the most in the ith round.
        // The index of the max value in totals[] is the id of the tf2class
        //  that the player played the most in the match.
        var r = $scope.selectedRounds[i];
        totals[ this.mostplayedclass[r] ] += $scope.stats.roundduration[r];
      }
      // Find the index of the max value in totals[].
      var theClass=0, theMax=0;
      for (var j=0; j<=9; j++) {
        if (totals[j] > theMax) {
          theMax = totals[j];
          theClass = j;
        }
      }
      return theClass;
  };
  Player.prototype.playedClasses = function() {
    return filterBySelectedRounds(this.playedclasses)
             .reduce(function(a,b) { return a | b; },0);
  };

  $scope.overallStatsTableData = [
    ['Name', null, 'name']
  , ['C', 'Most Played Class', 'mostPlayedClass()']
  , ['P', 'Points', 'sumOf("points")']
  , ['FA/D', 'Frags+Assists Per Death', 'fapd()']
  , ['F', 'Frags', 'sumOf("kills")']
  , ['A', 'Assists', 'sumOf("killassists")']
  , ['D', 'Deaths', 'sumOf("deaths")']
  , ['S', 'Suicides', 'sumOf("suicides")']
  , ['DPM', 'Damage Per Minute', 'perMinute("damagedone")']
  , ['DMG', 'Damage', 'sumOf("damagedone")']
  , ['MP', 'Medic Picks', 'sumOf("medpicks")']
  , ['HR', 'Heals Received (Excl Buffs)', 'sumOf("healsreceived")']
  , ['CPC', 'Capture Points Captured', 'sumOf("captures")']
  , ['CPB', 'Capture Points Blocked', 'sumOf("defenses")']
  , ['DOM', 'Dominations', 'sumOf("dominations")']
  , ['REV', 'Revenges', 'sumOf("revenge")']
  // , ['', '', 'sumOf("buildingsdestroyed")']
  // , ['', '', 'sumOf("crits")']
  // , ['', '', 'sumOf("teleports")']
  // , ['', '', 'sumOf("resupplypoints")']
  // , ['', '', 'sumOf("bonuspoints")']
  ];
  $scope.medicStatsTableData = [
    ['Medics', null, 'name']
  , ['H', 'Heals Given (Incl Buffs)', 'sumOf("healpoints")']
  , ['U', 'Ubers', 'sumOf("invulns")']
  , ['UD', 'Ubers Dropped', 'sumOf("ubersdropped")']
  ];
  $scope.sniperStatsTableData = [
    ['Snipers', null, 'name']
  , ['HS', 'Headshot Kills', 'sumOf("headshots")']
  ];
  $scope.spyStatsTableData = [
    ['Spies', null, 'name']
  , ['BS', 'Backstabs', 'sumOf("backstabs")']
  ];
  $scope.engineerStatsTableData = [
    ['Engineers', null, 'name']
  , ['BB', 'Buildings Built', 'sumOf("buildingsbuilt")']
  ];

  var parseStats = function(data, reinitializeSelectedRounds) {
    // Stupid hack -- need to keep both a hash and and array of players,
    //  because AngularJS's orderBy function doesn't work on a hash.
    $scope.playersArr = [];
    $scope.players = {};

    // Stupid placeholder object for when things go wrong
    if (!data  || typeof data !== 'object') {
      data = { stats: {
        redname: 'RED'
      , bluname: 'BLU'
      , redscore: []
      , bluscore: []
      , players: {}
      }};
    }
    var stats = $scope.stats = data.stats;

    // If redscore.length is greater than the current number of rounds, then
    //  that means a new round just started -- add it to $scope.selectedRounds.
    if (stats.redscore.length > $scope.numRounds) {
      $scope.selectedRounds.push(stats.redscore.length-1);
    }

    var numRounds = $scope.numRounds = stats.redscore.length;

    if (reinitializeSelectedRounds) { $scope.initializeSelectedRoundsArray(); }

    var redScore = $scope.redScore =
                   stats.redscore.reduce(function(a,b) { return a + b; },0);
    var bluScore = $scope.bluScore =
                   stats.bluscore.reduce(function(a,b) { return a + b; },0);
    // Score Comparison
    $scope.scoreComparison = redScore > bluScore ?
                             '>' :
                             redScore < bluScore ?
                             '<' :
                             '==';

    // Total playable time
    var playableTime = $scope.playableTime = sumArray(stats.roundduration);

    // Construct player objects
    angular.forEach(stats.players, function(playerdata, steamid) {
      var player = $scope.players[steamid] = new Player(playerdata);
      // stupid hack
      $scope.playersArr.push(player);
    });

    // Calculate total midfights won for each team
    var totalMidfightsWon = [0,0,0,0];
    var filteredTeamfirstcapArr = filterBySelectedRounds(stats.teamfirstcap);
    for (var j=0; j<numRounds; j++) {
      totalMidfightsWon[ filteredTeamfirstcapArr[j] ] += 1;
    }

    // Calculate total damage and frags for each team
    var totalDamage = [0,0,0,0];
    var totalFrags = [0,0,0,0];
    angular.forEach($scope.players, function(player, steamid) {
      totalDamage[player.team] += sumArray(player.damagedone);
      totalFrags[player.team] += sumArray(player.kills);
    });

    // Assemble score overview table rows
    var redRoundScores = stats.redscore.concat(redScore,totalDamage[2]
                                             , totalFrags[2]
                                             , totalMidfightsWon[2]);
    var bluRoundScores = stats.bluscore.concat(bluScore,totalDamage[3]
                                             , totalFrags[3]
                                             , totalMidfightsWon[3]);
    $scope.redtr = '' + escapeHtml(stats.redname) +
                   '	' + redRoundScores.join('	') + '	';
    $scope.blutr = '' + escapeHtml(stats.bluname) +
                   '	' + bluRoundScores.join('	') + '	';
  };

  // Helpers

  $scope.selectedRounds = [];
  $scope.initializeSelectedRoundsArray = function() {
    $scope.selectedRounds = [];
    for (var i=0; i<$scope.numRounds; i++) {
      $scope.selectedRounds[i] = i;
    }
  };
  $scope.ctrl = false;
  window.onmouseup = function(e) {
    $scope.ctrl = !!e.ctrlKey;
  };
  $scope.clickRoundHeader = function(round) {
    if ($scope.selectedRounds.length === 0) {
      $scope.selectedRounds.push(round);
    } else if ($scope.selectedRounds.length === 1 &&
               $scope.selectedRounds[0] === round && !$scope.ctrl) {
      $scope.initializeSelectedRoundsArray();
    } else if ($scope.ctrl) {
      if ($scope.selectedRounds.indexOf(round) > -1) {
        $scope.selectedRounds.splice($scope.selectedRounds.indexOf(round),1);
      } else {
        $scope.selectedRounds.push(round);
      }
    } else {
      $scope.selectedRounds = [round];
    }
    $scope.selectedRounds.sort(function(a,b){return a-b;});
    parseStats({stats: $scope.stats, playerdata: $scope.playerMetaData}, false);
  };
  $scope.filterBinds = true;
  $scope.bindFilter = function(chat) {
    return (!$scope.filterBinds || !chat.isBind);
  };
  var sumArray = $scope.sumArray = function(arr) {
    if (!arr || !arr.length) return 0;
    return filterBySelectedRounds(arr).reduce(function(a,b) { return a + b; },0);
  };
  var sumArray2 = $scope.sumArray2 = function(arr) {
    if (!arr || !arr.length) return '-';
    var filteredArr = filterBySelectedRounds(arr);
    if (!filteredArr.length) return '-';
    return filteredArr.reduce(function(a,b) { return a + b; });
  };
  var ratio = $scope.ratio = function(den, numArray1, numArray2) {
    var numerator = sumArray2(numArray1) + sumArray(numArray2);
    var denominator = den.length ? sumArray2(den) : den;
    if (typeof numerator !== 'number') return '-';
    if (numerator === 0) return 0;
    if (denominator === 0) return '∞';
    return Math.round( (numerator/denominator)*100 )/100;
  };
  var filterBySelectedRounds = function(arr) {
    var filteredArr = [];
    for (var i=0, r; r=$scope.selectedRounds[i]+1; i++) {
      if (exists(arr[r-1])) { filteredArr.push(arr[r-1]); }
    }
    return filteredArr;
  };
  var exists = function(n) {
    return (n !== null && n !== undefined);
  };
  var escapeHtml = $scope.escapeHtml = (function () {
    var chr = { '"': '"', '&': '&', '<': '<', '>': '>' };
    return function (text) {
      return text.replace(/[\"&<>]/g, function (a) { return chr[a]; });
    };
  })();
  $scope.secondsToHMS = function(seconds) {
    var h = parseInt(seconds/3600, 10);
    var m = parseInt((seconds-h*3600)/60, 10);
    var s = seconds-h*3600-m*60;
    if (h === 0) { return ('0'+m).slice(-2)+':'+('0'+s).slice(-2); }
    return h+':'+('0'+m).slice(-2)+':'+('0'+s).slice(-2);
  };

  // The very first time you load the controller, use the resolvedData.
  parseStats(resolvedData, true);

  // hack. FIXME
  $rootScope.statsCtrlIsLoadedSoDontCallResolveFunction = true;
  $scope.$on('$destroy', function() {
    $rootScope.statsCtrlIsLoadedSoDontCallResolveFunction = false;
  });
}
// This is for the first time you load the controller
//  -- so that you don't see all the empty divs and tables.
StatsCtrl.resolve = {
  resolvedData: function($q, $http, $route, $rootScope) {
    // hack. FIXME
    if ($rootScope.statsCtrlIsLoadedSoDontCallResolveFunction) {
      // return;
    }
    var deferred = $q.defer();
    $http({
      method: 'GET',
      url: '/api/stats/' + $route.current.params.id
    })
      .success(function(data) {
        deferred.resolve(data);
      })
      .error(function(data) {
        window.alert('Something went wrong.');
        deferred.reject(data);
      });
    return deferred.promise;
  }
};

function StatsEditCtrl($scope, $location, $route, $http, socket, resolvedData) {
  $scope.data = {
    redname: resolvedData.stats.redname
  , bluname: resolvedData.stats.bluname
  };

  $scope.saveButton = function() {
    $http.put('/api/stats/' + resolvedData.stats._id, $scope.data)
      .success(function(data, status, headers, config) {
        // TODO: Do something better
        $location.path('/stats/' + resolvedData.stats._id);
      })
      .error(function(data, status, headers, config) {
        console.log(data);
        window.alert('Something went wrong.');
      });

  };

  $scope.deleteButton = function() {
    if ( !window.confirm('Are you sure you want to delete these stats?') ) {
      return false;
    }
    $http.delete('/api/stats/' + resolvedData.stats._id)
      .success(function(data, status, headers, config) {
        // TODO: Do something better
        $location.path( "/" );
      })
      .error(function(data, status, headers, config) {
        // TODO: Do something
        console.log(data);
        window.alert('Something went wrong.');
      });
  };

}
<commit_msg>Fixed client not subscribing to socket.io room on first load.<commit_after>/*jshint browser: true, globalstrict: true*/
/*global angular, console*/
'use strict';

function StatsCtrl($scope, $rootScope, $route, $http, socket, resolvedData) {
  // A hack to short circuit the routechange so that the controller doesn't get
  //  reloaded on path change
  var lastRoute = $route.current;
  $scope.$on('$locationChangeSuccess', function(event) {
    // Only do it for the stats path.
    if ($route.current.$route &&
        $route.current.$route.templateUrl === 'partials/stats') {
    // if (lastRoute && $route.current.$route.templateUrl === 'partials/stats') {
      // Grab the new matchid before we stop the routechange
      var matchId = $route.current.params.id;
      // This stops the routechange from succeeding
      $route.current = lastRoute;
      $route.current.params.id = matchId;

      $http({
        method: 'GET',
        url: '/api/stats/' + matchId
      }).success(function(data) {
        $rootScope.loading = false;
        parseStats(data, true);
      });

      socket.emit('stats:subscribe', matchId);
    }
  });

  socket.on('stats:update', function (data) {
    console.log('Stat update received!');
    parseStats(data, false);
  });

  function Player(data) {
    for (var key in data) {
      this[key] = data[key];
    }
  }
  Player.prototype.sumOf = function(statistic) {
    return sumArray2(this[statistic]);
  };
  Player.prototype.perMinute = function(statistic) {
    return ratio($scope.playableTime/60, this[statistic]);
  };
  Player.prototype.fapd = function() {
    return ratio(this.deaths, this.kills, this.killassists);
  };
  Player.prototype.mostPlayedClass = function() {
      if (this.mostplayedclass.length == 1) {
        return this.mostplayedclass[0];
      }
      // Determine what class was played the most by summing the rounddurations
      //  for the according tf2class in the "mostplayedclass" array.
      var totals = [0,0,0,0,0,0,0,0,0,0];
      for (var i=0, ilen=$scope.selectedRounds.length; i<ilen; i++) {
        // mostplayedclass[r] is the id (1-9) of the tf2class
        //  that the player played the most in the ith round.
        // The index of the max value in totals[] is the id of the tf2class
        //  that the player played the most in the match.
        var r = $scope.selectedRounds[i];
        totals[ this.mostplayedclass[r] ] += $scope.stats.roundduration[r];
      }
      // Find the index of the max value in totals[].
      var theClass=0, theMax=0;
      for (var j=0; j<=9; j++) {
        if (totals[j] > theMax) {
          theMax = totals[j];
          theClass = j;
        }
      }
      return theClass;
  };
  Player.prototype.playedClasses = function() {
    return filterBySelectedRounds(this.playedclasses)
             .reduce(function(a,b) { return a | b; },0);
  };

  $scope.overallStatsTableData = [
    ['Name', null, 'name']
  , ['C', 'Most Played Class', 'mostPlayedClass()']
  , ['P', 'Points', 'sumOf("points")']
  , ['FA/D', 'Frags+Assists Per Death', 'fapd()']
  , ['F', 'Frags', 'sumOf("kills")']
  , ['A', 'Assists', 'sumOf("killassists")']
  , ['D', 'Deaths', 'sumOf("deaths")']
  , ['S', 'Suicides', 'sumOf("suicides")']
  , ['DPM', 'Damage Per Minute', 'perMinute("damagedone")']
  , ['DMG', 'Damage', 'sumOf("damagedone")']
  , ['MP', 'Medic Picks', 'sumOf("medpicks")']
  , ['HR', 'Heals Received (Excl Buffs)', 'sumOf("healsreceived")']
  , ['CPC', 'Capture Points Captured', 'sumOf("captures")']
  , ['CPB', 'Capture Points Blocked', 'sumOf("defenses")']
  , ['DOM', 'Dominations', 'sumOf("dominations")']
  , ['REV', 'Revenges', 'sumOf("revenge")']
  // , ['', '', 'sumOf("buildingsdestroyed")']
  // , ['', '', 'sumOf("crits")']
  // , ['', '', 'sumOf("teleports")']
  // , ['', '', 'sumOf("resupplypoints")']
  // , ['', '', 'sumOf("bonuspoints")']
  ];
  $scope.medicStatsTableData = [
    ['Medics', null, 'name']
  , ['H', 'Heals Given (Incl Buffs)', 'sumOf("healpoints")']
  , ['U', 'Ubers', 'sumOf("invulns")']
  , ['UD', 'Ubers Dropped', 'sumOf("ubersdropped")']
  ];
  $scope.sniperStatsTableData = [
    ['Snipers', null, 'name']
  , ['HS', 'Headshot Kills', 'sumOf("headshots")']
  ];
  $scope.spyStatsTableData = [
    ['Spies', null, 'name']
  , ['BS', 'Backstabs', 'sumOf("backstabs")']
  ];
  $scope.engineerStatsTableData = [
    ['Engineers', null, 'name']
  , ['BB', 'Buildings Built', 'sumOf("buildingsbuilt")']
  ];

  var parseStats = function(data, reinitializeSelectedRounds) {
    // Stupid hack -- need to keep both a hash and and array of players,
    //  because AngularJS's orderBy function doesn't work on a hash.
    $scope.playersArr = [];
    $scope.players = {};

    // Stupid placeholder object for when things go wrong
    if (!data  || typeof data !== 'object') {
      data = { stats: {
        redname: 'RED'
      , bluname: 'BLU'
      , redscore: []
      , bluscore: []
      , players: {}
      }};
    }
    var stats = $scope.stats = data.stats;

    // If redscore.length is greater than the current number of rounds, then
    //  that means a new round just started -- add it to $scope.selectedRounds.
    if (stats.redscore.length > $scope.numRounds) {
      $scope.selectedRounds.push(stats.redscore.length-1);
    }

    var numRounds = $scope.numRounds = stats.redscore.length;

    if (reinitializeSelectedRounds) { $scope.initializeSelectedRoundsArray(); }

    var redScore = $scope.redScore =
                   stats.redscore.reduce(function(a,b) { return a + b; },0);
    var bluScore = $scope.bluScore =
                   stats.bluscore.reduce(function(a,b) { return a + b; },0);
    // Score Comparison
    $scope.scoreComparison = redScore > bluScore ?
                             '>' :
                             redScore < bluScore ?
                             '<' :
                             '==';

    // Total playable time
    var playableTime = $scope.playableTime = sumArray(stats.roundduration);

    // Construct player objects
    angular.forEach(stats.players, function(playerdata, steamid) {
      var player = $scope.players[steamid] = new Player(playerdata);
      // stupid hack
      $scope.playersArr.push(player);
    });

    // Calculate total midfights won for each team
    var totalMidfightsWon = [0,0,0,0];
    var filteredTeamfirstcapArr = filterBySelectedRounds(stats.teamfirstcap);
    for (var j=0; j<numRounds; j++) {
      totalMidfightsWon[ filteredTeamfirstcapArr[j] ] += 1;
    }

    // Calculate total damage and frags for each team
    var totalDamage = [0,0,0,0];
    var totalFrags = [0,0,0,0];
    angular.forEach($scope.players, function(player, steamid) {
      totalDamage[player.team] += sumArray(player.damagedone);
      totalFrags[player.team] += sumArray(player.kills);
    });

    // Assemble score overview table rows
    var redRoundScores = stats.redscore.concat(redScore,totalDamage[2]
                                             , totalFrags[2]
                                             , totalMidfightsWon[2]);
    var bluRoundScores = stats.bluscore.concat(bluScore,totalDamage[3]
                                             , totalFrags[3]
                                             , totalMidfightsWon[3]);
    $scope.redtr = '' + escapeHtml(stats.redname) +
                   '	' + redRoundScores.join('	') + '	';
    $scope.blutr = '' + escapeHtml(stats.bluname) +
                   '	' + bluRoundScores.join('	') + '	';
  };

  // Helpers

  $scope.selectedRounds = [];
  $scope.initializeSelectedRoundsArray = function() {
    $scope.selectedRounds = [];
    for (var i=0; i<$scope.numRounds; i++) {
      $scope.selectedRounds[i] = i;
    }
  };
  $scope.ctrl = false;
  window.onmouseup = function(e) {
    $scope.ctrl = !!e.ctrlKey;
  };
  $scope.clickRoundHeader = function(round) {
    if ($scope.selectedRounds.length === 0) {
      $scope.selectedRounds.push(round);
    } else if ($scope.selectedRounds.length === 1 &&
               $scope.selectedRounds[0] === round && !$scope.ctrl) {
      $scope.initializeSelectedRoundsArray();
    } else if ($scope.ctrl) {
      if ($scope.selectedRounds.indexOf(round) > -1) {
        $scope.selectedRounds.splice($scope.selectedRounds.indexOf(round),1);
      } else {
        $scope.selectedRounds.push(round);
      }
    } else {
      $scope.selectedRounds = [round];
    }
    $scope.selectedRounds.sort(function(a,b){return a-b;});
    parseStats({stats: $scope.stats, playerdata: $scope.playerMetaData}, false);
  };
  $scope.filterBinds = true;
  $scope.bindFilter = function(chat) {
    return (!$scope.filterBinds || !chat.isBind);
  };
  var sumArray = $scope.sumArray = function(arr) {
    if (!arr || !arr.length) return 0;
    return filterBySelectedRounds(arr).reduce(function(a,b) { return a + b; },0);
  };
  var sumArray2 = $scope.sumArray2 = function(arr) {
    if (!arr || !arr.length) return '-';
    var filteredArr = filterBySelectedRounds(arr);
    if (!filteredArr.length) return '-';
    return filteredArr.reduce(function(a,b) { return a + b; });
  };
  var ratio = $scope.ratio = function(den, numArray1, numArray2) {
    var numerator = sumArray2(numArray1) + sumArray(numArray2);
    var denominator = den.length ? sumArray2(den) : den;
    if (typeof numerator !== 'number') return '-';
    if (numerator === 0) return 0;
    if (denominator === 0) return '∞';
    return Math.round( (numerator/denominator)*100 )/100;
  };
  var filterBySelectedRounds = function(arr) {
    var filteredArr = [];
    for (var i=0, r; r=$scope.selectedRounds[i]+1; i++) {
      if (exists(arr[r-1])) { filteredArr.push(arr[r-1]); }
    }
    return filteredArr;
  };
  var exists = function(n) {
    return (n !== null && n !== undefined);
  };
  var escapeHtml = $scope.escapeHtml = (function () {
    var chr = { '"': '"', '&': '&', '<': '<', '>': '>' };
    return function (text) {
      return text.replace(/[\"&<>]/g, function (a) { return chr[a]; });
    };
  })();
  $scope.secondsToHMS = function(seconds) {
    var h = parseInt(seconds/3600, 10);
    var m = parseInt((seconds-h*3600)/60, 10);
    var s = seconds-h*3600-m*60;
    if (h === 0) { return ('0'+m).slice(-2)+':'+('0'+s).slice(-2); }
    return h+':'+('0'+m).slice(-2)+':'+('0'+s).slice(-2);
  };

  // The very first time you load the controller, use the resolvedData.
  parseStats(resolvedData, true);
  // And subscribe to the socket.io room.
  socket.emit('stats:subscribe', $route.current.params.id);

  // hack. FIXME
  $rootScope.statsCtrlIsLoadedSoDontCallResolveFunction = true;
  $scope.$on('$destroy', function() {
    $rootScope.statsCtrlIsLoadedSoDontCallResolveFunction = false;
  });
}
// This is for the first time you load the controller
//  -- so that you don't see all the empty divs and tables.
StatsCtrl.resolve = {
  resolvedData: function($q, $http, $route, $rootScope) {
    // hack. FIXME
    if ($rootScope.statsCtrlIsLoadedSoDontCallResolveFunction) {
      // return;
    }
    var deferred = $q.defer();
    $http({
      method: 'GET',
      url: '/api/stats/' + $route.current.params.id
    })
      .success(function(data) {
        deferred.resolve(data);
      })
      .error(function(data) {
        window.alert('Something went wrong.');
        deferred.reject(data);
      });
    return deferred.promise;
  }
};

function StatsEditCtrl($scope, $location, $route, $http, socket, resolvedData) {
  $scope.data = {
    redname: resolvedData.stats.redname
  , bluname: resolvedData.stats.bluname
  };

  $scope.saveButton = function() {
    $http.put('/api/stats/' + resolvedData.stats._id, $scope.data)
      .success(function(data, status, headers, config) {
        // TODO: Do something better
        $location.path('/stats/' + resolvedData.stats._id);
      })
      .error(function(data, status, headers, config) {
        console.log(data);
        window.alert('Something went wrong.');
      });

  };

  $scope.deleteButton = function() {
    if ( !window.confirm('Are you sure you want to delete these stats?') ) {
      return false;
    }
    $http.delete('/api/stats/' + resolvedData.stats._id)
      .success(function(data, status, headers, config) {
        // TODO: Do something better
        $location.path( "/" );
      })
      .error(function(data, status, headers, config) {
        // TODO: Do something
        console.log(data);
        window.alert('Something went wrong.');
      });
  };

}
<|endoftext|>


<commit_before>import _trim from 'lodash.trim'
import decode from 'jwt-decode'
import inflection from 'inflection'

export function stripSlashes (location) {
  return Array.isArray(location) ? location.map(l => _trim(l, '/')) : _trim(location, '/')
}

export function upperCaseFirst (string) {
  return string.charAt(0).toUpperCase() + string.slice(1)
}

export function getShortName (service) {
  let namespace = stripSlashes(service)
  if (Array.isArray(namespace)) {
    namespace = namespace.slice(-1)
  } else if (namespace.includes('/')) {
    namespace = namespace.slice(namespace.lastIndexOf('/') + 1)
  }
  return namespace
}

export function getNameFromPath (service) {
  return stripSlashes(service)
}

// from https://github.com/iliakan/detect-node
export const isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'

export const isBrowser = !isNode

const authDefaults = {
  commit: undefined,
  req: undefined,
  moduleName: 'auth',
  cookieName: 'feathers-jwt'
}

export const initAuth = function initAuth (options) {
  const { commit, req, moduleName, cookieName } = Object.assign({}, authDefaults, options)

  if (typeof commit !== 'function') {
    throw new Error('You must pass the `commit` function in the `initAuth` function options.')
  }
  if (!req) {
    throw new Error('You must pass the `req` object in the `initAuth` function options.')
  }

  const accessToken = readCookie(req.headers.cookie, cookieName)
  const payload = getValidPayloadFromToken(accessToken)

  if (payload) {
    commit(`${moduleName}/setAccessToken`, accessToken)
    commit(`${moduleName}/setPayload`, payload)
  }
  return Promise.resolve(payload)
}

export function getValidPayloadFromToken (token) {
  if (token) {
    try {
      var payload = decode(token)
      return payloadIsValid(payload) ? payload : undefined
    } catch (error) {
      return undefined
    }
  }
  return undefined
}

// Pass a decoded payload and it will return a boolean based on if it hasn't expired.
export function payloadIsValid (payload) {
  return payload && payload.exp * 1000 > new Date().getTime()
}

// Reads and returns the contents of a cookie with the provided name.
export function readCookie (cookies, name) {
  if (!cookies) {
    return undefined
  }
  var nameEQ = name + '='
  var ca = cookies.split(';')
  for (var i = 0; i < ca.length; i++) {
    var c = ca[i]
    while (c.charAt(0) === ' ') {
      c = c.substring(1, c.length)
    }
    if (c.indexOf(nameEQ) === 0) {
      return c.substring(nameEQ.length, c.length)
    }
  }
  return null
}

export function checkId (id, item, debug) {
  if (id === undefined || id === null) {
    if (debug) {
      console.error('No id found for item. Do you need to customize the `idField`?', item)
    }
    return false
  }
  return true
}

export function registerModel (Model, globalModels, apiPrefix, servicePath) {
  const modelName = getModelName(Model)
  let path = apiPrefix ? `${apiPrefix}.${modelName}` : modelName

  setByDot(globalModels, path, Model)
  globalModels.byServicePath[servicePath] = Model
  return {
    path,
    name: modelName
  }
}

// Creates a Model class name from the last part of the servicePath
export function getModelName (Model) {
  // If the Model.name has been customized, use it.
  if (Model.modelName) {
    return Model.modelName
  }

  // Otherwise, use an inflection of the last bit of the servicePath
  const parts = Model.servicePath.split('/')
  let name = parts[parts.length - 1]
  name = inflection.titleize(name)
  name = name.split('-').join('')
  name = inflection.singularize(name)
  return name
}

//  From feathers-plus/feathers-hooks-common
export function setByDot (obj, path, value, ifDelete) {
  if (ifDelete) {
    console.log('DEPRECATED. Use deleteByDot instead of setByDot(obj,path,value,true). (setByDot)')
  }

  if (path.indexOf('.') === -1) {
    obj[path] = value

    if (value === undefined && ifDelete) {
      delete obj[path]
    }

    return
  }

  const parts = path.split('.')
  const lastIndex = parts.length - 1
  return parts.reduce(
    (obj1, part, i) => {
      if (i !== lastIndex) {
        if (!obj1.hasOwnProperty(part) || typeof obj1[part] !== 'object') {
          obj1[part] = {}
        }
        return obj1[part]
      }

      obj1[part] = value
      if (value === undefined && ifDelete) {
        delete obj1[part]
      }
      return obj1
    },
    obj
  )
}
<commit_msg>Add diff lib to utils<commit_after>import _trim from 'lodash.trim'
import decode from 'jwt-decode'
import inflection from 'inflection'
import { diff } from 'deep-diff'

export function stripSlashes (location) {
  return Array.isArray(location) ? location.map(l => _trim(l, '/')) : _trim(location, '/')
}

export function upperCaseFirst (string) {
  return string.charAt(0).toUpperCase() + string.slice(1)
}

export function getShortName (service) {
  let namespace = stripSlashes(service)
  if (Array.isArray(namespace)) {
    namespace = namespace.slice(-1)
  } else if (namespace.includes('/')) {
    namespace = namespace.slice(namespace.lastIndexOf('/') + 1)
  }
  return namespace
}

export function getNameFromPath (service) {
  return stripSlashes(service)
}

// from https://github.com/iliakan/detect-node
export const isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'

export const isBrowser = !isNode

const authDefaults = {
  commit: undefined,
  req: undefined,
  moduleName: 'auth',
  cookieName: 'feathers-jwt'
}

export const initAuth = function initAuth (options) {
  const { commit, req, moduleName, cookieName } = Object.assign({}, authDefaults, options)

  if (typeof commit !== 'function') {
    throw new Error('You must pass the `commit` function in the `initAuth` function options.')
  }
  if (!req) {
    throw new Error('You must pass the `req` object in the `initAuth` function options.')
  }

  const accessToken = readCookie(req.headers.cookie, cookieName)
  const payload = getValidPayloadFromToken(accessToken)

  if (payload) {
    commit(`${moduleName}/setAccessToken`, accessToken)
    commit(`${moduleName}/setPayload`, payload)
  }
  return Promise.resolve(payload)
}

export function getValidPayloadFromToken (token) {
  if (token) {
    try {
      var payload = decode(token)
      return payloadIsValid(payload) ? payload : undefined
    } catch (error) {
      return undefined
    }
  }
  return undefined
}

// Pass a decoded payload and it will return a boolean based on if it hasn't expired.
export function payloadIsValid (payload) {
  return payload && payload.exp * 1000 > new Date().getTime()
}

// Reads and returns the contents of a cookie with the provided name.
export function readCookie (cookies, name) {
  if (!cookies) {
    return undefined
  }
  var nameEQ = name + '='
  var ca = cookies.split(';')
  for (var i = 0; i < ca.length; i++) {
    var c = ca[i]
    while (c.charAt(0) === ' ') {
      c = c.substring(1, c.length)
    }
    if (c.indexOf(nameEQ) === 0) {
      return c.substring(nameEQ.length, c.length)
    }
  }
  return null
}

export function checkId (id, item, debug) {
  if (id === undefined || id === null) {
    if (debug) {
      console.error('No id found for item. Do you need to customize the `idField`?', item)
    }
    return false
  }
  return true
}

export function registerModel (Model, globalModels, apiPrefix, servicePath) {
  const modelName = getModelName(Model)
  let path = apiPrefix ? `${apiPrefix}.${modelName}` : modelName

  setByDot(globalModels, path, Model)
  globalModels.byServicePath[servicePath] = Model
  return {
    path,
    name: modelName
  }
}

// Creates a Model class name from the last part of the servicePath
export function getModelName (Model) {
  // If the Model.name has been customized, use it.
  if (Model.modelName) {
    return Model.modelName
  }

  // Otherwise, use an inflection of the last bit of the servicePath
  const parts = Model.servicePath.split('/')
  let name = parts[parts.length - 1]
  name = inflection.titleize(name)
  name = name.split('-').join('')
  name = inflection.singularize(name)
  return name
}

//  From feathers-plus/feathers-hooks-common
export function setByDot (obj, path, value, ifDelete) {
  if (ifDelete) {
    console.log('DEPRECATED. Use deleteByDot instead of setByDot(obj,path,value,true). (setByDot)')
  }

  if (path.indexOf('.') === -1) {
    obj[path] = value

    if (value === undefined && ifDelete) {
      delete obj[path]
    }

    return
  }

  const parts = path.split('.')
  const lastIndex = parts.length - 1
  return parts.reduce(
    (obj1, part, i) => {
      if (i !== lastIndex) {
        if (!obj1.hasOwnProperty(part) || typeof obj1[part] !== 'object') {
          obj1[part] = {}
        }
        return obj1[part]
      }

      obj1[part] = value
      if (value === undefined && ifDelete) {
        delete obj1[part]
      }
      return obj1
    },
    obj
  )
}

export function diffFunctions () {
  return diff
}
<|endoftext|>


<commit_before>const assert = require('chai').assert
const remoteFiles = require('../remoteFiles')
const fs = require('fs')
const zlib = require('zlib')
const config = {
  s3Params: {
    Bucket: 'none',
  },
  file: {
    name: 'something',
  },
}

describe('remoteFiles', () => {
  beforeAll(() => {
    // fetch mock
    global.fetch = jest
      .fn()
      .mockImplementation(() =>
        Promise.resolve({ ok: true, buffer: () => 'buffer' }),
      )
  })

  beforeEach(() => {
    delete process.env.AWS_ACCESS_KEY_ID
  })

  describe('accessRemoteFile', () => {
    it('should return a promise', () => {
      const promise = remoteFiles.accessRemoteFile(config)
      expect(promise).toBeInstanceOf(Promise)
    })
    it('should return the response of constructAwsRequest if successful', () => {
      remoteFiles
        .accessRemoteFile(config)
        .then(res => expect(res).toBe('buffer'))
    })
    it('should return the issue of extractGzipBuffer if unzip is unsuccessful', done => {
      config.file.name = 'something.gz'
      remoteFiles.accessRemoteFile(config).catch(issue => {
        expect(issue).toHaveProperty('code')
        config.file.name = 'something'
        done()
      })
    })
    it('should return the error of constructAwsRequest if unsuccessful', () => {
      process.env.AWS_ACCESS_KEY_ID = 12
      return expect(remoteFiles.accessRemoteFile(config)).rejects.toBeTruthy()
    })
  })

  describe('constructAwsRequest', () => {
    it('should return a fetch resolution promise when aws creds are not present', async () => {
      const response = remoteFiles.constructAwsRequest({
        s3Params: { Bucket: 'matters not' },
      })
      expect(response).toBeInstanceOf(Promise)
    })
    it('should return the buffer() property of the fetch response', async done => {
      remoteFiles
        .constructAwsRequest({
          s3Params: { Bucket: 'matters not' },
        })
        .then(data => {
          assert.equal(data, 'buffer')
          done()
        })
    })
    it('should return a s3.getObject resolution promise when aws creds are present', done => {
      process.env.AWS_ACCESS_KEY_ID = 12
      config.s3Params.Key = 'this_is_not_valid'
      const response = remoteFiles.constructAwsRequest(config).catch(e => {
        expect(e)
        done()
      })
      expect(response).toBeInstanceOf(Promise)
    }, 120000)
    it('should throw an error when a bucket cannot be accessed', done => {
      process.env.AWS_ACCESS_KEY_ID = 12
      remoteFiles.constructAwsRequest(config).catch(e => {
        expect(e)
        done()
      })
    }, 120000)
  })

  describe('extractGzipBuffer', () => {
    it('should extract proper gzip files', async () => {
      zlib.gzip('Some String', async (err, res) => {
        const gzip = await remoteFiles.extractGzipBuffer(res, {})
        expect(gzip).toBeInstanceOf(Buffer)
      })
    })
    it('should reject with an issue when gzip reading fails', async () => {
      try {
        const zip = 'bad data'
        await remoteFiles.extractGzipBuffer(zip, {})
      } catch (e) {
        expect(e).toHaveProperty('code')
        expect(e.code).toEqual(28)
      }
    })
  })

  describe('callGitAnnex', () => {
    it('should return the string result of execSync', () => {
      const resp = remoteFiles.callGitAnnex('echo test')
      expect(resp.trim()).toBe('test')
    })
  })

  describe('getRemotesInfo', () => {
    it('should return an empty array if callGitAnnex does not return contents of a metadata file', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue('bad_response')
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {
        relativePath: 'some_file',
      })
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an empty array if file is not properly formatted', () => {
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {})
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an empty array if directory is not properly formatted', () => {
      const remotesInfo = remoteFiles.getRemotesInfo('bad directory', {
        relativePath: 'some_path',
      })
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an array of remote objects if getRemoteData returns properly formatted remote metadata file', () => {
      remoteFiles.getRemoteMetadata = jest.fn()
      remoteFiles.getRemoteMetadata.mockReturnValue(
        'timestamp remoteuuid:commitinfo xversionId#fileName',
      )
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {
        relativePath: 'some_file',
      })
      remoteFiles.getRemoteMetadata.mockRestore()
      assert.lengthOf(remotesInfo, 1)
    })
  })

  describe('getSingleRemoteInfo', () => {
    it('returns an object with null Bucket property if the response does not contain remote info', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue('bad_response')
      const singleRemoteInfo = remoteFiles.getSingleRemoteInfo(
        'some_dir',
        'some_uuid',
      )
      expect(singleRemoteInfo).toHaveProperty('Bucket')
      expect(singleRemoteInfo.Bucket).toBe(null)
    })
    it('returns an object with a Bucket property if callGitAnnex returns an object with the Bucket field', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue(
        'good_response\nbucket: such_bucket\nawesome_line',
      )
      const singleRemoteInfo = remoteFiles.getSingleRemoteInfo(
        'some_dir',
        'some_uuid',
      )
      expect(singleRemoteInfo).toHaveProperty('Bucket')
      expect(singleRemoteInfo.Bucket).toEqual('such_bucket')
    })
  })

  describe('getRemoteBucket', () => {
    it('returns an object with a Bucket property if the response contains that field', () => {
      const resp = 'something:something\nbucket: omg\nawesome:awesome'
      const params = remoteFiles.getRemoteBucket(resp)
      expect(params).toHaveProperty('Bucket')
      expect(params.Bucket).toEqual('omg')
    })
    it('returns an object with null Bucket property if the response does not contain the bucket field', () => {
      const resp = 'wow_this_is_a_bad_response'
      const params = remoteFiles.getRemoteBucket(resp)
      expect(params).toHaveProperty('Bucket')
      expect(params.Bucket).toBe(null)
    })
  })

  describe('processRemoteMetadata', () => {
    it('properly parses a git-annex remote metadata file', () => {
      const resp = 'timestamp remoteuuid:commitinfo xversionId#fileName'
      const remotesInfo = remoteFiles.processRemoteMetadata(resp)
      assert.lengthOf(remotesInfo, 1)
      const remoteObj = remotesInfo[0]
      expect(remoteObj).toHaveProperty('timestamp')
      expect(remoteObj.timestamp).toEqual('timestamp')
      expect(remoteObj).toHaveProperty('remoteUuid')
      expect(remoteObj.remoteUuid).toEqual('remoteuuid')
      expect(remoteObj).toHaveProperty('fileName')
      expect(remoteObj.fileName).toEqual('fileName')
      expect(remoteObj).toHaveProperty('versionId')
      expect(remoteObj.versionId).toEqual('versionId')
    })
    it('returns an empty array if there is an improperly formatted metadata file', () => {
      let remotesInfo
      const no_spaces = 'poorly_formatted_response' // contains no spaces
      remotesInfo = remoteFiles.processRemoteMetadata(no_spaces)
      assert.lengthOf(remotesInfo, 0)
      const not_enough_items = 'one two' // does not contain enough "columns"
      remotesInfo = remoteFiles.processRemoteMetadata(not_enough_items)
      assert.lengthOf(remotesInfo, 0)

      // does not have the properly one two:three xfour#five format
      const not_properly_formatted = 'one two:three four'
      remotesInfo = remoteFiles.processRemoteMetadata(not_properly_formatted)
      assert.lengthOf(remotesInfo, 0)
      const not_the_right_separators = 'one two:three xfour:five'
      remotesInfo = remoteFiles.processRemoteMetadata(not_the_right_separators)
      assert.lengthOf(remotesInfo, 0)
    })
    it('returns objects corresponding to any properly formatted line', () => {
      const one_line_right =
        'properly formatted:response xwith#a\nline_that_is_not_properly_formatted'
      const remotesInfo = remoteFiles.processRemoteMetadata(one_line_right)
      assert.lengthOf(remotesInfo, 1)
    })
  })

  describe('isGitAnnex', () => {
    it('returns false when fs.existsSync returns false', () => {
      fs.existsSync = jest.fn()
      fs.existsSync.mockReturnValue(false)
      const isGitAnnex = remoteFiles.isGitAnnex('some-path')
      expect(fs.existsSync).toHaveBeenCalled()
      expect(isGitAnnex).toBe(false)
    })
    it('returns true when fs.existsSync returns true', () => {
      fs.existsSync = jest.fn()
      fs.existsSync.mockReturnValue(true)
      const isGitAnnex = remoteFiles.isGitAnnex('some-path')
      expect(fs.existsSync).toHaveBeenCalled()
      expect(isGitAnnex).toBe(true)
    })
  })

  describe('tryRemote', () => {
    it('should resolve with the results of accessRemoteFile', done => {
      remoteFiles.getSingleRemoteInfo = jest.fn()
      remoteFiles.getSingleRemoteInfo.mockReturnValue({ Bucket: 'wow' })
      remoteFiles.accessRemoteFile = jest.fn()
      remoteFiles.accessRemoteFile.mockReturnValue(Promise.resolve('data'))
      remoteFiles
        .tryRemote(
          {},
          { dir: 'directory', file: { relativePath: 'wow', name: 'name' } },
        )
        .then(data => {
          expect(data)
          done()
        })
        .catch(done)
    })
  })
  // reset the fs object back to its normal state
  // so we dont break jest
  afterAll(() => {
    fs.existsSync.mockRestore()
  })
})
<commit_msg>Disable remoteFiles failure case tests.<commit_after>const assert = require('chai').assert
const remoteFiles = require('../remoteFiles')
const fs = require('fs')
const zlib = require('zlib')
const config = {
  s3Params: {
    Bucket: 'none',
  },
  file: {
    name: 'something',
  },
}

describe('remoteFiles', () => {
  beforeAll(() => {
    // fetch mock
    global.fetch = jest
      .fn()
      .mockImplementation(() =>
        Promise.resolve({ ok: true, buffer: () => 'buffer' }),
      )
  })

  beforeEach(() => {
    delete process.env.AWS_ACCESS_KEY_ID
  })

  describe('accessRemoteFile', () => {
    it('should return a promise', () => {
      const promise = remoteFiles.accessRemoteFile(config)
      expect(promise).toBeInstanceOf(Promise)
    })
    it('should return the response of constructAwsRequest if successful', () => {
      remoteFiles
        .accessRemoteFile(config)
        .then(res => expect(res).toBe('buffer'))
    })
    it('should return the issue of extractGzipBuffer if unzip is unsuccessful', done => {
      config.file.name = 'something.gz'
      remoteFiles.accessRemoteFile(config).catch(issue => {
        expect(issue).toHaveProperty('code')
        config.file.name = 'something'
        done()
      })
    })
  })

  describe('constructAwsRequest', () => {
    it('should return a fetch resolution promise when aws creds are not present', async () => {
      const response = remoteFiles.constructAwsRequest({
        s3Params: { Bucket: 'matters not' },
      })
      expect(response).toBeInstanceOf(Promise)
    })
    it('should return the buffer() property of the fetch response', async done => {
      remoteFiles
        .constructAwsRequest({
          s3Params: { Bucket: 'matters not' },
        })
        .then(data => {
          assert.equal(data, 'buffer')
          done()
        })
    })
  })

  describe('extractGzipBuffer', () => {
    it('should extract proper gzip files', async () => {
      zlib.gzip('Some String', async (err, res) => {
        const gzip = await remoteFiles.extractGzipBuffer(res, {})
        expect(gzip).toBeInstanceOf(Buffer)
      })
    })
    it('should reject with an issue when gzip reading fails', async () => {
      try {
        const zip = 'bad data'
        await remoteFiles.extractGzipBuffer(zip, {})
      } catch (e) {
        expect(e).toHaveProperty('code')
        expect(e.code).toEqual(28)
      }
    })
  })

  describe('callGitAnnex', () => {
    it('should return the string result of execSync', () => {
      const resp = remoteFiles.callGitAnnex('echo test')
      expect(resp.trim()).toBe('test')
    })
  })

  describe('getRemotesInfo', () => {
    it('should return an empty array if callGitAnnex does not return contents of a metadata file', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue('bad_response')
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {
        relativePath: 'some_file',
      })
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an empty array if file is not properly formatted', () => {
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {})
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an empty array if directory is not properly formatted', () => {
      const remotesInfo = remoteFiles.getRemotesInfo('bad directory', {
        relativePath: 'some_path',
      })
      assert.lengthOf(remotesInfo, 0)
    })
    it('should return an array of remote objects if getRemoteData returns properly formatted remote metadata file', () => {
      remoteFiles.getRemoteMetadata = jest.fn()
      remoteFiles.getRemoteMetadata.mockReturnValue(
        'timestamp remoteuuid:commitinfo xversionId#fileName',
      )
      const remotesInfo = remoteFiles.getRemotesInfo('some_directory', {
        relativePath: 'some_file',
      })
      remoteFiles.getRemoteMetadata.mockRestore()
      assert.lengthOf(remotesInfo, 1)
    })
  })

  describe('getSingleRemoteInfo', () => {
    it('returns an object with null Bucket property if the response does not contain remote info', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue('bad_response')
      const singleRemoteInfo = remoteFiles.getSingleRemoteInfo(
        'some_dir',
        'some_uuid',
      )
      expect(singleRemoteInfo).toHaveProperty('Bucket')
      expect(singleRemoteInfo.Bucket).toBe(null)
    })
    it('returns an object with a Bucket property if callGitAnnex returns an object with the Bucket field', () => {
      remoteFiles.callGitAnnex = jest.fn()
      remoteFiles.callGitAnnex.mockReturnValue(
        'good_response\nbucket: such_bucket\nawesome_line',
      )
      const singleRemoteInfo = remoteFiles.getSingleRemoteInfo(
        'some_dir',
        'some_uuid',
      )
      expect(singleRemoteInfo).toHaveProperty('Bucket')
      expect(singleRemoteInfo.Bucket).toEqual('such_bucket')
    })
  })

  describe('getRemoteBucket', () => {
    it('returns an object with a Bucket property if the response contains that field', () => {
      const resp = 'something:something\nbucket: omg\nawesome:awesome'
      const params = remoteFiles.getRemoteBucket(resp)
      expect(params).toHaveProperty('Bucket')
      expect(params.Bucket).toEqual('omg')
    })
    it('returns an object with null Bucket property if the response does not contain the bucket field', () => {
      const resp = 'wow_this_is_a_bad_response'
      const params = remoteFiles.getRemoteBucket(resp)
      expect(params).toHaveProperty('Bucket')
      expect(params.Bucket).toBe(null)
    })
  })

  describe('processRemoteMetadata', () => {
    it('properly parses a git-annex remote metadata file', () => {
      const resp = 'timestamp remoteuuid:commitinfo xversionId#fileName'
      const remotesInfo = remoteFiles.processRemoteMetadata(resp)
      assert.lengthOf(remotesInfo, 1)
      const remoteObj = remotesInfo[0]
      expect(remoteObj).toHaveProperty('timestamp')
      expect(remoteObj.timestamp).toEqual('timestamp')
      expect(remoteObj).toHaveProperty('remoteUuid')
      expect(remoteObj.remoteUuid).toEqual('remoteuuid')
      expect(remoteObj).toHaveProperty('fileName')
      expect(remoteObj.fileName).toEqual('fileName')
      expect(remoteObj).toHaveProperty('versionId')
      expect(remoteObj.versionId).toEqual('versionId')
    })
    it('returns an empty array if there is an improperly formatted metadata file', () => {
      let remotesInfo
      const no_spaces = 'poorly_formatted_response' // contains no spaces
      remotesInfo = remoteFiles.processRemoteMetadata(no_spaces)
      assert.lengthOf(remotesInfo, 0)
      const not_enough_items = 'one two' // does not contain enough "columns"
      remotesInfo = remoteFiles.processRemoteMetadata(not_enough_items)
      assert.lengthOf(remotesInfo, 0)

      // does not have the properly one two:three xfour#five format
      const not_properly_formatted = 'one two:three four'
      remotesInfo = remoteFiles.processRemoteMetadata(not_properly_formatted)
      assert.lengthOf(remotesInfo, 0)
      const not_the_right_separators = 'one two:three xfour:five'
      remotesInfo = remoteFiles.processRemoteMetadata(not_the_right_separators)
      assert.lengthOf(remotesInfo, 0)
    })
    it('returns objects corresponding to any properly formatted line', () => {
      const one_line_right =
        'properly formatted:response xwith#a\nline_that_is_not_properly_formatted'
      const remotesInfo = remoteFiles.processRemoteMetadata(one_line_right)
      assert.lengthOf(remotesInfo, 1)
    })
  })

  describe('isGitAnnex', () => {
    it('returns false when fs.existsSync returns false', () => {
      fs.existsSync = jest.fn()
      fs.existsSync.mockReturnValue(false)
      const isGitAnnex = remoteFiles.isGitAnnex('some-path')
      expect(fs.existsSync).toHaveBeenCalled()
      expect(isGitAnnex).toBe(false)
    })
    it('returns true when fs.existsSync returns true', () => {
      fs.existsSync = jest.fn()
      fs.existsSync.mockReturnValue(true)
      const isGitAnnex = remoteFiles.isGitAnnex('some-path')
      expect(fs.existsSync).toHaveBeenCalled()
      expect(isGitAnnex).toBe(true)
    })
  })

  describe('tryRemote', () => {
    it('should resolve with the results of accessRemoteFile', done => {
      remoteFiles.getSingleRemoteInfo = jest.fn()
      remoteFiles.getSingleRemoteInfo.mockReturnValue({ Bucket: 'wow' })
      remoteFiles.accessRemoteFile = jest.fn()
      remoteFiles.accessRemoteFile.mockReturnValue(Promise.resolve('data'))
      remoteFiles
        .tryRemote(
          {},
          { dir: 'directory', file: { relativePath: 'wow', name: 'name' } },
        )
        .then(data => {
          expect(data)
          done()
        })
        .catch(done)
    })
  })
  // reset the fs object back to its normal state
  // so we dont break jest
  afterAll(() => {
    fs.existsSync.mockRestore()
  })
})
<|endoftext|>


<commit_before>(function main($angular) {

    /**
     * @module umlApp
     * @submodule class
     * @author Adam Timberlake
     */
    $angular.module('umlApp').directive('class', function classDirective(Snap) {

        return {

            /**
             * @property restrict
             * @type {String}
             * @default 'E'
             */
            restrict: 'E',

            /**
             * @property scope
             * @type {Boolean}
             * @default true
             */
            scope: {
                model: '=ngModel'
            },

            /**
             * @property require
             * @type {String}
             */
            require: '^uml',

            /**
             * @method controller
             * @param $scope {Object}
             * @return {void}
             */
            controller: function controller($scope) {

                /**
                 * @method styleClass
                 * @param classItem {Object}
                 * @return {void}
                 */
                $scope.styleClass = function styleClass(classItem) {

                    classItem.attr({
                        fill: 'white',
                        stroke: 'lightgrey',
                        strokeOpacity: 1,
                        strokeWidth: 1,
                        cursor: 'move'
                    });

                };

                /**
                 * @method styleText
                 * @param textItem {Object}
                 * @return {void}
                 */
                $scope.styleText = function styleText(textItem) {

                    textItem.attr({
                        fontFamily: 'Arial, Tahoma, Helvetica',
                        fontSize: '11px'
                    });

                };

            },

            /**
             * @method link
             * @param scope {Object}
             * @param element {Object}
             * @param attributes {Object}
             * @param umlController {Object}
             * @return {void}
             */
            link: function link(scope, element, attributes, umlController) {

                var group = umlController.snap.group();

                // Create the class item, and then style it!
                var classItem = group.rect(scope.model.x, scope.model.y, 200, 100);
                var textItem  = group.text(scope.model.x + 8, scope.model.y + 18, scope.model.name);
                scope.styleClass(classItem);
                scope.styleText(textItem);
                group.drag();

            }

        };

    });

})(window.angular);<commit_msg>Added shadow<commit_after>(function main($angular) {

    /**
     * @module umlApp
     * @submodule class
     * @author Adam Timberlake
     */
    $angular.module('umlApp').directive('class', function classDirective(Snap) {

        return {

            /**
             * @property restrict
             * @type {String}
             * @default 'E'
             */
            restrict: 'E',

            /**
             * @property scope
             * @type {Boolean}
             * @default true
             */
            scope: {
                model: '=ngModel'
            },

            /**
             * @property require
             * @type {String}
             */
            require: '^uml',

            /**
             * @method controller
             * @param $scope {Object}
             * @return {void}
             */
            controller: function controller($scope) {

                /**
                 * @method styleClass
                 * @param classItem {Object}
                 * @param shadowFilter {Object}
                 * @return {void}
                 */
                $scope.styleClass = function styleClass(classItem, shadowFilter) {

                    classItem.attr({
                        fill: 'white',
                        stroke: 'lightgrey',
                        strokeOpacity: 1,
                        strokeWidth: 1,
                        cursor: 'move',
                        filter: shadowFilter
                    });

                };

                /**
                 * @method styleText
                 * @param textItem {Object}
                 * @return {void}
                 */
                $scope.styleText = function styleText(textItem) {

                    textItem.attr({
                        fontFamily: 'Arial, Tahoma, Helvetica',
                        fontSize: '11px',
                    });

                };

            },

            /**
             * @method link
             * @param scope {Object}
             * @param element {Object}
             * @param attributes {Object}
             * @param umlController {Object}
             * @return {void}
             */
            link: function link(scope, element, attributes, umlController) {

                var group = umlController.snap.group();

                // Create the class item, and then style it!
                var classItem    = group.rect(scope.model.x, scope.model.y, 200, 100),
                    textItem     = group.text(scope.model.x + 8, scope.model.y + 18, scope.model.name),
                    shadowFilter = umlController.snap.paper.filter(Snap.filter.shadow(0, 0, 1, .1));

                scope.styleClass(classItem, shadowFilter);
                scope.styleText(textItem);
                group.drag();

            }

        };

    });

})(window.angular);<|endoftext|>


<commit_before>import './typedefs'
import _ from 'lodash'

/* eslint-disable complexity */
/**
 * Check if the given model includes a required field
 * @param {Model} model - the model definition
 * @returns {Boolean} true if model or any child has a required field
 */
export function doesModelContainRequiredField (model) {
  if (Array.isArray(model)) {
    for (let i = 0, len = model.length; i < len; i++) {
      if (doesModelContainRequiredField(model[i])) {
        return true
      }
    }

    return false
  }

  if (!_.isPlainObject(model)) {
    return false
  }

  if (!_.isEmpty(model.required)) {
    return true
  }

  for (let key in model) {
    if (doesModelContainRequiredField(model[key])) {
      return true
    }
  }
}
/* eslint-enable complexity */

/**
 * Get the user-visible label from the model instance
 * @param {String} label - the label override from the view
 * @param {BunsenModel} model - the model (to get title from if present)
 * @param {String} id - the dotted refeference to this object
 * @returns {String} the user-visible label
 */
export function getLabel (label, model, id) {
  const title = model ? model.title : null
  let idLabel = (id) ? _.startCase(id.split('.').slice(-1)[0]) : ''
  idLabel = _.capitalize(idLabel.toLowerCase())
  return `${label || title || idLabel}`
}

/**
 * Convert a model reference to a proper path in the model schema
 *
 * hero.firstName => hero.attributes.firstName
 * foo.bar.baz => foo.attributes.bar.attributes.baz
 *
 * Leading or trailing '.' mess up our trivial split().join() and aren't valid anyway, so we
 * handle them specially, undefined being passed into _.get() will yield undefined, and display
 * the error we want to display when the model reference is invalid, so we return undefined
 *
 * hero. => undefined
 * .hero => undefined
 *
 * @param {String} reference - the dotted reference to the model
 * @param {String} [dependencyReference] - the dotted reference to the model dependency
 * @returns {String[]} the proper dotted path in the model schema (or undefined if it's a bad path)
 */
export function _getModelPath (reference, dependencyReference) {
  const pattern = /^[^\.](.*[^\.])?$/ // eslint-disable-line no-useless-escape
  if (!pattern.test(reference)) {
    return
  }

  const pathArr = reference.split('.')
  if (dependencyReference) {
    const dependencyName = dependencyReference.split('.').pop()
    pathArr.splice(-1, 0, '$dependencies', dependencyName)
  }

  return pathArr
}

/**
 * Builds a dotted path to a submodel of a nested value based on its dotted path within
 * the value object
 *
 * @export
 * @param {BunsenModel} model The bunsen model of the value
 * @param {String} reference Dotted path to the value within the value object
 * @returns {BunsenModelPath} Model path object with the path to the model of the specified
 * value
 */
export function getModelPath (model, reference) {
  return new BunsenModelPath(model, reference)
}

/**
 * Get the sub-model for a given dotted reference
 * @param {BunsenModel} model - the starting model
 * @param {String} reference - the reference to fetch
 * @param {String} [dependencyReference] - the dotted reference to the model dependency (if any)
 * @returns {BunsenModel} the sub-model
 */
export function getSubModel (model, reference, dependencyReference) {
  const path = getModelPath(model, reference).modelPath()
  return _.get(model, path)
}

/**
 * Figure out an initial value based on existing value, initialValue, and model
 * @param {String} id - the dotted path to this value in the formValue
 * @param {Object} formValue - the existing value of the whole form
 * @param {*} initialValue - the initialValue passed in to the component
 * @param {Object} model - the model to check for a default value in
 * @param {*} defaultValue - the default value to use if no other defaults are found
 * @returns {*} the initial value
 */
export function getInitialValue (id, formValue, initialValue, model, defaultValue = '') {
  const values = [_.get(formValue, id), initialValue, model['default']]
  const value = _.find(values, (value) => value !== undefined)

  if (value !== undefined) {
    return value
  }

  return defaultValue
}

/**
 * mine an object for a value
 * @param {Object} obj the object to mine
 * @param {String} valuePath the path to the value in the object
 * @param {String} startPath - start path
 * @returns {String} the value
 */
export function findValue (obj, valuePath, startPath = '') {
  const depths = startPath.split('.')
  const valueLevels = valuePath.split('./')
  const parentLevels = valueLevels.filter((element) => element === '.')
  const valueKey = valueLevels.pop()
  const absValuePath = _.without(depths.slice(0, depths.length - parentLevels.length - 1), '', '.').join('.')
  const absValueKey = [absValuePath, valueKey].join('.').replace(/^\./, '')
  return _.get(obj, absValueKey)
}

/* eslint-disable complexity */
/**
 * finds variables in orch-style queryParam values
 * @param {Object} valueObj - the value object to mine for query values
 * @param {String} queryJSON - the stringified filter object to parse
 * @param {String} startPath - start path
 * @param {Boolean} allowEmpty - allow empty values to be represented by ''
 * @returns {String} the populated filter
 * @throws Will throw when any value at the resolved path is empty
 */
export function parseVariables (valueObj, queryJSON, startPath = '', allowEmpty = false) {
  if (!queryJSON) {
    return ''
  }

  if (queryJSON.indexOf('${') !== -1) {
    const valueVariable = queryJSON.split('${')[1].split('}')[0]
    let result = findValue(valueObj, valueVariable, startPath)

    if (result === undefined || String(result) === '') {
      if (allowEmpty) {
        result = ''
      } else {
        throw new Error(`value at ${valueVariable} is empty`)
      }
    }

    const newQueryJson = queryJSON.split('${' + valueVariable + '}').join(result)
    return parseVariables(valueObj, newQueryJson, startPath, allowEmpty)
  }

  return queryJSON
}
/* eslint-enable complexity */

/**
 * grooms the query for variables using a ${variableName} syntax and populates the values
 * @param {Object} valueObj - the value object to mine for values
 * @param {Object} query - the definition from the model schema
 * @param {String} startPath - start path
 * @returns {Object} the populated query
 */
export function populateQuery (valueObj, query, startPath = '') {
  try {
    return JSON.parse(parseVariables(valueObj, JSON.stringify(query || {}), startPath))
  } catch (err) {
    return null
  }
}

/**
 * Checks if the query object has valid values
 * @param {Object} value - form value
 * @param {Object} queryDef - query definition
 * @param {String} startPath - starting path
 * @returns {Boolean} true if valid
 */
export function hasValidQueryValues (value, queryDef, startPath) {
  // need to invert this for the next major rev since returning true is contrary to the function name
  if (!queryDef) {
    return true
  }

  const query = populateQuery(value, queryDef, startPath)
  if (!query) {
    return false
  }
  return Object.keys(query).every((key) => String(query[key]) !== '')
}

export class BunsenModelPath {
  static objectPrePath (model, pathSeg) {
    if (model.properties[pathSeg]) {
      return 'properties'
    }
    const dependency = _.findKey(model.dependencies, (dep) => {
      return !Array.isArray(dep) && dep.properties !== undefined && dep.properties[pathSeg] !== undefined
    })
    if (dependency) {
      return `dependencies.${dependency}.properties`
    }
  }
  static arrayPrePath (model, pathSeg) {
    if (Array.isArray(model.items) &&
      !isNaN(pathSeg) &&
      _.get(model.items, pathSeg) === undefined &&
      typeof model.additionalItems === 'object'
    ) {
      return 'additionalItems'
    } else if (model.items !== undefined) {
      return 'items'
    }
  }
  static getPrePath (model, pathSeg) {
    if (model.type === 'object') {
      return BunsenModelPath.objectPrePath(model, pathSeg)
    } else if (model.type === 'array') {
      return BunsenModelPath.arrayPrePath(model, pathSeg)
    }
  }
  static createPathSegment (curModel, prePath, pathSeg) {
    if (prePath) {
      if (prePath === 'items' && !Array.isArray(curModel.items) || prePath === 'additionalItems') {
        // skip path segment for array items
        return prePath
      } else {
        return `${prePath}.${pathSeg}`
      }
    }
    return pathSeg
  }

  constructor (model, initialPath) {
    this._model = model
    this._currentModel = model
    this._path = []
    this._valid = true
    if (typeof initialPath === 'string') {
      initialPath = initialPath.split('.')
      this.append(initialPath)
    }
  }

  append (pathSeg) {
    if (!this._valid || pathSeg === undefined) {
      return
    }
    if (Array.isArray(pathSeg)) {
      pathSeg.forEach(seg => this.append(seg))
      return
    }
    const curModel = this._currentModel
    const prePath = BunsenModelPath.getPrePath(curModel, pathSeg)
    const nextSeg = BunsenModelPath.createPathSegment(curModel, prePath, pathSeg)
    const nextModel = _.get(curModel, nextSeg)
    if (nextModel === undefined) {
      this._valid = false
      return
    }
    this._currentModel = nextModel
    this._path.push(nextSeg)
  }

  pop () {
    if (this._valid) {
      const last = this._path.pop()
      this._currentModel = _.get(this._model, this.modelPath())
      return last
    }
  }

  last () {
    return _.last(this._path)
  }

  toString () {
    if (this._valid) {
      return this._path.join('.')
    }
    return ''
  }

  valuePath () {
    if (this._valid) {
      return this._path.map((element) => element.split('.').pop())
    }
  }

  modelPath () {
    if (this._valid) {
      return this._path.join('.')
    }
  }

  get isValid () {
    return this._valid
  }

  concat (additionalPath) {
    const newPath = new BunsenModelPath(this._model)
    newPath._currentModel = this._currentModel
    newPath._path = _.clone(this._path)
    newPath.append(additionalPath)
    return newPath
  }
}

export function clearInternals (value) {
  if (Array.isArray(value)) {
    value.forEach(function (subValue) {
      clearInternals(subValue)
    })
  } else if (typeof value === 'object') {
    delete value._internal
    _.forEach(value, function (subValue) {
      clearInternals(subValue)
    })
  }
  return value
}
<commit_msg>Fixed dotted notation appending in BunsenModelPath<commit_after>import './typedefs'
import _ from 'lodash'

/* eslint-disable complexity */
/**
 * Check if the given model includes a required field
 * @param {Model} model - the model definition
 * @returns {Boolean} true if model or any child has a required field
 */
export function doesModelContainRequiredField (model) {
  if (Array.isArray(model)) {
    for (let i = 0, len = model.length; i < len; i++) {
      if (doesModelContainRequiredField(model[i])) {
        return true
      }
    }

    return false
  }

  if (!_.isPlainObject(model)) {
    return false
  }

  if (!_.isEmpty(model.required)) {
    return true
  }

  for (let key in model) {
    if (doesModelContainRequiredField(model[key])) {
      return true
    }
  }
}
/* eslint-enable complexity */

/**
 * Get the user-visible label from the model instance
 * @param {String} label - the label override from the view
 * @param {BunsenModel} model - the model (to get title from if present)
 * @param {String} id - the dotted refeference to this object
 * @returns {String} the user-visible label
 */
export function getLabel (label, model, id) {
  const title = model ? model.title : null
  let idLabel = (id) ? _.startCase(id.split('.').slice(-1)[0]) : ''
  idLabel = _.capitalize(idLabel.toLowerCase())
  return `${label || title || idLabel}`
}

/**
 * Convert a model reference to a proper path in the model schema
 *
 * hero.firstName => hero.attributes.firstName
 * foo.bar.baz => foo.attributes.bar.attributes.baz
 *
 * Leading or trailing '.' mess up our trivial split().join() and aren't valid anyway, so we
 * handle them specially, undefined being passed into _.get() will yield undefined, and display
 * the error we want to display when the model reference is invalid, so we return undefined
 *
 * hero. => undefined
 * .hero => undefined
 *
 * @param {String} reference - the dotted reference to the model
 * @param {String} [dependencyReference] - the dotted reference to the model dependency
 * @returns {String[]} the proper dotted path in the model schema (or undefined if it's a bad path)
 */
export function _getModelPath (reference, dependencyReference) {
  const pattern = /^[^\.](.*[^\.])?$/ // eslint-disable-line no-useless-escape
  if (!pattern.test(reference)) {
    return
  }

  const pathArr = reference.split('.')
  if (dependencyReference) {
    const dependencyName = dependencyReference.split('.').pop()
    pathArr.splice(-1, 0, '$dependencies', dependencyName)
  }

  return pathArr
}

/**
 * Builds a dotted path to a submodel of a nested value based on its dotted path within
 * the value object
 *
 * @export
 * @param {BunsenModel} model The bunsen model of the value
 * @param {String} reference Dotted path to the value within the value object
 * @returns {BunsenModelPath} Model path object with the path to the model of the specified
 * value
 */
export function getModelPath (model, reference) {
  return new BunsenModelPath(model, reference)
}

/**
 * Get the sub-model for a given dotted reference
 * @param {BunsenModel} model - the starting model
 * @param {String} reference - the reference to fetch
 * @param {String} [dependencyReference] - the dotted reference to the model dependency (if any)
 * @returns {BunsenModel} the sub-model
 */
export function getSubModel (model, reference, dependencyReference) {
  const path = getModelPath(model, reference).modelPath()
  return _.get(model, path)
}

/**
 * Figure out an initial value based on existing value, initialValue, and model
 * @param {String} id - the dotted path to this value in the formValue
 * @param {Object} formValue - the existing value of the whole form
 * @param {*} initialValue - the initialValue passed in to the component
 * @param {Object} model - the model to check for a default value in
 * @param {*} defaultValue - the default value to use if no other defaults are found
 * @returns {*} the initial value
 */
export function getInitialValue (id, formValue, initialValue, model, defaultValue = '') {
  const values = [_.get(formValue, id), initialValue, model['default']]
  const value = _.find(values, (value) => value !== undefined)

  if (value !== undefined) {
    return value
  }

  return defaultValue
}

/**
 * mine an object for a value
 * @param {Object} obj the object to mine
 * @param {String} valuePath the path to the value in the object
 * @param {String} startPath - start path
 * @returns {String} the value
 */
export function findValue (obj, valuePath, startPath = '') {
  const depths = startPath.split('.')
  const valueLevels = valuePath.split('./')
  const parentLevels = valueLevels.filter((element) => element === '.')
  const valueKey = valueLevels.pop()
  const absValuePath = _.without(depths.slice(0, depths.length - parentLevels.length - 1), '', '.').join('.')
  const absValueKey = [absValuePath, valueKey].join('.').replace(/^\./, '')
  return _.get(obj, absValueKey)
}

/* eslint-disable complexity */
/**
 * finds variables in orch-style queryParam values
 * @param {Object} valueObj - the value object to mine for query values
 * @param {String} queryJSON - the stringified filter object to parse
 * @param {String} startPath - start path
 * @param {Boolean} allowEmpty - allow empty values to be represented by ''
 * @returns {String} the populated filter
 * @throws Will throw when any value at the resolved path is empty
 */
export function parseVariables (valueObj, queryJSON, startPath = '', allowEmpty = false) {
  if (!queryJSON) {
    return ''
  }

  if (queryJSON.indexOf('${') !== -1) {
    const valueVariable = queryJSON.split('${')[1].split('}')[0]
    let result = findValue(valueObj, valueVariable, startPath)

    if (result === undefined || String(result) === '') {
      if (allowEmpty) {
        result = ''
      } else {
        throw new Error(`value at ${valueVariable} is empty`)
      }
    }

    const newQueryJson = queryJSON.split('${' + valueVariable + '}').join(result)
    return parseVariables(valueObj, newQueryJson, startPath, allowEmpty)
  }

  return queryJSON
}
/* eslint-enable complexity */

/**
 * grooms the query for variables using a ${variableName} syntax and populates the values
 * @param {Object} valueObj - the value object to mine for values
 * @param {Object} query - the definition from the model schema
 * @param {String} startPath - start path
 * @returns {Object} the populated query
 */
export function populateQuery (valueObj, query, startPath = '') {
  try {
    return JSON.parse(parseVariables(valueObj, JSON.stringify(query || {}), startPath))
  } catch (err) {
    return null
  }
}

/**
 * Checks if the query object has valid values
 * @param {Object} value - form value
 * @param {Object} queryDef - query definition
 * @param {String} startPath - starting path
 * @returns {Boolean} true if valid
 */
export function hasValidQueryValues (value, queryDef, startPath) {
  // need to invert this for the next major rev since returning true is contrary to the function name
  if (!queryDef) {
    return true
  }

  const query = populateQuery(value, queryDef, startPath)
  if (!query) {
    return false
  }
  return Object.keys(query).every((key) => String(query[key]) !== '')
}

export class BunsenModelPath {
  static objectPrePath (model, pathSeg) {
    if (model.properties[pathSeg]) {
      return 'properties'
    }
    const dependency = _.findKey(model.dependencies, (dep) => {
      return !Array.isArray(dep) && dep.properties !== undefined && dep.properties[pathSeg] !== undefined
    })
    if (dependency) {
      return `dependencies.${dependency}.properties`
    }
  }
  static arrayPrePath (model, pathSeg) {
    if (Array.isArray(model.items) &&
      !isNaN(pathSeg) &&
      _.get(model.items, pathSeg) === undefined &&
      typeof model.additionalItems === 'object'
    ) {
      return 'additionalItems'
    } else if (model.items !== undefined) {
      return 'items'
    }
  }
  static getPrePath (model, pathSeg) {
    if (model.type === 'object') {
      return BunsenModelPath.objectPrePath(model, pathSeg)
    } else if (model.type === 'array') {
      return BunsenModelPath.arrayPrePath(model, pathSeg)
    }
  }
  static createPathSegment (curModel, prePath, pathSeg) {
    if (prePath) {
      if (prePath === 'items' && !Array.isArray(curModel.items) || prePath === 'additionalItems') {
        // skip path segment for array items
        return prePath
      } else {
        return `${prePath}.${pathSeg}`
      }
    }
    return pathSeg
  }

  constructor (model, initialPath) {
    this._model = model
    this._currentModel = model
    this._path = []
    this._valid = true
    if (typeof initialPath === 'string') {
      initialPath = initialPath.split('.')
      this.append(initialPath)
    }
  }

  append (pathSeg) {
    if (!this._valid || pathSeg === undefined) {
      return
    }
    let segments
    if (Array.isArray(pathSeg)) {
      segments = pathSeg
    } else {
      segments = pathSeg.split('.')
    }
    segments.forEach(seg => this._append(seg))
  }

  _append (pathSeg) {
    const curModel = this._currentModel
    const prePath = BunsenModelPath.getPrePath(curModel, pathSeg)
    const nextSeg = BunsenModelPath.createPathSegment(curModel, prePath, pathSeg)
    const nextModel = _.get(curModel, nextSeg)
    if (nextModel === undefined) {
      this._valid = false
      return
    }
    this._currentModel = nextModel
    this._path.push(nextSeg)
  }

  pop () {
    if (this._valid) {
      const last = this._path.pop()
      this._currentModel = _.get(this._model, this.modelPath())
      return last
    }
  }

  last () {
    return _.last(this._path)
  }

  toString () {
    if (this._valid) {
      return this._path.join('.')
    }
    return ''
  }

  valuePath () {
    if (this._valid) {
      return this._path.map((element) => element.split('.').pop())
    }
  }

  modelPath () {
    if (this._valid) {
      return this._path.join('.')
    }
  }

  get isValid () {
    return this._valid
  }

  concat (additionalPath) {
    const newPath = new BunsenModelPath(this._model)
    newPath._currentModel = this._currentModel
    newPath._path = _.clone(this._path)
    newPath.append(additionalPath)
    return newPath
  }
}

export function clearInternals (value) {
  if (Array.isArray(value)) {
    value.forEach(function (subValue) {
      clearInternals(subValue)
    })
  } else if (typeof value === 'object') {
    delete value._internal
    _.forEach(value, function (subValue) {
      clearInternals(subValue)
    })
  }
  return value
}
<|endoftext|>