<commit_before>import request from 'request'
import cheerio from 'cheerio'
import { merge } from 'lodash'
import getImageSize from '../services/GetImageSize'

const httpget = url => new Promise((resolve, reject) =>
  request.get(url, {gzip: true}, (err, res, body) =>
    err ? reject(err) : resolve([res, body])))

const parse = body => {
  const $ = cheerio.load(body)
  const metaContent = name => $(`meta[property="${name}"]`).attr('content')
  return {
    title: metaContent('og:title') || $('title').text(),
    description: metaContent('og:description'),
    image_url: metaContent('og:image')
  }
}

const LinkPreview = bookshelf.Model.extend({
  tableName: 'link_previews'

}, {
  parse,

  queue: url => {
    return LinkPreview.forge({url, created_at: new Date()}).save()
    .then(({ id }) => Queue.classMethod('LinkPreview', 'populate', {id}, 0))
    .catch(err => {
      if (err.message && !err.message.includes('duplicate key value')) {
        throw err
      }
    })
  },

  populate: ({ id }) => {
    return LinkPreview.find(id).then(preview =>
      httpget(preview.get('url'))
      .then(([ res, body ]) => {
        const attrs = merge(parse(body), {updated_at: new Date(), done: true})

        return (attrs.image_url
          ? getImageSize(attrs.image_url).catch(err => null) // eslint-disable-line handle-callback-err
          : Promise.resolve())
        .then(size => {
          if (!size) return
          attrs.image_width = size.width
          attrs.image_height = size.height
        })
        .then(() => preview.save(attrs, {patch: true}))
      }))
  },

  find: (idOrUrl, opts) => {
    const attr = isNaN(Number(idOrUrl)) ? 'url' : 'id'
    return LinkPreview.where(attr, idOrUrl).fetch(opts)
  }
})

module.exports = LinkPreview
<commit_msg>handle http errors in LinkPreview.populate<commit_after>import request from 'request'
import cheerio from 'cheerio'
import { merge } from 'lodash'
import getImageSize from '../services/GetImageSize'

const httpget = url => new Promise((resolve, reject) =>
  request.get(url, {gzip: true}, (err, res, body) =>
    err ? reject(err) : resolve([res, body])))

const parse = body => {
  const $ = cheerio.load(body)
  const metaContent = name => $(`meta[property="${name}"]`).attr('content')
  return {
    title: metaContent('og:title') || $('title').text(),
    description: metaContent('og:description'),
    image_url: metaContent('og:image')
  }
}

const LinkPreview = bookshelf.Model.extend({
  tableName: 'link_previews'

}, {
  parse,

  queue: url => {
    return LinkPreview.forge({url, created_at: new Date()}).save()
    .then(({ id }) => Queue.classMethod('LinkPreview', 'populate', {id}, 0))
    .catch(err => {
      if (err.message && !err.message.includes('duplicate key value')) {
        throw err
      }
    })
  },

  populate: ({ id }) => {
    const doneAttrs = () => ({updated_at: new Date(), done: true})
    return LinkPreview.find(id).then(preview =>
      httpget(preview.get('url'))
      .catch(err => preview.save(doneAttrs()) && null) // eslint-disable-line handle-callback-err
      .then(resp => {
        if (!resp) return
        const body = resp[1]
        const attrs = merge(parse(body), doneAttrs())

        return (attrs.image_url
          ? getImageSize(attrs.image_url).catch(err => null) // eslint-disable-line handle-callback-err
          : Promise.resolve())
        .then(size => {
          if (!size) return
          attrs.image_width = size.width
          attrs.image_height = size.height
        })
        .then(() => preview.save(attrs, {patch: true}))
      }))
  },

  find: (idOrUrl, opts) => {
    const attr = isNaN(Number(idOrUrl)) ? 'url' : 'id'
    return LinkPreview.where(attr, idOrUrl).fetch(opts)
  }
})

module.exports = LinkPreview
<|endoftext|>


<commit_before>'use strict';

(function() {
  describe('MEAN controllers', function() {
        describe('StationsController', function() {

        });
  });
}());<commit_msg>stations angular controller 역 목록 가져오는 테스트 코드 작성. 확인은 아직.<commit_after>'use strict';

(function() {
  describe('MEAN controllers', function() {
        describe('StationsController', function() {
          var scope, ctrl, $mockBackend;

          beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
            $monckBackend = _$httpBackend_;
            $mockBackend.expectGET('stations/').
                respond([{name:'신세이비조 역'}, {name:'세이비조 역'}]);

            scope = $rootScope.$new();
            ctrl = $controller('StationsController', {$scope: scope});
          }));

          it('stations 모델 두개가 fetch 한 것과 동일해야 함', function() {
            expect(scope.stations).toEqualData([]);
            $mockBackend.flush();

            expect(scope.stations).toEqualData(
              [{name:'신세이비조 역'}, {name:'세이비조 역'}]);
          });
        });
  });
}());<|endoftext|>


<commit_before>'use strict';

var htmlToUnicode = require('../../gl3d/lib/html2unicode');
var str2RGBArray = require('../../gl3d/lib/str2rgbarray');

function Axes2DOptions(scene) {
  this.title = '';

  this.gl = scene.gl;
  this.pixelRatio = scene.pixelRatio;

  this.screenBox = [0,0,1,1];
  this.viewBox   = [0,0,1,1];
  this.dataBox   = [-1,-1,1,1];

  this.borderLineEnable = [false,false,false,false];
  this.borderLineWidth = [1,1,1,1];
  this.borderLineColor = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];

  this.ticks = [[], []];
  this.tickEnable = [true, true, false, false];
  this.tickPad   = [15,15,15,15];
  this.tickAngle = [0,0,0,0];
  this.tickColor = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];
  this.tickMarkWidth = [0,0,0,0];
  this.tickMarkLength = [0,0,0,0];

  this.labels = ['x', 'y'];
  this.labelEnable = [true,true,false,false];
  this.labelAngle  = [0,Math.PI/2,0,3.0*Math.PI/2];
  this.labelPad    = [15,15,15,15];
  this.labelSize   = [12,12];
  this.labelFont   = ['sans-serif', 'sans-serif'];
  this.labelColor  = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];

  this.title       = '';
  this.titleEnable = true;
  this.titleCenter = [0,0,0,0];
  this.titleAngle  = 0;
  this.titleColor  = [0,0,0,1];
  this.titleFont   = 'sans-serif';
  this.titleSize   = 18;

  this.gridLineEnable = [true,true];
  this.gridLineColor  = [
    [0,0,0,0.5],
    [0,0,0,0.5]
  ];
  this.gridLineWidth  = [1,1];

  this.zeroLineEnable = [true,true];
  this.zeroLineWidth  = [1,1];
  this.zeroLineColor  = [
    [0,0,0,1],
    [0,0,0,1]
  ];
}

var proto = Axes2DOptions.prototype;

var AXES = [ 'xaxis', 'yaxis' ];

proto.merge = function(options) {

  //Titles are rendered in SVG
  this.titleEnable = false;

  for(var i=0; i<2; ++i) {
    var ax = options[AXES[i]];
    var axTitle = /Click to enter .+ title/.test(ax.title) ?
        '' :
        ax.title;

    for(var j=0; j<=2; j+=2) {
      this.labels[i+j]     = htmlToUnicode(axTitle);
      this.labelEnable[i+j] = false;
      this.labelColor[i+j] = str2RGBArray(ax.titlefont.color);
      this.labelFont[i+j]  = ax.titlefont.family;
      this.labelSize[i+j]  = ax.titlefont.size;

      this.tickColor[i+j] = str2RGBArray(ax.tickcolor);

      if(ax.tickangle === 'auto') {
        this.tickAngle[i+j] = 0;
      } else {
        this.tickAngle[i+j] = -ax.tickangle;
      }

      this.borderLineEnable[i+j] = ax.showline;
      this.borderLineColor[i+j] = str2RGBArray(ax.linecolor);
      this.borderLineWidth[i+j] = ax.linewidth;
    }

    this.tickEnable[i]     = ax.showticklabels;
    this.labelEnable[i]    = ax.showticklabels;
    this.tickMarkLength[i] = ax.ticklen;
    this.tickMarkWidth[i]  = ax.tickwidth;

    this.gridLineEnable[i]  = ax.showgrid;
    this.gridLineColor[i]   = str2RGBArray(ax.gridcolor);
    this.gridLineWidth[i]   = ax.gridwidth;

    this.zeroLineEnable[i]  = ax.zeroline;
    this.zeroLineColor[i]   = str2RGBArray(ax.zerolinecolor);
    this.zeroLineWidth[i]   = ax.zerolinewidth;
  }
};

function createAxes2D(scene) {
  return new Axes2DOptions(scene);
}

module.exports = createAxes2D;
<commit_msg>pass background color property<commit_after>'use strict';

var htmlToUnicode = require('../../gl3d/lib/html2unicode');
var str2RGBArray = require('../../gl3d/lib/str2rgbarray');

function Axes2DOptions(scene) {
  this.title = '';

  this.gl = scene.gl;
  this.pixelRatio = scene.pixelRatio;

  this.screenBox = [0,0,1,1];
  this.viewBox   = [0,0,1,1];
  this.dataBox   = [-1,-1,1,1];

  this.borderLineEnable = [false,false,false,false];
  this.borderLineWidth = [1,1,1,1];
  this.borderLineColor = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];

  this.ticks = [[], []];
  this.tickEnable = [true, true, false, false];
  this.tickPad   = [15,15,15,15];
  this.tickAngle = [0,0,0,0];
  this.tickColor = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];
  this.tickMarkWidth = [0,0,0,0];
  this.tickMarkLength = [0,0,0,0];

  this.labels = ['x', 'y'];
  this.labelEnable = [true,true,false,false];
  this.labelAngle  = [0,Math.PI/2,0,3.0*Math.PI/2];
  this.labelPad    = [15,15,15,15];
  this.labelSize   = [12,12];
  this.labelFont   = ['sans-serif', 'sans-serif'];
  this.labelColor  = [
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1],
    [0,0,0,1]
  ];

  this.title       = '';
  this.titleEnable = true;
  this.titleCenter = [0,0,0,0];
  this.titleAngle  = 0;
  this.titleColor  = [0,0,0,1];
  this.titleFont   = 'sans-serif';
  this.titleSize   = 18;

  this.gridLineEnable = [true,true];
  this.gridLineColor  = [
    [0,0,0,0.5],
    [0,0,0,0.5]
  ];
  this.gridLineWidth  = [1,1];

  this.zeroLineEnable = [true,true];
  this.zeroLineWidth  = [1,1];
  this.zeroLineColor  = [
    [0,0,0,1],
    [0,0,0,1]
  ];

  this.borderColor      = [0,0,0,0];
  this.backgroundColor  = [0,0,0,0];
}

var proto = Axes2DOptions.prototype;

var AXES = [ 'xaxis', 'yaxis' ];

proto.merge = function(options) {

  //Titles are rendered in SVG
  this.titleEnable = false;
  this.backgroundColor = str2RGBArray(options.plot_bgcolor);

  for(var i=0; i<2; ++i) {
    var ax = options[AXES[i]];
    var axTitle = /Click to enter .+ title/.test(ax.title) ?
        '' :
        ax.title;

    for(var j=0; j<=2; j+=2) {
      this.labels[i+j]     = htmlToUnicode(axTitle);
      this.labelEnable[i+j] = false;
      this.labelColor[i+j] = str2RGBArray(ax.titlefont.color);
      this.labelFont[i+j]  = ax.titlefont.family;
      this.labelSize[i+j]  = ax.titlefont.size;

      this.tickColor[i+j] = str2RGBArray(ax.tickcolor);

      if(ax.tickangle === 'auto') {
        this.tickAngle[i+j] = 0;
      } else {
        this.tickAngle[i+j] = -ax.tickangle;
      }

      this.borderLineEnable[i+j] = ax.showline;
      this.borderLineColor[i+j] = str2RGBArray(ax.linecolor);
      this.borderLineWidth[i+j] = ax.linewidth;
    }

    this.tickEnable[i]     = ax.showticklabels;
    this.labelEnable[i]    = ax.showticklabels;
    this.tickMarkLength[i] = ax.ticklen;
    this.tickMarkWidth[i]  = ax.tickwidth;

    this.gridLineEnable[i]  = ax.showgrid;
    this.gridLineColor[i]   = str2RGBArray(ax.gridcolor);
    this.gridLineWidth[i]   = ax.gridwidth;

    this.zeroLineEnable[i]  = ax.zeroline;
    this.zeroLineColor[i]   = str2RGBArray(ax.zerolinecolor);
    this.zeroLineWidth[i]   = ax.zerolinewidth;
  }
};

function createAxes2D(scene) {
  return new Axes2DOptions(scene);
}

module.exports = createAxes2D;
<|endoftext|>


<commit_before>const db = require('../db.js');

module.exports = (self, offSet) => {
  const queryString = `SELECT UserLanguageLevel.User FROM UserLanguageLevel
                         INNER JOIN LanguageLevel
                           ON UserLanguageLevel.LanguageLevel = LanguageLevel.id
                         INNER JOIN Language
                           ON LanguageLevel.Language = Language.id
                         INNER JOIN Level
                           ON LanguageLevel.Level = Level.id
                         WHERE Language.name IN (${self.canTeach.map(language => language[0]).join(',')}) AND Level.name IN ('fluent', 'native')
                      `;
  // db.query();
};
<commit_msg>Refactor to snakecase<commit_after>const db = require('../db.js');

module.exports = (self, offSet) => {
  const queryString = `(
                        SELECT users_languages_levels.user AS teach_id FROM users_languages_levels
                         INNER JOIN languages_levels
                           ON users_languages_levels.languages_levels = languages_levels.id
                         INNER JOIN languages
                           ON languages_levels.language = language.id
                         INNER JOIN levels
                           ON languages_levels.level = level.id
                         WHERE language.name IN (${self.canTeach.map(language => language[0]).join(',')}) AND level.name IN ('fluent', 'native')
                        ) AS teach
                        INNER JOIN

                      `;
  // db.query();
};
<|endoftext|>


<commit_before>"use strict";

$(function() {
  // Back to top
  (function () {
    var $back2top = $("#back2top");

    $(window).scroll(function () {
      if($(window).scrollTop() > 100) {
        $back2top.animate({
          opacity: 1
        }, 'slow', 'ease');
      } else {
        $back2top.animate({
          opacity: 0
        }, 'slow', 'ease');
      }
    });

    function smoothScroll(el, to, duration) {
      if (duration < 0) {
        return;
      }
      var difference = to - $(window).scrollTop();
      var perTick = difference / duration * 10;
      setTimeout(function() {
        if (!isNaN(parseInt(perTick, 10))) {
          window.scrollTo(0, $(window).scrollTop() + perTick);
          smoothScroll(el, to, duration - 10);
        }
      }.bind(this), 10);
    }

    $back2top.click(function (e) {
      e.preventDefault();
      smoothScroll($(window), 0, 200);
    });
  })();

  // Post follow sidebar
  (function() {
    var $sidebar    = $("#sidebar"),
        $tags       = $('.tags'),
        $headerlink = $(".headerlink"),
        $toclink    = $(".toc-link");

    if ($sidebar.length) {
      var minScrollTop = $sidebar.offset().top,
          maxScrollTop = $tags.offset().top - $sidebar.height();

      $(window).load(function() {
        maxScrollTop = $tags.offset().top - $sidebar.height();
      });
      $(window).scroll(function () {
        var scrollTop     = $(window).scrollTop();

        if (minScrollTop < scrollTop && scrollTop < maxScrollTop) {
          $sidebar.css({
            "position": "fixed",
            "top": "15px"
          });
        } else if (scrollTop > maxScrollTop) {
          $sidebar.css({
            "position": "absolute",
            "top": maxScrollTop
          });
        }else {
          $sidebar.css({
            "position": "absolute",
            "top": minScrollTop
          });
        }

        for(var i = 0; i < $headerlink.length; i++) {
          var eqLength = i + 1 === $headerlink.length,
              minTop = $($headerlink[i]).offset().top - 20,
              maxTop = eqLength ? Infinity : $($headerlink[i+1]).offset().top - 20;

          if (minTop < scrollTop && scrollTop <= maxTop) {
            $($toclink[i]).addClass("active");
          } else {
            $($toclink[i]).removeClass("active");
          }
        }
      });
    }
  })();
});
<commit_msg>fix sidebar position<commit_after>"use strict";

$(function() {
  // Back to top
  (function () {
    var $back2top = $("#back2top");

    $(window).scroll(function () {
      if($(window).scrollTop() > 100) {
        $back2top.animate({
          opacity: 1
        }, 'slow', 'ease');
      } else {
        $back2top.animate({
          opacity: 0
        }, 'slow', 'ease');
      }
    });

    function smoothScroll(el, to, duration) {
      if (duration < 0) {
        return;
      }
      var difference = to - $(window).scrollTop();
      var perTick = difference / duration * 10;
      setTimeout(function() {
        if (!isNaN(parseInt(perTick, 10))) {
          window.scrollTo(0, $(window).scrollTop() + perTick);
          smoothScroll(el, to, duration - 10);
        }
      }.bind(this), 10);
    }

    $back2top.click(function (e) {
      e.preventDefault();
      smoothScroll($(window), 0, 200);
    });
  })();

  // Post follow sidebar
  (function() {
    var $sidebar    = $("#sidebar"),
        $tags       = $('.tags'),
        $headerlink = $(".headerlink"),
        $toclink    = $(".toc-link");

    if ($sidebar.length) {
      var minScrollTop = $sidebar.offset().top - 15,
          maxScrollTop = $tags.offset().top - $sidebar.height() - 15;

      $(window).load(function() {
        maxScrollTop = $tags.offset().top - $sidebar.height() - 15;
      });
      $(window).scroll(function () {
        var scrollTop     = $(window).scrollTop();

        if (minScrollTop < scrollTop && scrollTop < maxScrollTop) {
          $sidebar.css({
            "position": "fixed",
            "top": "15px"
          });
        } else if (scrollTop > maxScrollTop) {
          $sidebar.css({
            "position": "absolute",
            "top": maxScrollTop + 15
          });
        } else {
          $sidebar.css({
            "position": "absolute",
            "top": minScrollTop + 15
          });
        }

        for(var i = 0; i < $headerlink.length; i++) {
          var eqLength = i + 1 === $headerlink.length,
              minTop = $($headerlink[i]).offset().top - 20,
              maxTop = eqLength ? Infinity : $($headerlink[i+1]).offset().top - 20;

          if (minTop < scrollTop && scrollTop <= maxTop) {
            $($toclink[i]).addClass("active");
          } else {
            $($toclink[i]).removeClass("active");
          }
        }
      });
    }
  })();
});
<|endoftext|>


<commit_before>import Client from './client';
import getScope from './get-scope.ts';
import getArgs from './get-args';

export default async (sentry, error, apiUrl, configFiles) => {
  let user = null;
  let team = null;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
  }

  sentry.withScope(scope => {
    if (user) {
      const spec = {
        email: user.email,
        id: user.uid
      };

      if (user.username) {
        spec.username = user.username;
      }

      if (user.name) {
        spec.name = user.name;
      }

      scope.setUser(spec);
    }

    if (team) {
      scope.setTag('currentTeam', team.id);
    }

    // Report process.argv without sensitive data
    let args
    try {
      args = getArgs(process.argv.slice(2), {});
    } catch (_) {}

    if (args) {
      const flags = ['--env', '--build-env', '--token']
      for (const flag of flags) {
        if (args[flag]) args[flag] = 'REDACTED';
      }
      if (args._.length >= 4 && args._[0].startsWith('secret') && args._[1] === 'add') {
        args._[3] = 'REDACTED';
      }
      scope.setExtra('args', args);
    } else {
      scope.setExtra('args', 'Unable to parse args');
    }

    sentry.captureException(error);
  });

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<commit_msg>Add information when team/user/args parsing fails to Sentry reports (#2635)<commit_after>import Client from './client';
import getScope from './get-scope.ts';
import getArgs from './get-args';

export default async (sentry, error, apiUrl, configFiles) => {
  let user;
  let team;
  let scopeError;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
    scopeError = err;
  }

  sentry.withScope(scope => {
    if (user) {
      const spec = {
        email: user.email,
        id: user.uid
      };

      if (user.username) {
        spec.username = user.username;
      }

      if (user.name) {
        spec.name = user.name;
      }

      scope.setUser(spec);
    }

    if (team) {
      scope.setTag('currentTeam', team.id);
    }

    if (scopeError) {
      scope.setExtra('scopeError', {
        name: scopeError.name,
        message: scopeError.message,
        stack: scopeError.stack
      });
    }

    // Report `process.argv` without sensitive data
    let args;
    let argsError;
    try {
      args = getArgs(process.argv.slice(2), {});
    } catch (err) {
      argsError = err;
    }

    if (args) {
      const flags = ['--env', '--build-env', '--token'];
      for (const flag of flags) {
        if (args[flag]) args[flag] = 'REDACTED';
      }
      if (
        args._.length >= 4 &&
        args._[0].startsWith('secret') &&
        args._[1] === 'add'
      ) {
        args._[3] = 'REDACTED';
      }
      scope.setExtra('args', args);
    } else {
      let msg = 'Unable to parse args';
      if (argsError) {
        msg += `: ${argsError}`;
      }
      scope.setExtra('args', msg);
    }

    // Report information about the version of `node` being used
    scope.setExtra('node', {
      execPath: process.execPath,
      version: process.version
    });

    sentry.captureException(error);
  });

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<|endoftext|>


<commit_before>var eventQueue = [];
var svg;
var element;
var drawingArea;
var width;
var height;
var volume = 0.6;
var ULTIMATE_DREAM_KILLER = false;

var scale_factor = 6,
    note_overlap = 2,
    note_timeout = 300,
    current_notes = 0,
    max_life = 20000;

var svg_background_color_online = '#0288D1',
    svg_background_color_offline = '#E91E63'
    svg_text_color = '#FFFFFF',
    newuser_box_color = 'rgb(41, 128, 185)',
    push_color = 'rgb(155, 89, 182)',
    issue_color = 'rgb(46, 204, 113)',
    pull_request_color = 'rgb(46, 204, 113)',
    comment_color = 'rgb(46, 204, 113)'
    edit_color = '#fff',
    total_sounds = 51,
    total_edits = 0;

    var celesta = [],
        clav = [],
        swells = [],
        all_loaded = false;

var socket = io(document.location.hostname);
socket.on('github', function (data) {
  $('.online-users-count').html(data.connected_users);
  data.data.forEach(function(event){
    if(!isEventInQueue(event) || shouldEventBeIgnored(event)){
      eventQueue.push(event);
    }
  });
  // Don't let the eventQueue grow more than 100
  if (eventQueue.length > 100) eventQueue = eventQueue.slice(0, 100);
});

socket.on('connect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_online);
      $('header').css('background-color', svg_background_color_online);
      $('.offline-text').css('visibility', 'hidden');
      $('.events-remaining-text').css('visibility', 'hidden');
      $('.events-remaining-value').css('visibility', 'hidden');
    }
});

socket.on('disconnect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');

    }
});

socket.on('error', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');
    }
});

/**
* This function checks whether an event is already in the queue
*/
function isEventInQueue(event){
  for(var i=0; i<eventQueue.length; i++){
    if(eventQueue[i].id == event.id)
      return true;
  }
  return false;
}

/**
 * This function adds a filter for events that we don't want to hear.
 *
 * To extend this function, simply add return true for events that should be filtered.
 */
function shouldEventBeIgnored(event){
  // This adds an easter egg to only play closed PRs
  if (!!ULTIMATE_DREAM_KILLER)
    return (event.type !== "PullRequestEvent" || event.action !== "closed");

  return false;
}

$(function(){
  element = document.documentElement;
  drawingArea = document.getElementsByTagName('#area')[0];
  width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
  height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
  $('svg').css('background-color', svg_background_color_online);
  $('header').css('background-color', svg_background_color_online);
  $('svg text').css('color', svg_text_color);
  $('#volumeSlider').slider({
    'max': 100,
    'min': 0,
    'value': volume*100,
    'slide' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    },
    'change' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    }
  });

  $('#eventConsumptionSlider').slider({
    'min': 1,
    'max': 100
  });

  // Main drawing area
  svg = d3.select("#area").append("svg");
  svg.attr({width: width, height: height});
  svg.style('background-color', svg_background_color_online);

  // For window resizes
  var update_window = function() {
      width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
      height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
      svg.attr("width", width).attr("height", height);
  }
  window.onresize = update_window;
  update_window();

  var loaded_sounds = 0;
  var sound_load = function(r) {
      loaded_sounds += 1;
      if (loaded_sounds == total_sounds) {
          all_loaded = true;
          setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000));
          // Starting the second exchange makes music a bad experience
          // setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 2000));
      }
  }

  // Load sounds
  for (var i = 1; i <= 24; i++) {
      if (i > 9) {
          fn = 'c0' + i;
      } else {
          fn = 'c00' + i;
      }
      celesta.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.mp3'],
          volume : 0.7,
          onload : sound_load(),
          buffer: true,
      }))
      clav.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.mp3'],
          volume : 0.4,
          onload : sound_load(),
          buffer: true,
      }))
  }

  for (var i = 1; i <= 3; i++) {
      swells.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.mp3'],
          volume : 1,
          onload : sound_load(),
          buffer: true,
      }));
  }

  Howler.volume(volume);

});

/**
* Randomly selects a swell sound and plays it
*/
function playRandomSwell() {
    var index = Math.round(Math.random() * (swells.length - 1));
    swells[index].play();
}

/**
* Plays a sound(celesta and clav) based on passed parameters
*/
function playSound(size, type) {
    var max_pitch = 100.0;
    var log_used = 1.0715307808111486871978099;
    var pitch = 100 - Math.min(max_pitch, Math.log(size + log_used) / Math.log(log_used));
    var index = Math.floor(pitch / 100.0 * Object.keys(celesta).length);
    var fuzz = Math.floor(Math.random() * 4) - 2;
    index += fuzz;
    index = Math.min(Object.keys(celesta).length - 1, index);
    index = Math.max(1, index);
    if (current_notes < note_overlap) {
        current_notes++;
        if (type == 'IssuesEvent' || type == 'IssueCommentEvent') {
            clav[index].play();
        } else if(type == 'PushEvent') {
            celesta[index].play();
        }else{
          playRandomSwell();
        }
        setTimeout(function() {
            current_notes--;
        }, note_timeout);
    }
}

// Following are the n numbers of event consumers
// consuming n events each per second with a random delay between them

function playFromQueueExchange1(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length*1.1, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

function playFromQueueExchange2(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 800) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

// This method capitalizes the string in place
String.prototype.capitalize=function(all){
    if(all){
      return this.split(' ').map(function(e){
        return e.capitalize().join(' ');
      });
    }else{
         return this.charAt(0).toUpperCase() + this.slice(1);
    }
}

function drawEvent(data, svg_area) {
    var starting_opacity = 1;
    var opacity = 1 / (100 / data.message.length);
    if (opacity > 0.5) {
        opacity = 0.5;
    }
    var size = data.message.length;
    var label_text;
    var ring_radius = 80;
    var ring_anim_duration = 3000;
    svg_text_color = '#FFFFFF';
    switch(data.type){
      case "PushEvent":
        label_text = data.user.capitalize() + " pushed to " + data.repo_name;
        edit_color = '#B2DFDB';
      break;
      case "PullRequestEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " " + " a PR for " + data.repo_name;
          edit_color = '#C6FF00';
          ring_anim_duration = 10000;
          ring_radius = 600;
      break;
      case "IssuesEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " an issue in " + data.repo_name;
          edit_color = '#FFEB3B';
      break;
      case "IssueCommentEvent":
        label_text = data.user.capitalize() + " commented in " + data.repo_name;
        edit_color = '#FF5722';
      break;
    }
    var csize = size;
    var no_label = false;
    var type = data.type;

    var circle_id = 'd' + ((Math.random() * 100000) | 0);
    var abs_size = Math.abs(size);
    size = Math.max(Math.sqrt(abs_size) * scale_factor, 3);

    Math.seedrandom(data.message)
    var x = Math.random() * (width - size) + size;
    var y = Math.random() * (height - size) + size;

    var circle_group = svg_area.append('g')
        .attr('transform', 'translate(' + x + ', ' + y + ')')
        .attr('fill', edit_color)
        .style('opacity', starting_opacity)

    var ring = circle_group.append('circle');
    ring.attr({r: size, stroke: 'none'});
    ring.transition()
        .attr('r', size + ring_radius)
        .style('opacity', 0)
        .ease(Math.sqrt)
        .duration(ring_anim_duration)
        .remove();

    var circle_container = circle_group.append('a');
    circle_container.attr('xlink:href', 'https://github.com/' + data.repo_name);
    circle_container.attr('target', '_blank');
    circle_container.attr('fill', svg_text_color);

    var circle = circle_container.append('circle');
    circle.classed(type, true);
    circle.attr('r', size)
      .attr('fill', edit_color)
      .transition()
      .duration(max_life)
      .style('opacity', 0)
      .remove();

    circle_container.on('mouseover', function() {
      circle_container.append('text')
          .text(label_text)
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('font-size', '0.8em')
          .transition()
          .delay(1000)
          .style('opacity', 0)
          .duration(2000)
          .each(function() { no_label = true; })
          .remove();
    });

    var text = circle_container.append('text')
        .text(label_text)
        .classed('article-label', true)
        .attr('text-anchor', 'middle')
        .attr('font-size', '0.8em')
        .transition()
        .delay(2000)
        .style('opacity', 0)
        .duration(5000)
        .each(function() { no_label = true; })
        .remove();

  // Remove HTML of decayed events
  // Keep it less than 50
  if($('#area svg g').length > 50){
    $('#area svg g:lt(10)').remove();
  }
}
<commit_msg>Increase the queue size to 1000; new users won't miss events<commit_after>var eventQueue = [];
var svg;
var element;
var drawingArea;
var width;
var height;
var volume = 0.6;
var ULTIMATE_DREAM_KILLER = false;

var scale_factor = 6,
    note_overlap = 2,
    note_timeout = 300,
    current_notes = 0,
    max_life = 20000;

var svg_background_color_online = '#0288D1',
    svg_background_color_offline = '#E91E63'
    svg_text_color = '#FFFFFF',
    newuser_box_color = 'rgb(41, 128, 185)',
    push_color = 'rgb(155, 89, 182)',
    issue_color = 'rgb(46, 204, 113)',
    pull_request_color = 'rgb(46, 204, 113)',
    comment_color = 'rgb(46, 204, 113)'
    edit_color = '#fff',
    total_sounds = 51,
    total_edits = 0;

    var celesta = [],
        clav = [],
        swells = [],
        all_loaded = false;

var socket = io(document.location.hostname);
socket.on('github', function (data) {
  $('.online-users-count').html(data.connected_users);
  data.data.forEach(function(event){
    if(!isEventInQueue(event) || shouldEventBeIgnored(event)){
      eventQueue.push(event);
    }
  });
  // Don't let the eventQueue grow more than 1000
  if (eventQueue.length > 1000) eventQueue = eventQueue.slice(0, 1000);
});

socket.on('connect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_online);
      $('header').css('background-color', svg_background_color_online);
      $('.offline-text').css('visibility', 'hidden');
      $('.events-remaining-text').css('visibility', 'hidden');
      $('.events-remaining-value').css('visibility', 'hidden');
    }
});

socket.on('disconnect', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');

    }
});

socket.on('error', function(){
    if(svg != null){
      $('svg').css('background-color', svg_background_color_offline);
      $('header').css('background-color', svg_background_color_offline);
      $('.offline-text').css('visibility', 'visible');
      $('.events-remaining-text').css('visibility', 'visible');
      $('.events-remaining-value').css('visibility', 'visible');
    }
});

/**
* This function checks whether an event is already in the queue
*/
function isEventInQueue(event){
  for(var i=0; i<eventQueue.length; i++){
    if(eventQueue[i].id == event.id)
      return true;
  }
  return false;
}

/**
 * This function adds a filter for events that we don't want to hear.
 *
 * To extend this function, simply add return true for events that should be filtered.
 */
function shouldEventBeIgnored(event){
  // This adds an easter egg to only play closed PRs
  if (!!ULTIMATE_DREAM_KILLER)
    return (event.type !== "PullRequestEvent" || event.action !== "closed");

  return false;
}

$(function(){
  element = document.documentElement;
  drawingArea = document.getElementsByTagName('#area')[0];
  width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
  height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
  $('svg').css('background-color', svg_background_color_online);
  $('header').css('background-color', svg_background_color_online);
  $('svg text').css('color', svg_text_color);
  $('#volumeSlider').slider({
    'max': 100,
    'min': 0,
    'value': volume*100,
    'slide' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    },
    'change' : function(event, ui){
      volume = ui.value/100.0;
      Howler.volume(volume);
    }
  });

  $('#eventConsumptionSlider').slider({
    'min': 1,
    'max': 100
  });

  // Main drawing area
  svg = d3.select("#area").append("svg");
  svg.attr({width: width, height: height});
  svg.style('background-color', svg_background_color_online);

  // For window resizes
  var update_window = function() {
      width = window.innerWidth || element.clientWidth || drawingArea.clientWidth;
      height = (window.innerHeight  - $('header').height())|| (element.clientHeight - $('header').height()) || (drawingArea.clientHeight - $('header').height());
      svg.attr("width", width).attr("height", height);
  }
  window.onresize = update_window;
  update_window();

  var loaded_sounds = 0;
  var sound_load = function(r) {
      loaded_sounds += 1;
      if (loaded_sounds == total_sounds) {
          all_loaded = true;
          setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000));
          // Starting the second exchange makes music a bad experience
          // setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 2000));
      }
  }

  // Load sounds
  for (var i = 1; i <= 24; i++) {
      if (i > 9) {
          fn = 'c0' + i;
      } else {
          fn = 'c00' + i;
      }
      celesta.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/celesta/' + fn + '.mp3'],
          volume : 0.7,
          onload : sound_load(),
          buffer: true,
      }))
      clav.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/clav/' + fn + '.mp3'],
          volume : 0.4,
          onload : sound_load(),
          buffer: true,
      }))
  }

  for (var i = 1; i <= 3; i++) {
      swells.push(new Howl({
          src : ['https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.ogg',
                  'https://d1fz9d31zqor6x.cloudfront.net/sounds/swells/swell' + i + '.mp3'],
          volume : 1,
          onload : sound_load(),
          buffer: true,
      }));
  }

  Howler.volume(volume);

});

/**
* Randomly selects a swell sound and plays it
*/
function playRandomSwell() {
    var index = Math.round(Math.random() * (swells.length - 1));
    swells[index].play();
}

/**
* Plays a sound(celesta and clav) based on passed parameters
*/
function playSound(size, type) {
    var max_pitch = 100.0;
    var log_used = 1.0715307808111486871978099;
    var pitch = 100 - Math.min(max_pitch, Math.log(size + log_used) / Math.log(log_used));
    var index = Math.floor(pitch / 100.0 * Object.keys(celesta).length);
    var fuzz = Math.floor(Math.random() * 4) - 2;
    index += fuzz;
    index = Math.min(Object.keys(celesta).length - 1, index);
    index = Math.max(1, index);
    if (current_notes < note_overlap) {
        current_notes++;
        if (type == 'IssuesEvent' || type == 'IssueCommentEvent') {
            clav[index].play();
        } else if(type == 'PushEvent') {
            celesta[index].play();
        }else{
          playRandomSwell();
        }
        setTimeout(function() {
            current_notes--;
        }, note_timeout);
    }
}

// Following are the n numbers of event consumers
// consuming n events each per second with a random delay between them

function playFromQueueExchange1(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length*1.1, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange1, Math.floor(Math.random() * 1000) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

function playFromQueueExchange2(){
  var event = eventQueue.shift();
  if(event != null && event.message != null && svg != null){
    playSound(event.message.length, event.type);
    if(!document.hidden)
      drawEvent(event, svg);
  }
  setTimeout(playFromQueueExchange2, Math.floor(Math.random() * 800) + 500);
  $('.events-remaining-value').html(eventQueue.length);
}

// This method capitalizes the string in place
String.prototype.capitalize=function(all){
    if(all){
      return this.split(' ').map(function(e){
        return e.capitalize().join(' ');
      });
    }else{
         return this.charAt(0).toUpperCase() + this.slice(1);
    }
}

function drawEvent(data, svg_area) {
    var starting_opacity = 1;
    var opacity = 1 / (100 / data.message.length);
    if (opacity > 0.5) {
        opacity = 0.5;
    }
    var size = data.message.length;
    var label_text;
    var ring_radius = 80;
    var ring_anim_duration = 3000;
    svg_text_color = '#FFFFFF';
    switch(data.type){
      case "PushEvent":
        label_text = data.user.capitalize() + " pushed to " + data.repo_name;
        edit_color = '#B2DFDB';
      break;
      case "PullRequestEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " " + " a PR for " + data.repo_name;
          edit_color = '#C6FF00';
          ring_anim_duration = 10000;
          ring_radius = 600;
      break;
      case "IssuesEvent":
        label_text = data.user.capitalize() + " " +
          data.action + " an issue in " + data.repo_name;
          edit_color = '#FFEB3B';
      break;
      case "IssueCommentEvent":
        label_text = data.user.capitalize() + " commented in " + data.repo_name;
        edit_color = '#FF5722';
      break;
    }
    var csize = size;
    var no_label = false;
    var type = data.type;

    var circle_id = 'd' + ((Math.random() * 100000) | 0);
    var abs_size = Math.abs(size);
    size = Math.max(Math.sqrt(abs_size) * scale_factor, 3);

    Math.seedrandom(data.message)
    var x = Math.random() * (width - size) + size;
    var y = Math.random() * (height - size) + size;

    var circle_group = svg_area.append('g')
        .attr('transform', 'translate(' + x + ', ' + y + ')')
        .attr('fill', edit_color)
        .style('opacity', starting_opacity)

    var ring = circle_group.append('circle');
    ring.attr({r: size, stroke: 'none'});
    ring.transition()
        .attr('r', size + ring_radius)
        .style('opacity', 0)
        .ease(Math.sqrt)
        .duration(ring_anim_duration)
        .remove();

    var circle_container = circle_group.append('a');
    circle_container.attr('xlink:href', 'https://github.com/' + data.repo_name);
    circle_container.attr('target', '_blank');
    circle_container.attr('fill', svg_text_color);

    var circle = circle_container.append('circle');
    circle.classed(type, true);
    circle.attr('r', size)
      .attr('fill', edit_color)
      .transition()
      .duration(max_life)
      .style('opacity', 0)
      .remove();

    circle_container.on('mouseover', function() {
      circle_container.append('text')
          .text(label_text)
          .classed('label', true)
          .attr('text-anchor', 'middle')
          .attr('font-size', '0.8em')
          .transition()
          .delay(1000)
          .style('opacity', 0)
          .duration(2000)
          .each(function() { no_label = true; })
          .remove();
    });

    var text = circle_container.append('text')
        .text(label_text)
        .classed('article-label', true)
        .attr('text-anchor', 'middle')
        .attr('font-size', '0.8em')
        .transition()
        .delay(2000)
        .style('opacity', 0)
        .duration(5000)
        .each(function() { no_label = true; })
        .remove();

  // Remove HTML of decayed events
  // Keep it less than 50
  if($('#area svg g').length > 50){
    $('#area svg g:lt(10)').remove();
  }
}
<|endoftext|>


<commit_before><commit_msg>Catching all non-existing routes.<commit_after><|endoftext|>


<commit_before>'use strict';

angular.module('kalabox.notificationQueue', [
  'ui.router',
  'ui.bootstrap',
  'kalabox.nodewrappers',
  'kalabox.guiEngine'
])
.factory('notificationQueue', function(_) {
  var notificationQueue = {};
  notificationQueue.unread = false;

  notificationQueue.notifications = [];

  notificationQueue.add = function(message) {
    this.notifications.push(
      {message: message, read: false, id: this.notifications.length + 1}
    );
    this.unread = true;
  };

  notificationQueue.markAllRead = function() {
    var notifications = this.notifications;
    this.notifications = _.forEach(notifications, function(value, key) {
      notifications[key].read = true;
    });
    this.unread = false;
  };

  notificationQueue.delete = function(id) {
    var notifications = this.notifications;
    this.notifications = _.reject(notifications, function(entry) {
      return entry.id === id;
    });

    if (this.notifications.length === 0) {
      this.unread = false;
    }
  };

  return notificationQueue;
})
.directive('notificationCenter', function() {
  console.log('in the directive');
  return {
    controller: function($scope, notificationQueue) {
      $scope.queue = notificationQueue;
      $scope.markAllRead = function() {
        $scope.apply(function() {
          notificationQueue.markAllRead();
        });
      };
    },
    scope: true,
    templateUrl: 'modules/notificationQueue/notificationQueue.html.tmpl'
  };
});
<commit_msg>Get rid of notification queue debugging code.<commit_after>'use strict';

angular.module('kalabox.notificationQueue', [
  'ui.router',
  'ui.bootstrap',
  'kalabox.nodewrappers',
  'kalabox.guiEngine'
])
.factory('notificationQueue', function(_) {
  var notificationQueue = {};
  notificationQueue.unread = false;

  notificationQueue.notifications = [];

  notificationQueue.add = function(message) {
    this.notifications.push(
      {message: message, read: false, id: this.notifications.length + 1}
    );
    this.unread = true;
  };

  notificationQueue.markAllRead = function() {
    var notifications = this.notifications;
    this.notifications = _.forEach(notifications, function(value, key) {
      notifications[key].read = true;
    });
    this.unread = false;
  };

  notificationQueue.delete = function(id) {
    var notifications = this.notifications;
    this.notifications = _.reject(notifications, function(entry) {
      return entry.id === id;
    });

    if (this.notifications.length === 0) {
      this.unread = false;
    }
  };

  return notificationQueue;
})
.directive('notificationCenter', function() {
  return {
    controller: function($scope, notificationQueue) {
      $scope.queue = notificationQueue;
      $scope.markAllRead = function() {
        $scope.apply(function() {
          notificationQueue.markAllRead();
        });
      };
    },
    scope: true,
    templateUrl: 'modules/notificationQueue/notificationQueue.html.tmpl'
  };
});
<|endoftext|>


<commit_before>// ==========================================================================
//                                DG.Case
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('models/model_store');
sc_require('models/base_model');

/** @class
 *
 * Represents an individual case in the collection. For the base collection (the
 * rightmost collection, the one with no child collection) there is one case
 * for each item in the data set. For all upper level collections a case is a unique
 * combination of attribute values among the data items with the same parentage.
 * Each collection manages an attribute group. An individual case is the set of
 * DataItems in the DataSet for which the attributes in the attribute group all
 * have the same values.
 *
 * @extends SC.Object
 */
DG.Case = DG.BaseModel.extend((function() {
  /** @scope DG.Case.prototype */

  /**
    Utility function to convert dates to strings while leaving
    all other values alone.
   */
  function convertValue(x, iAttr) {
    if (iAttr && (iAttr.type === 'date' && (DG.isDate(x)))) {
      return DG.formatDate(x, iAttr.precision);
    }
    else {
      return (DG.isDate(x)) ? DG.formatDate(x) : x;
    }
  }

  return {

    /**
     * Revision counter for the case.
     *  Incrementing the revision property causes change notifications to be sent.
     *  Calling beginCaseValueChanges()/endCaseValueChanges() will automatically
     *  increment the revision counter when endCaseValueChanges() is called.
     * @property {Number}
     */
    revision: 0,

    /**
     * A relational link back to the parent collection.
     * @property {DG.Collection}
     */
    collection: null,

    /**
     * A relational link back to the parent case (if any).
     * @property {DG.Case}
     */
    parent: null,

    /**
     * A relational link to the child cases of this case.
     * @property {[DG.Case]}
     */
    children: null,

      /**
       * Every case gets its attribute values from an item.
       * @type {DG.DataItem}
       */
    item: null,

    _cachedDate: null,

    /**
     * Associative array of {AttrID:value} pairs for quicker lookups.
     */
    _valuesMap: function () {
      return this.get('item').values;
    }.property(),

    /**
     * Initialization function for the case.
     */
    init: function() {
      sc_super();

      this._cachedDate = {};
      DG.Case._addCaseToItemMap(this);

      this.children = [];
      if (this.parent) {
        this.parent.children.pushObject(this);
      }
    },

    verify: function () {
      if (SC.empty(this.collection)) {
        DG.logWarn('Unattached case: ' + this.id);
      }
      if (typeof this.collection === 'number') {
        DG.logWarn('Unresolved reference to collection id, ' + this.collection +
          ', in case: ' + this.id);
      }
      if (typeof this.get('parent') === 'number') {
        DG.logWarn('Unresolved reference to parent, ' + this.get('parent') +
          ', in case: ' + this.id);
      }
    },

    /**
     * Destruction function for the case.
     */
    destroy: function() {

      DG.Case._removeCaseFromItemMap(this);

      sc_super();
    },

    beginCaseValueChanges: function() {
      this.beginPropertyChanges();
    },

    /**
     * Ends a set of property changes and increments the revision property.
     */
    endCaseValueChanges: function() {
      this.incrementProperty('revision');
      this.endPropertyChanges();
    },

    /**
     * Returns the raw value of the specified attribute for this case.
     * Dates are returned as Dates rather than converted to strings.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Number | String | null}
     */
    getRawValue: function( iAttrID) {

      if( !SC.none( iAttrID)) {

        // If we have an attribute formula, we must evaluate it.
        var tAttr = DG.Attribute.getAttributeByID( iAttrID);
        var valuesMap = this.get('_valuesMap');
        if (tAttr && tAttr.get('hasFormula') &&
          // we only do the evaluation if it's one of this case's attributes
          (this.getPath('collection.id') === tAttr.getPath('collection.id'))) {
          return tAttr.evalFormula( this);
        }

        // If we have a cached value, simply return it
        if( tAttr && valuesMap && (!SC.none(valuesMap[iAttrID])) &&
            // we only do the evaluation if it's one of this case's attributes. This is important!
            (this.getPath('collection.id') === tAttr.getPath('collection.id'))) {
          return valuesMap[iAttrID];
        }

        // one last chance if we've got a parent and it has 'getValue'
        // Need for this seems to have been brought about by a particular import of boundary file data. Can't hurt.
        var tParent = this.get('parent');
        if( tParent )
          return tParent.getRawValue( iAttrID);
      }
      // if we get here, return value is undefined
    },

    /**
     * Returns the value of the specified attribute for this case.
     * Dates are converted to strings.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Number | String | null}
     */
    getValue: function( iAttrID) {
      var rawValue = this.getRawValue(iAttrID);
      var attr = DG.Attribute.getAttributeByID( iAttrID);
      return rawValue != null ? convertValue(rawValue, attr) : rawValue;
    },

    /**
     * Returns the numeric value of the specified attribute for this case.
     * Note that the built-in isFinite() can be used with the result (rather than
     * DG.isFinite()) because nulls/undefineds/etc. have already been filtered out.
     * @param {Number} iAttrID -- ID of the attribute for which the value should be returned.
     * @param {Object} oOptInfo -- Additional information about the value can be returned to
     *                              the client via this object.
     *                  .type -- The type of the value, e.g. "string", "number", etc.
     *                  .isNominal -- True if the value is non-numeric and can be
     *                                converted to a non-empty string.
     * @returns {Number}
     */
    getNumValue: function( iAttrID, oOptInfo) {
      var value = this.getRawValue( iAttrID),
          valType = typeof value,
          tAttr = DG.Attribute.getAttributeByID( iAttrID),
          attrType = tAttr && tAttr.type;
      if (DG.isDate(value)) {
        // treat dates numerically
        value = Number(value);
        valType = "number";
      } else if (this._cachedDate[iAttrID]) {
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      } else if (attrType === 'date') {
        this._cachedDate[iAttrID] = DG.parseDate(value, true);
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      } else if (DG.isDateString(value)) {
        this._cachedDate[iAttrID] = DG.parseDate(value);
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      }
      if( oOptInfo) {
        oOptInfo.type = valType;
        // Note: we don't consider non-primitive types (e.g. objects, arrays, etc.)
        oOptInfo.isNominal = (valType === "boolean") ||
          ((valType === "string") && !SC.empty(String(value)));
      }
      switch( valType) {
        case "number":
          return value;
        case "string":
          return (value !== "" ? Number(value) : NaN);
      }
      return NaN;
    },

   /**
     * Returns a numeric value of the specified attribute for this case where the raw value has been stripped
    *   of non-numeric characters and the first valid number.
     * @param {Number} iAttrID -- ID of the attribute for which the value should be returned.
   */
    getForcedNumericValue: function( iAttrID) {
      var tInfo = {},
          tValue = this.getNumValue( iAttrID, tInfo);
      if( tInfo.type === 'number')
        return tValue;
      else
        return DG.MathUtilities.extractNumeric( this.getRawValue( iAttrID));
    },

    /**
     * Returns the string value of the specified attribute for this case.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @param {Object} oOptInfo -- Additional information about the value can be returned to
     *                              the client via this object.
     *                  .type -- The type of the value, e.g. "string", "number", etc.
     * @returns {String}
     */
    getStrValue: function( iAttrID, oOptInfo) {
      var value = this.getValue( iAttrID),
          valType = typeof value;
      if( oOptInfo) {
        oOptInfo.type = valType;
      }
      return DG.DataUtilities.toString(value);
    },

    /**
     * Return the value that fits the attribute's type
     * @param iAttrID {Number}
     * @return {number|string|null}
     */
    getTypedValue: function( iAttrID) {
      switch (DG.Attribute.getAttributeByID( iAttrID).get('type')) {
        case 'numeric':
          return this.getForcedNumericValue( iAttrID);
        case 'date':
          return this.getValue( iAttrID);
        case 'nominal':
          return this.getStrValue( iAttrID);
        default:
          return this.getRawValue( iAttrID);
      }
      //return this.getRawValue( iAttrID);
    },

    /**
     * Is there a value of the specified attribute for this case.
     * Note that a formula can return a null value, so some cases will be thought
     * to have a value for the given attribute ID, when actually they don't.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Boolean}
     */
    hasValue: function( iAttrID) {
      if (!SC.none( iAttrID)) {
        var tAttr = this.get('collection').getAttributeByID( iAttrID);
        if(tAttr && tAttr.get('hasFormula'))
          return !SC.empty( this.getValue( iAttrID));

        if( tAttr)
          return this.get('_valuesMap') && !SC.empty( this.get('_valuesMap')[iAttrID]);

        var tParent = this.get('parent');
        return tParent && tParent.hasValue( iAttrID);
      }
    },

    /**
     * Sets the value of the specified attribute for this case.
     * @param {Number} iAttrID ID of the attribute for which the value should be set.
     * @param {String} iValue Value of the newly created case value.
     */
    setValue: function(iAttrID, iValue) {
      var children = this.get('children');
      if( !this.get('_valuesMap')) return; // Looks like case has been destroyed

      this._cachedDate[iAttrID] = null;
      if (children.length === 0) {
        this.item.setValue(iAttrID, iValue);
      } else {
        children.forEach(function (child) { child.setValue(iAttrID, iValue); });
      }
    },

    /**
     Returns a copy of the case's values.
     */
    copyValues: function() {
      var valuesMap = this.get('_valuesMap'),
          attrs = this.collection.attrs,
          values = {};
      attrs.forEach(function (attr) {
        values[attr.name] = valuesMap[attr.id];
      });
      return values;
    },

    /**
     Override to handle 'values' specially.
     During runtime, the _valuesMap, which maps from attrID to value,
     is the definitive contents of the case. In this method, we copy the
     contents of the _valuesMap to the 'archivableValues' property, which
     maps from attrName to value and converts values for archival purposes.
     */
    archivableValues: function() {
      var values = {};
      var valuesMap = this.get('_valuesMap');
      var attrs = this.collection.attrs;
      attrs.forEach(function (attr, ix) {
        values[attr.name] = convertValue(valuesMap[attr.id]);
      });
      return values;
    }.property(),

    debugLog: function(iPrompt) {
      DG.log('Case ' + this.get('id'));
    },

    toArchive: function () {
      var result;
      result = {
        parent: (this.get('parent') && this.get('parent').id)  || undefined,
        guid: this.id,
        id: this.id,
        itemID: this.item.id,
        values: this.get('archivableValues')
      };
      return result;
    }
  };
})());

/*
  Map from { collectionID: { itemID: DG.Case }}
  Used for looking up cases from items.
 */
DG.Case._itemCaseMaps = {};

/*
  Adds a (new) case to the item case map.
 */
DG.Case._addCaseToItemMap = function(iCase) {
  var collectionID = iCase.getPath('collection.id'),
      itemID = iCase.getPath('item.id'),
      itemCaseMap = collectionID && DG.Case._itemCaseMaps[collectionID];
  if (!SC.none(itemID) && collectionID) {
    if (!itemCaseMap)
      DG.Case._itemCaseMaps[collectionID] = itemCaseMap = {};
    itemCaseMap[itemID] = iCase;
  }
};

/*
  Removes a case from the item case map.
 */
DG.Case._removeCaseFromItemMap = function(iCase) {
  var collectionID = iCase.getPath('collection.id'),
      itemID = iCase.getPath('item.id'),
      itemCaseMap = collectionID && DG.Case._itemCaseMaps[collectionID];
  if (!SC.none(itemID) && itemCaseMap) {
    delete itemCaseMap[itemID];
  }
};

/*
  Find a case (if any) that corresponds to the specified item.
 */
DG.Case.findCase = function(iCollectionID, iItemID) {
  var itemCaseMap = iCollectionID && DG.Case._itemCaseMaps[iCollectionID];
  return itemCaseMap && !SC.none(iItemID) && itemCaseMap[String(iItemID)];
};

/**
 * Creates a new case with the specified properties.
 * @param iProperties
 *    {{collection:DG.Collection|id, parent:DG.Case|id, extentStart: number}}
 *    Properties to apply to the newly-created DG.Case
 *
 * @return {DG.Case}  The newly-created DG.Case
 */
DG.Case.createCase = function( iProperties) {
  var collection = iProperties.collection,
    parent = iProperties.parent,
    newCase;
  iProperties.collection = DG.store.resolve(collection);
  if (!SC.empty(parent)) {
    iProperties.parent = DG.store.resolve(parent);
  }
  newCase = DG.Case.create(iProperties || {});
  return newCase;
};

/**
 * Destroys the specified DG.Case.
 * @param {DG.Case} iCase The case to destroy
 */
DG.Case.destroyCase = function( iCase) {
  iCase.destroy();
};
<commit_msg>[#175104044] Bug fix: Plotted Value of mean() does not work as of 0554<commit_after>// ==========================================================================
//                                DG.Case
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

sc_require('models/model_store');
sc_require('models/base_model');

/** @class
 *
 * Represents an individual case in the collection. For the base collection (the
 * rightmost collection, the one with no child collection) there is one case
 * for each item in the data set. For all upper level collections a case is a unique
 * combination of attribute values among the data items with the same parentage.
 * Each collection manages an attribute group. An individual case is the set of
 * DataItems in the DataSet for which the attributes in the attribute group all
 * have the same values.
 *
 * @extends SC.Object
 */
DG.Case = DG.BaseModel.extend((function() {
  /** @scope DG.Case.prototype */

  /**
    Utility function to convert dates to strings while leaving
    all other values alone.
   */
  function convertValue(x, iAttr) {
    if (iAttr && (iAttr.type === 'date' && (DG.isDate(x)))) {
      return DG.formatDate(x, iAttr.precision);
    }
    else {
      return (DG.isDate(x)) ? DG.formatDate(x) : x;
    }
  }

  return {

    /**
     * Revision counter for the case.
     *  Incrementing the revision property causes change notifications to be sent.
     *  Calling beginCaseValueChanges()/endCaseValueChanges() will automatically
     *  increment the revision counter when endCaseValueChanges() is called.
     * @property {Number}
     */
    revision: 0,

    /**
     * A relational link back to the parent collection.
     * @property {DG.Collection}
     */
    collection: null,

    /**
     * A relational link back to the parent case (if any).
     * @property {DG.Case}
     */
    parent: null,

    /**
     * A relational link to the child cases of this case.
     * @property {[DG.Case]}
     */
    children: null,

      /**
       * Every case gets its attribute values from an item.
       * @type {DG.DataItem}
       */
    item: null,

    _cachedDate: null,

    /**
     * Associative array of {AttrID:value} pairs for quicker lookups.
     */
    _valuesMap: function () {
      return this.get('item').values;
    }.property(),

    /**
     * Initialization function for the case.
     */
    init: function() {
      sc_super();

      this._cachedDate = {};
      DG.Case._addCaseToItemMap(this);

      this.children = [];
      if (this.parent) {
        this.parent.children.pushObject(this);
      }
    },

    verify: function () {
      if (SC.empty(this.collection)) {
        DG.logWarn('Unattached case: ' + this.id);
      }
      if (typeof this.collection === 'number') {
        DG.logWarn('Unresolved reference to collection id, ' + this.collection +
          ', in case: ' + this.id);
      }
      if (typeof this.get('parent') === 'number') {
        DG.logWarn('Unresolved reference to parent, ' + this.get('parent') +
          ', in case: ' + this.id);
      }
    },

    /**
     * Destruction function for the case.
     */
    destroy: function() {

      DG.Case._removeCaseFromItemMap(this);

      sc_super();
    },

    beginCaseValueChanges: function() {
      this.beginPropertyChanges();
    },

    /**
     * Ends a set of property changes and increments the revision property.
     */
    endCaseValueChanges: function() {
      this.incrementProperty('revision');
      this.endPropertyChanges();
    },

    /**
     * Returns the raw value of the specified attribute for this case.
     * Dates are returned as Dates rather than converted to strings.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Number | String | null}
     */
    getRawValue: function( iAttrID) {

      if( !SC.none( iAttrID)) {

        // If we have an attribute formula, we must evaluate it.
        var tAttr = DG.Attribute.getAttributeByID( iAttrID);
        var valuesMap = this.get('_valuesMap');
        if (tAttr && tAttr.get('hasFormula') &&
          // we only do the evaluation if it's one of this case's attributes
          (this.getPath('collection.id') === tAttr.getPath('collection.id'))) {
          return tAttr.evalFormula( this);
        }

        // If we have a cached value, simply return it
        if( tAttr && valuesMap && (!SC.none(valuesMap[iAttrID])) &&
            // we only do the evaluation if it's one of this case's attributes. This is important!
            (this.getPath('collection.id') === tAttr.getPath('collection.id'))) {
          return valuesMap[iAttrID];
        }

        // one last chance if we've got a parent and it has 'getValue'
        // Need for this seems to have been brought about by a particular import of boundary file data. Can't hurt.
        var tParent = this.get('parent');
        if( tParent )
          return tParent.getRawValue( iAttrID);
      }
      // if we get here, return value is undefined
    },

    /**
     * Returns the value of the specified attribute for this case.
     * Dates are converted to strings.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Number | String | null}
     */
    getValue: function( iAttrID) {
      var rawValue = this.getRawValue(iAttrID);
      var attr = DG.Attribute.getAttributeByID( iAttrID);
      return rawValue != null ? convertValue(rawValue, attr) : rawValue;
    },

    /**
     * Returns the numeric value of the specified attribute for this case.
     * Note that the built-in isFinite() can be used with the result (rather than
     * DG.isFinite()) because nulls/undefineds/etc. have already been filtered out.
     * @param {Number} iAttrID -- ID of the attribute for which the value should be returned.
     * @param {Object} oOptInfo -- Additional information about the value can be returned to
     *                              the client via this object.
     *                  .type -- The type of the value, e.g. "string", "number", etc.
     *                  .isNominal -- True if the value is non-numeric and can be
     *                                converted to a non-empty string.
     * @returns {Number}
     */
    getNumValue: function( iAttrID, oOptInfo) {
      var value = this.getRawValue( iAttrID),
          valType = typeof value,
          tAttr = DG.Attribute.getAttributeByID( iAttrID),
          attrType = tAttr && tAttr.type;
      if (DG.isDate(value)) {
        // treat dates numerically
        value = Number(value);
        valType = "number";
      } else if (this._cachedDate[iAttrID]) {
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      } else if (attrType === 'date') {
        this._cachedDate[iAttrID] = DG.parseDate(value, true);
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      } else if (DG.isDateString(value)) {
        this._cachedDate[iAttrID] = DG.parseDate(value);
        value = Number(this._cachedDate[iAttrID]);
        valType = "number";
      }
      if( oOptInfo) {
        oOptInfo.type = valType;
        // Note: we don't consider non-primitive types (e.g. objects, arrays, etc.)
        oOptInfo.isNominal = (valType === "boolean") ||
          ((valType === "string") && !SC.empty(String(value)));
      }
      switch( valType) {
        case "number":
          return value;
        case "string":
          return (value !== "" ? Number(value) : NaN);
      }
      return NaN;
    },

   /**
     * Returns a numeric value of the specified attribute for this case where the raw value has been stripped
    *   of non-numeric characters and the first valid number.
     * @param {Number} iAttrID -- ID of the attribute for which the value should be returned.
   */
    getForcedNumericValue: function( iAttrID) {
      var tInfo = {},
          tValue = this.getNumValue( iAttrID, tInfo);
      if( tInfo.type === 'number')
        return tValue;
      else
        return DG.MathUtilities.extractNumeric( this.getRawValue( iAttrID));
    },

    /**
     * Returns the string value of the specified attribute for this case.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @param {Object} oOptInfo -- Additional information about the value can be returned to
     *                              the client via this object.
     *                  .type -- The type of the value, e.g. "string", "number", etc.
     * @returns {String}
     */
    getStrValue: function( iAttrID, oOptInfo) {
      var value = this.getValue( iAttrID),
          valType = typeof value;
      if( oOptInfo) {
        oOptInfo.type = valType;
      }
      return DG.DataUtilities.toString(value);
    },

    /**
     * Return the value that fits the attribute's type
     * @param iAttrID {Number}
     * @return {number|string|null}
     */
    getTypedValue: function( iAttrID) {
      var tAttribute = DG.Attribute.getAttributeByID( iAttrID);
      if( tAttribute) {
        switch (DG.Attribute.getAttributeByID(iAttrID).get('type')) {
          case 'numeric':
            return this.getForcedNumericValue(iAttrID);
          case 'date':
            return this.getValue(iAttrID);
          case 'nominal':
            return this.getStrValue(iAttrID);
          default:
            return this.getRawValue(iAttrID);
        }
      }
      else
        return this.getRawValue( iAttrID);
    },

    /**
     * Is there a value of the specified attribute for this case.
     * Note that a formula can return a null value, so some cases will be thought
     * to have a value for the given attribute ID, when actually they don't.
     * @param {Number} iAttrID ID of the attribute for which the value should be returned.
     * @returns {Boolean}
     */
    hasValue: function( iAttrID) {
      if (!SC.none( iAttrID)) {
        var tAttr = this.get('collection').getAttributeByID( iAttrID);
        if(tAttr && tAttr.get('hasFormula'))
          return !SC.empty( this.getValue( iAttrID));

        if( tAttr)
          return this.get('_valuesMap') && !SC.empty( this.get('_valuesMap')[iAttrID]);

        var tParent = this.get('parent');
        return tParent && tParent.hasValue( iAttrID);
      }
    },

    /**
     * Sets the value of the specified attribute for this case.
     * @param {Number} iAttrID ID of the attribute for which the value should be set.
     * @param {String} iValue Value of the newly created case value.
     */
    setValue: function(iAttrID, iValue) {
      var children = this.get('children');
      if( !this.get('_valuesMap')) return; // Looks like case has been destroyed

      this._cachedDate[iAttrID] = null;
      if (children.length === 0) {
        this.item.setValue(iAttrID, iValue);
      } else {
        children.forEach(function (child) { child.setValue(iAttrID, iValue); });
      }
    },

    /**
     Returns a copy of the case's values.
     */
    copyValues: function() {
      var valuesMap = this.get('_valuesMap'),
          attrs = this.collection.attrs,
          values = {};
      attrs.forEach(function (attr) {
        values[attr.name] = valuesMap[attr.id];
      });
      return values;
    },

    /**
     Override to handle 'values' specially.
     During runtime, the _valuesMap, which maps from attrID to value,
     is the definitive contents of the case. In this method, we copy the
     contents of the _valuesMap to the 'archivableValues' property, which
     maps from attrName to value and converts values for archival purposes.
     */
    archivableValues: function() {
      var values = {};
      var valuesMap = this.get('_valuesMap');
      var attrs = this.collection.attrs;
      attrs.forEach(function (attr, ix) {
        values[attr.name] = convertValue(valuesMap[attr.id]);
      });
      return values;
    }.property(),

    debugLog: function(iPrompt) {
      DG.log('Case ' + this.get('id'));
    },

    toArchive: function () {
      var result;
      result = {
        parent: (this.get('parent') && this.get('parent').id)  || undefined,
        guid: this.id,
        id: this.id,
        itemID: this.item.id,
        values: this.get('archivableValues')
      };
      return result;
    }
  };
})());

/*
  Map from { collectionID: { itemID: DG.Case }}
  Used for looking up cases from items.
 */
DG.Case._itemCaseMaps = {};

/*
  Adds a (new) case to the item case map.
 */
DG.Case._addCaseToItemMap = function(iCase) {
  var collectionID = iCase.getPath('collection.id'),
      itemID = iCase.getPath('item.id'),
      itemCaseMap = collectionID && DG.Case._itemCaseMaps[collectionID];
  if (!SC.none(itemID) && collectionID) {
    if (!itemCaseMap)
      DG.Case._itemCaseMaps[collectionID] = itemCaseMap = {};
    itemCaseMap[itemID] = iCase;
  }
};

/*
  Removes a case from the item case map.
 */
DG.Case._removeCaseFromItemMap = function(iCase) {
  var collectionID = iCase.getPath('collection.id'),
      itemID = iCase.getPath('item.id'),
      itemCaseMap = collectionID && DG.Case._itemCaseMaps[collectionID];
  if (!SC.none(itemID) && itemCaseMap) {
    delete itemCaseMap[itemID];
  }
};

/*
  Find a case (if any) that corresponds to the specified item.
 */
DG.Case.findCase = function(iCollectionID, iItemID) {
  var itemCaseMap = iCollectionID && DG.Case._itemCaseMaps[iCollectionID];
  return itemCaseMap && !SC.none(iItemID) && itemCaseMap[String(iItemID)];
};

/**
 * Creates a new case with the specified properties.
 * @param iProperties
 *    {{collection:DG.Collection|id, parent:DG.Case|id, extentStart: number}}
 *    Properties to apply to the newly-created DG.Case
 *
 * @return {DG.Case}  The newly-created DG.Case
 */
DG.Case.createCase = function( iProperties) {
  var collection = iProperties.collection,
    parent = iProperties.parent,
    newCase;
  iProperties.collection = DG.store.resolve(collection);
  if (!SC.empty(parent)) {
    iProperties.parent = DG.store.resolve(parent);
  }
  newCase = DG.Case.create(iProperties || {});
  return newCase;
};

/**
 * Destroys the specified DG.Case.
 * @param {DG.Case} iCase The case to destroy
 */
DG.Case.destroyCase = function( iCase) {
  iCase.destroy();
};
<|endoftext|>


<commit_before>
/*
    This file must compile in EcmaScript 3 and work in IE7.
*/

define(["openerp", "im_common", "underscore", "require", "jquery",
        "jquery.achtung"], function(openerp, im_common, _, require, $) {
    /* jshint es3: true */
    "use strict";

    var _t = openerp._t;

    var livesupport = {};

    livesupport.main = function(server_url, db, login, password, channel, options) {
        options = options || {};
        _.defaults(options, {
            buttonText: _t("Chat with one of our collaborators"),
            inputPlaceholder: _t("How may I help you?"),
            defaultMessage: null,
            auto: false,
            userName: _t("Anonymous"),
            anonymous_mode: true
        });

        im_common.notification = notification;

        console.log("starting live support customer app");
        im_common.connection = new openerp.Session(null, server_url, { override_session: true });
        return im_common.connection.session_authenticate(db, login, password).then(function() {
            var defs = [];
            defs.push(add_css("/im/static/src/css/im_common.css"));
            defs.push(add_css("/im_livechat/static/ext/static/lib/jquery-achtung/src/ui.achtung.css"));
            defs.push(im_common.connection.rpc('/web/proxy/load', {path: '/im_livechat/static/ext/static/js/livechat.xml'}).then(function(xml) {
                openerp.qweb.add_template(xml);
            }));
            defs.push(im_common.connection.rpc('/web/proxy/load', {path: '/im/static/src/xml/im_common.xml'}).then(function(xml) {
                openerp.qweb.add_template(xml);
            }));
            return $.when.apply($, defs);
        }).then(function() {
            return im_common.connection.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
                if (! activated & ! options.auto)
                    return;
                var button = new im_common.ChatButton(null, channel, options);
                button.appendTo($("body"));
                if (options.auto)
                    button.click();
            });
        });
    };

    var add_css = function(relative_file_name) {
        var css_def = $.Deferred();
        $('<link rel="stylesheet" href="' + im_common.connection.url(relative_file_name) + '"></link>')
                .appendTo($("head")).ready(function() {
            css_def.resolve();
        });
        return css_def.promise();
    };

    var notification = function(message) {
        $.achtung({message: message, timeout: 0, showEffects: false, hideEffects: false});
    };

    im_common.ChatButton = openerp.Widget.extend({
        className: "openerp_style oe_chat_button",
        events: {
            "click": "click"
        },
        init: function(parent, channel, options) {
            this._super(parent);
            this.channel = channel;
            this.options = options;
            this.text = options.buttonText;
        },
        start: function() {
            this.$().append(openerp.qweb.render("chatButton", {widget: this}));
        },
        click: function() {
            if (! this.manager) {
                this.manager = new im_common.ConversationManager(this, this.options);
                this.manager.set("bottom_offset", 37);
                this.activated_def = this.manager.start_polling();
            }
            var def = $.Deferred();
            $.when(this.activated_def).then(function() {
                def.resolve();
            }, function() {
                def.reject();
            });
            setTimeout(function() {
                def.reject();
            }, 5000);
            return def.then(_.bind(this.chat, this), function() {
                im_common.notification(_t("It seems the connection to the server is encountering problems, please try again later."));
            });
        },
        chat: function() {
            var self = this;
            if (this.manager.conversations.length > 0)
                return;
            im_common.connection.model("im_livechat.channel").call("get_session", [this.channel, this.manager.me.get("uuid")]).then(function(session_id) {
                if (! session_id) {
                    im_common.notification(_t("None of our collaborators seems to be available, please try again later."));
                    return;
                }
                self.manager.activate_session(session_id, true).then(function(conv) {
                    if (self.options.defaultMessage) {
                        conv.received_message({
                            message: self.options.defaultMessage,
                            date: openerp.datetime_to_str(new Date()),
                            from_id: [conv.users[0].get("id"), "Unknown"]
                        });
                    }
                });
            });
        }
    });

    return livesupport;
});
<commit_msg>Fixed small bug in live support<commit_after>
/*
    This file must compile in EcmaScript 3 and work in IE7.
*/

define(["openerp", "im_common", "underscore", "require", "jquery",
        "jquery.achtung"], function(openerp, im_common, _, require, $) {
    /* jshint es3: true */
    "use strict";

    var _t = openerp._t;

    var livesupport = {};

    livesupport.main = function(server_url, db, login, password, channel, options) {
        options = options || {};
        _.defaults(options, {
            buttonText: _t("Chat with one of our collaborators"),
            inputPlaceholder: _t("How may I help you?"),
            defaultMessage: null,
            auto: false,
            userName: _t("Anonymous"),
            anonymous_mode: true
        });

        im_common.notification = notification;

        console.log("starting live support customer app");
        im_common.connection = new openerp.Session(null, server_url, { override_session: true });
        return im_common.connection.session_authenticate(db, login, password).then(function() {
            var defs = [];
            defs.push(add_css("/im/static/src/css/im_common.css"));
            defs.push(add_css("/im_livechat/static/ext/static/lib/jquery-achtung/src/ui.achtung.css"));
            defs.push(im_common.connection.rpc('/web/proxy/load', {path: '/im_livechat/static/ext/static/js/livechat.xml'}).then(function(xml) {
                openerp.qweb.add_template(xml);
            }));
            defs.push(im_common.connection.rpc('/web/proxy/load', {path: '/im/static/src/xml/im_common.xml'}).then(function(xml) {
                openerp.qweb.add_template(xml);
            }));
            return $.when.apply($, defs);
        }).then(function() {
            return im_common.connection.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
                if (! activated & ! options.auto)
                    return;
                var button = new im_common.ChatButton(null, channel, options);
                button.appendTo($("body"));
                if (options.auto)
                    button.click();
            });
        });
    };

    var add_css = function(relative_file_name) {
        var css_def = $.Deferred();
        $('<link rel="stylesheet" href="' + im_common.connection.url(relative_file_name) + '"></link>')
                .appendTo($("head")).ready(function() {
            css_def.resolve();
        });
        return css_def.promise();
    };

    var notification = function(message) {
        $.achtung({message: message, timeout: 0, showEffects: false, hideEffects: false});
    };

    im_common.ChatButton = openerp.Widget.extend({
        className: "openerp_style oe_chat_button",
        events: {
            "click": "click"
        },
        init: function(parent, channel, options) {
            this._super(parent);
            this.channel = channel;
            this.options = options;
            this.text = options.buttonText;
        },
        start: function() {
            this.$().append(openerp.qweb.render("chatButton", {widget: this}));
        },
        click: function() {
            if (! this.manager) {
                this.manager = new im_common.ConversationManager(this, this.options);
                this.manager.set("bottom_offset", 37);
                this.activated_def = this.manager.start_polling();
            }
            var def = $.Deferred();
            $.when(this.activated_def).then(function() {
                def.resolve();
            }, function() {
                def.reject();
            });
            setTimeout(function() {
                def.reject();
            }, 5000);
            return def.then(_.bind(this.chat, this), function() {
                im_common.notification(_t("It seems the connection to the server is encountering problems, please try again later."));
            });
        },
        chat: function() {
            var self = this;
            if (this.manager.conversations.length > 0)
                return;
            im_common.connection.model("im_livechat.channel").call("get_session", [this.channel, this.manager.me.get("uuid")]).then(function(session_id) {
                if (! session_id) {
                    im_common.notification(_t("None of our collaborators seems to be available, please try again later."));
                    return;
                }
                self.manager.activate_session(session_id, true).then(function(conv) {
                    if (self.options.defaultMessage) {
                        conv.received_message({
                            message: self.options.defaultMessage,
                            date: openerp.datetime_to_str(new Date()),
                            from_id: [conv.get("users")[0].get("id"), "Unknown"]
                        });
                    }
                });
            });
        }
    });

    return livesupport;
});
<|endoftext|>


<commit_before>const mockServer = require('mockserver-node');
const minimist = require('minimist');
const shell = require('shelljs');

function startEnvironment() {
	mockServer.start_mockserver({
		serverPort: 1080,
		trace: true
	});
}
function stopEnvironment() {
	mockServer.stop_mockserver({
		serverPort: 1080
	});
}

async function runTests() {
	const options = [
		`-c ${__dirname}/jest.config.js`,
		'--verbose',
		'--runInBand',
		'--detectOpenHandles',
		'--forceExit',
		'--color',
		...process.argv.slice(2),
	];

	console.log(`./node_modules/.bin/jest ${options.join(' ')}`);

	const { code, } = await shell.exec(`./node_modules/.bin/jest ${options.join(' ')}`);

	return code;
}

async function main() {
	const argv = minimist(process.argv);

	if (argv['start-environment']) {
		await startEnvironment();
		process.exit(0);
	}

	if (argv['stop-environment']) {
		return stopEnvironment();
	}

	try {
		const code = await runTests();

		process.exit(code || 0);
	} catch (error) {
		console.log(error);
		process.exit(1);
	}
}

main();
<commit_msg>994: added verbose true for mockserver<commit_after>const mockServer = require('mockserver-node');
const minimist = require('minimist');
const shell = require('shelljs');

function startEnvironment() {
	mockServer.start_mockserver({
		serverPort: 1080,
		trace: true,
		verbose: true,
	});
}
function stopEnvironment() {
	mockServer.stop_mockserver({
		serverPort: 1080
	});
}

async function runTests() {
	const options = [
		`-c ${__dirname}/jest.config.js`,
		'--verbose',
		'--runInBand',
		'--detectOpenHandles',
		'--forceExit',
		'--color',
		...process.argv.slice(2),
	];

	console.log(`./node_modules/.bin/jest ${options.join(' ')}`);

	const { code, } = await shell.exec(`./node_modules/.bin/jest ${options.join(' ')}`);

	return code;
}

async function main() {
	const argv = minimist(process.argv);

	if (argv['start-environment']) {
		await startEnvironment();
		process.exit(0);
	}

	if (argv['stop-environment']) {
		return stopEnvironment();
	}

	try {
		const code = await runTests();

		process.exit(code || 0);
	} catch (error) {
		console.log(error);
		process.exit(1);
	}
}

main();
<|endoftext|>