<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

// You can view the full text of each annotation by running mocha test/annotations_test.js
// Current output:
//
// Main text annotation for carelink/basal/temp-percent-create-scheduled:
// We are calculating the temp basal rates here by applying the percentage of the temp basal to your current schedule, but Demo did not directly provide us with these rate changes.
//
// Main text annotation for carelink/basal/off-schedule-rate:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your basal rate here.
//
// Main text annotation for carelink/settings/basal-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your basal settings here.
//
// Main text annotation for carelink/settings/wizard-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your bolus wizard settings here.
//
// Main text annotation for carelink/settings/activeSchedule-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your active basal pattern here.
//
// Main text annotation for diasend/basal/temp-basal-fabrication:
// The Demo .xls file doesn't report temp basals directly, so we have to infer from other evidence where you might have been using a temp basal rate. We think this segment could be a temp basal, but it may not be.
//
// Main text annotation for diasend/basal/temp-duration-truncated:
// Because of how the Demo .xls file reports the data, we've truncated what may have been a temp basal here to a maximum duration of 120 hours.
//
// Main text annotation for diasend/bolus/extended:
// The Demo .xls file doesn't report the split between the intitial and the extended delivery during a combo bolus. All we can display is the duration of the combo bolus and the total dose delivered.
//
// Main text annotation for stats-insufficient-data:
// There is not enough data to show this statistic.
//
// Lead text annotation for stats-insufficient-data:
// Why is this grey?

var format = require('../../../data/util/format');

var definitions = {
  LEAD_TEXT: {
    'stats-insufficient-data': function() {
      return 'Why is this grey?';
    }
  },
  MAIN_TEXT: {
    'carelink/basal/temp-percent-create-scheduled': function(source, defs) {
      var a = "We are calculating the temp basal rates here by applying the percentage of the temp basal to your current schedule, but ";
      var b = " did not directly provide us with these rate changes.";
      return defs.stitch(a, b, source);
    },
    'carelink/basal/off-schedule-rate': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your basal rate here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/basal-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your basal settings here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/wizard-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your bolus wizard settings here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/activeSchedule-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your active basal pattern here.";
      return defs.stitch(a, b, source);
    },
    'diasend/basal/temp-basal-fabrication': function(source, defs) {
      var a = "The ";
      var b = " .xls file doesn't report temp basals directly, so we have to infer from other evidence where you might have been using a temp basal rate. We think this segment could be a temp basal, but it may not be.";
      return defs.stitch(a, b, source);
    },
    'diasend/basal/temp-duration-truncated': function(source, defs) {
      var a = "Because of how the ";
      var b = " .xls file reports the data, we've truncated what may have been a temp basal here to a maximum duration of 120 hours.";
      return defs.stitch(a, b, source);
    },
    'diasend/bolus/extended': function(source, defs) {
      var a = "The ";
      var b = " .xls file doesn't report the split between the intitial and the extended delivery during a combo bolus. All we can display is the duration of the combo bolus and the total dose delivered.";
      return defs.stitch(a, b, source);
    },
    'stats-insufficient-data': function() {
      return 'There is not enough data to show this statistic.';
    }
  },
  default: function(source) {
    if (source == null) {
      return "We can't be 100% certain of the data displayed here.";
    }
    var a = "We can't be 100% certain of the data displayed here because of how ";
    var b = " reports the data.";
    return this.stitch(a, b, source);
  },
  main: function(annotation, source) {
    var a, b;
    if (this.MAIN_TEXT[annotation.code] != null) {
      return this.MAIN_TEXT[annotation.code](source, this);
    }
    else {
      return this.default(source);
    }
  },
  stitch: function(a, b, source) {
    return a + format.capitalize(source) + b;
  },
  lead: function(code) {
    code = code || '';
    if (this.LEAD_TEXT[code] != null) {
      return this.LEAD_TEXT[code]();
    }
    else {
      return false;
    }
  }
};

module.exports = definitions;<commit_msg>add annotations for device overlap, basal intersecting with incomplete suspend<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

// You can view the full text of each annotation by running mocha test/annotations_test.js
// Current output:
//
// Main text annotation for carelink/basal/temp-percent-create-scheduled:
// We are calculating the temp basal rates here by applying the percentage of the temp basal to your current schedule, but Demo did not directly provide us with these rate changes.
//
// Main text annotation for carelink/basal/off-schedule-rate:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your basal rate here.
//
// Main text annotation for carelink/settings/basal-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your basal settings here.
//
// Main text annotation for carelink/settings/wizard-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your bolus wizard settings here.
//
// Main text annotation for carelink/settings/activeSchedule-mismatch:
// You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how Demo reports the data, we can't be 100% certain of your active basal pattern here.
//
// Main text annotation for carelink/device-overlap-boundary:
// We are not showing data here because we've received datasets from multiple pumps that are in conflict. Demo did not provide enough information to distinguish the data from each pump.
//
// Main text annotation for diasend/basal/temp-basal-fabrication:
// The Demo .xls file doesn't report temp basals directly, so we have to infer from other evidence where you might have been using a temp basal rate. We think this segment could be a temp basal, but it may not be.
//
// Main text annotation for diasend/basal/temp-duration-truncated:
// Because of how the Demo .xls file reports the data, we've truncated what may have been a temp basal here to a maximum duration of 120 hours.
//
// Main text annotation for diasend/bolus/extended:
// The Demo .xls file doesn't report the split between the intitial and the extended delivery during a combo bolus. All we can display is the duration of the combo bolus and the total dose delivered.
//
// Main text annotation for basal/intersects-incomplete-suspend
// Within this basal segment, we are omitting a suspend event that didn't end. This may have resulted from switching to a new device. As a result, this basal segment may be inaccurate.
//
// Main text annotation for stats-insufficient-data:
// There is not enough data to show this statistic.
//
// Lead text annotation for stats-insufficient-data:
// Why is this grey?

var format = require('../../../data/util/format');

var definitions = {
  LEAD_TEXT: {
    'stats-insufficient-data': function() {
      return 'Why is this grey?';
    }
  },
  MAIN_TEXT: {
    'carelink/basal/temp-percent-create-scheduled': function(source, defs) {
      var a = "We are calculating the temp basal rates here by applying the percentage of the temp basal to your current schedule, but ";
      var b = " did not directly provide us with these rate changes.";
      return defs.stitch(a, b, source);
    },
    'carelink/basal/off-schedule-rate': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your basal rate here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/basal-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your basal settings here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/wizard-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your bolus wizard settings here.";
      return defs.stitch(a, b, source);
    },
    'carelink/settings/activeSchedule-mismatch': function(source, defs) {
      var a = "You may have changed pumps recently - perhaps because you had to have your pump replaced due to malfuction. As a result of how ";
      var b = " reports the data, we can't be 100% certain of your active basal pattern here.";
      return defs.stitch(a, b, source);
    },
    'carelink/device-overlap-boundary': function(source, defs) {
      var a = "We are not showing data here because we've received datasets from multiple pumps that are in conflict.";
      var b = " did not provide enough information to distinguish the data from each pump.";
      return defs.stitch(a, b, source);
    },
    'diasend/basal/temp-basal-fabrication': function(source, defs) {
      var a = "The ";
      var b = " .xls file doesn't report temp basals directly, so we have to infer from other evidence where you might have been using a temp basal rate. We think this segment could be a temp basal, but it may not be.";
      return defs.stitch(a, b, source);
    },
    'diasend/basal/temp-duration-truncated': function(source, defs) {
      var a = "Because of how the ";
      var b = " .xls file reports the data, we've truncated what may have been a temp basal here to a maximum duration of 120 hours.";
      return defs.stitch(a, b, source);
    },
    'diasend/bolus/extended': function(source, defs) {
      var a = "The ";
      var b = " .xls file doesn't report the split between the intitial and the extended delivery during a combo bolus. All we can display is the duration of the combo bolus and the total dose delivered.";
      return defs.stitch(a, b, source);
    },
    'basal/intersects-incomplete-susppend': function() {
      return "Within this basal segment, we are omitting a suspend event that didn't end. This may have resulted from switching to a new device. As a result, this basal segment may be inaccurate.";
    },
    'stats-insufficient-data': function() {
      return 'There is not enough data to show this statistic.';
    }
  },
  default: function(source) {
    if (source == null) {
      return "We can't be 100% certain of the data displayed here.";
    }
    var a = "We can't be 100% certain of the data displayed here because of how ";
    var b = " reports the data.";
    return this.stitch(a, b, source);
  },
  main: function(annotation, source) {
    var a, b;
    if (this.MAIN_TEXT[annotation.code] != null) {
      return this.MAIN_TEXT[annotation.code](source, this);
    }
    else {
      return this.default(source);
    }
  },
  stitch: function(a, b, source) {
    return a + format.capitalize(source) + b;
  },
  lead: function(code) {
    code = code || '';
    if (this.LEAD_TEXT[code] != null) {
      return this.LEAD_TEXT[code]();
    }
    else {
      return false;
    }
  }
};

module.exports = definitions;<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// @brief tests for query language, simple non-collection-based queries
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function ahuacatlQueryNonCollectionTestSuite () {

////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query
////////////////////////////////////////////////////////////////////////////////

  function executeQuery (query) {
    var cursor = AHUACATL_RUN(query, undefined);
    if (cursor instanceof ArangoError) {
      print(query, cursor.errorMessage);
    }
    assertFalse(cursor instanceof ArangoError);
    return cursor;
  }

////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query and return the results as an array
////////////////////////////////////////////////////////////////////////////////

  function getQueryResults (query, isFlat) {
    var result = executeQuery(query).getRows();
    var results = [ ];

    for (var i in result) {
      if (!result.hasOwnProperty(i)) {
        continue;
      }

      var row = result[i];
      if (isFlat) {
        results.push(row);
      }
      else {
        var keys = [ ];
        for (var k in row) {
          if (row.hasOwnProperty(k) && k != '_id' && k != '_rev') {
            keys.push(k);
          }
        }

        keys.sort();
        var resultRow = { };
        for (var k in keys) {
          if (keys.hasOwnProperty(k)) {
            resultRow[keys[k]] = row[keys[k]];
          }
        }
        results.push(resultRow);
      }
    }

    return results;
  }

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuery : function () {
      var expected = [ 2010, 2011, 2012 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return two values from a list
////////////////////////////////////////////////////////////////////////////////

    testListDocumentQuery : function () {
      var expected = [ { "days" : 365, "year" : 2010 }, { "days" : 365, "year" : 2011 }, { "days" : 366, "year" : 2012 } ];

      var actual = getQueryResults("FOR year IN [ { \"year\" : 2010, \"days\" : 365 } , { \"year\" : 2011, \"days\" : 365 }, { \"year\" : 2012, \"days\" : 366 } ] return year", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a sorted list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuerySort : function () {
      var expected = [ 2015, 2014, 2013, 2012, 2011, 2010 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014, 2015 ] SORT year DESC return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a sorted limited list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuerySortLimit : function () {
      var expected = [ 2013, 2012, 2011 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014, 2015 ] SORT year DESC LIMIT 2,3 return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a calculated from a list
////////////////////////////////////////////////////////////////////////////////

    testListCalculated : function () {
      var expected = [ { "anno" : 2010, "isLeapYear" : false }, { "anno" : 2011, "isLeapYear" : false }, { "anno" : 2012, "isLeapYear" : true }, { "anno" : 2013, "isLeapYear" : false }, { "anno" : 2014, "isLeapYear" : false } ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014 ] return { \"anno\" : year, \"isLeapYear\" : (year%4==0 && year%100!=0 || year%400==1) }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested for loops
////////////////////////////////////////////////////////////////////////////////

    testNestedFor1 : function () {
      var expected = [ { "q" : 1, "y": 2010 }, { "q" : 1, "y": 2011 }, { "q" : 1, "y": 2012 }, { "q" : 2, "y": 2010 }, { "q" : 2, "y": 2011 }, { "q" : 2, "y": 2012 }, { "q" : 3, "y": 2010 }, { "q" : 3, "y": 2011 }, { "q" : 3, "y": 2012 }, { "q" : 4, "y": 2010 }, { "q" : 4, "y": 2011 }, { "q" : 4, "y": 2012 } ];
      var actual = getQueryResults("FOR quarter IN [ 1, 2, 3, 4 ] FOR year IN [ 2010, 2011, 2012 ] return { \"q\" : quarter, \"y\" : year }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested for loops
////////////////////////////////////////////////////////////////////////////////

    testNestedFor2 : function () {
      var expected = [ { "q" : 1, "y": 2010 }, { "q" : 2, "y": 2010 }, { "q" : 3, "y": 2010 }, { "q" : 4, "y": 2010 }, { "q" : 1, "y": 2011 }, { "q" : 2, "y": 2011 }, { "q" : 3, "y": 2011 }, { "q" : 4, "y": 2011 }, { "q" : 1, "y": 2012 }, { "q" : 2, "y": 2012 }, { "q" : 3, "y": 2012 }, { "q" : 4, "y": 2012 } ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] for quarter IN [ 1, 2, 3, 4 ] return { \"q\" : quarter, \"y\" : year }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested subqueries
////////////////////////////////////////////////////////////////////////////////

    testSubqueryFor1 : function () {
      var expected = [ 4, 5, 6 ];
      var actual = getQueryResults("FOR i IN ((FOR j IN ((FOR k IN ((FOR l IN [1,2,3] RETURN l+1)) RETURN k+1)) RETURN j+1)) RETURN i", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return data from nested subqueries
////////////////////////////////////////////////////////////////////////////////

    testSubqueryFor2 : function () {
      var expected = [ 1, 2, 3 ];

      var actual = getQueryResults("FOR i IN (FOR j IN [1,2,3] RETURN j) RETURN i", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet1 : function () {
      var expected = [ 2010, 2011, 2012 ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] let quarters = ((for quarter IN [ 1, 2, 3, 4 ] return quarter)) RETURN year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet2 : function () {
      var expected = [ [ 1, 2, 3, 4 ], [ 1, 2, 3, 4 ], [ 1, 2, 3, 4 ] ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] let quarters = ((for quarter IN [ 1, 2, 3, 4 ] return quarter)) RETURN quarters", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet3 : function () {
      var expected = [{ "a" : 1, "b" : [4, 5, 6], "c" : [7, 8, 9] }, { "a" : 2, "b" : [5, 6, 7], "c" : [8, 9, 10] }, { "a" : 3, "b" : [6, 7, 8], "c" : [9, 10, 11] }];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=(FOR x IN [3,4,5] RETURN x+i) LET k=(FOR y IN [6,7,8] RETURN y+i) RETURN { \"a\": i, \"b\" : j, \"c\" : k }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet4 : function () {
      var expected = [[[9, 8, 7], [9, 8, 7]], [[9, 8, 7], [9, 8, 7]], [[9, 8, 7], [9, 8, 7]]];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=(FOR x IN [2010,2011] LET k=(FOR y IN [9,8,7] RETURN y) RETURN k) RETURN j", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from simple let
////////////////////////////////////////////////////////////////////////////////

    testSimpleLet1 : function () {
      var expected = [ 1, 2, 3 ];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=i RETURN j", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from simple let
////////////////////////////////////////////////////////////////////////////////

    testSimpleLet1 : function () {
      var expected = [ "the fox", "the fox", "the fox" ];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=\"the fox\" RETURN j", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return asterisk
////////////////////////////////////////////////////////////////////////////////

    testAsterisk : function () {
      var expected = [ { "r" : 2010, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] }, { "r" : 2011, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] }, { "r" : 2011, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] } ];
      var actual = getQueryResults("FOR r IN [ 2010, 2011, 2011] LET x = ((FOR f IN [ { \"names\" : [ { \"name\" : \"a\" }, { \"name\" : \"b\" } ] }, { \"names\": [ { \"name\" : \"c\" }, { \"name\": \"d\" } ] } ] return f)) return { \"r\" : r, \"x\" : x[*].names }", false);
      assertEqual(expected, actual);
    },

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(ahuacatlQueryNonCollectionTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<commit_msg>test case for issue #97<commit_after>////////////////////////////////////////////////////////////////////////////////
/// @brief tests for query language, simple non-collection-based queries
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function ahuacatlQueryNonCollectionTestSuite () {

////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query
////////////////////////////////////////////////////////////////////////////////

  function executeQuery (query) {
    var cursor = AHUACATL_RUN(query, undefined);
    if (cursor instanceof ArangoError) {
      print(query, cursor.errorMessage);
    }
    assertFalse(cursor instanceof ArangoError);
    return cursor;
  }

////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query and return the results as an array
////////////////////////////////////////////////////////////////////////////////

  function getQueryResults (query, isFlat) {
    var result = executeQuery(query).getRows();
    var results = [ ];

    for (var i in result) {
      if (!result.hasOwnProperty(i)) {
        continue;
      }

      var row = result[i];
      if (isFlat) {
        results.push(row);
      }
      else {
        var keys = [ ];
        for (var k in row) {
          if (row.hasOwnProperty(k) && k != '_id' && k != '_rev') {
            keys.push(k);
          }
        }

        keys.sort();
        var resultRow = { };
        for (var k in keys) {
          if (keys.hasOwnProperty(k)) {
            resultRow[keys[k]] = row[keys[k]];
          }
        }
        results.push(resultRow);
      }
    }

    return results;
  }

  return {

////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////

    setUp : function () {
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////

    tearDown : function () {
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuery : function () {
      var expected = [ 2010, 2011, 2012 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return two values from a list
////////////////////////////////////////////////////////////////////////////////

    testListDocumentQuery : function () {
      var expected = [ { "days" : 365, "year" : 2010 }, { "days" : 365, "year" : 2011 }, { "days" : 366, "year" : 2012 } ];

      var actual = getQueryResults("FOR year IN [ { \"year\" : 2010, \"days\" : 365 } , { \"year\" : 2011, \"days\" : 365 }, { \"year\" : 2012, \"days\" : 366 } ] return year", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a sorted list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuerySort : function () {
      var expected = [ 2015, 2014, 2013, 2012, 2011, 2010 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014, 2015 ] SORT year DESC return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a sorted limited list
////////////////////////////////////////////////////////////////////////////////

    testListValueQuerySortLimit : function () {
      var expected = [ 2013, 2012, 2011 ];

      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014, 2015 ] SORT year DESC LIMIT 2,3 return year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return a calculated from a list
////////////////////////////////////////////////////////////////////////////////

    testListCalculated : function () {
      var expected = [ { "anno" : 2010, "isLeapYear" : false }, { "anno" : 2011, "isLeapYear" : false }, { "anno" : 2012, "isLeapYear" : true }, { "anno" : 2013, "isLeapYear" : false }, { "anno" : 2014, "isLeapYear" : false } ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012, 2013, 2014 ] return { \"anno\" : year, \"isLeapYear\" : (year%4==0 && year%100!=0 || year%400==1) }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested for loops
////////////////////////////////////////////////////////////////////////////////

    testNestedFor1 : function () {
      var expected = [ { "q" : 1, "y": 2010 }, { "q" : 1, "y": 2011 }, { "q" : 1, "y": 2012 }, { "q" : 2, "y": 2010 }, { "q" : 2, "y": 2011 }, { "q" : 2, "y": 2012 }, { "q" : 3, "y": 2010 }, { "q" : 3, "y": 2011 }, { "q" : 3, "y": 2012 }, { "q" : 4, "y": 2010 }, { "q" : 4, "y": 2011 }, { "q" : 4, "y": 2012 } ];
      var actual = getQueryResults("FOR quarter IN [ 1, 2, 3, 4 ] FOR year IN [ 2010, 2011, 2012 ] return { \"q\" : quarter, \"y\" : year }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested for loops
////////////////////////////////////////////////////////////////////////////////

    testNestedFor2 : function () {
      var expected = [ { "q" : 1, "y": 2010 }, { "q" : 2, "y": 2010 }, { "q" : 3, "y": 2010 }, { "q" : 4, "y": 2010 }, { "q" : 1, "y": 2011 }, { "q" : 2, "y": 2011 }, { "q" : 3, "y": 2011 }, { "q" : 4, "y": 2011 }, { "q" : 1, "y": 2012 }, { "q" : 2, "y": 2012 }, { "q" : 3, "y": 2012 }, { "q" : 4, "y": 2012 } ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] for quarter IN [ 1, 2, 3, 4 ] return { \"q\" : quarter, \"y\" : year }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from nested subqueries
////////////////////////////////////////////////////////////////////////////////

    testSubqueryFor1 : function () {
      var expected = [ 4, 5, 6 ];
      var actual = getQueryResults("FOR i IN ((FOR j IN ((FOR k IN ((FOR l IN [1,2,3] RETURN l+1)) RETURN k+1)) RETURN j+1)) RETURN i", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return data from nested subqueries
////////////////////////////////////////////////////////////////////////////////

    testSubqueryFor2 : function () {
      var expected = [ 1, 2, 3 ];

      var actual = getQueryResults("FOR i IN (FOR j IN [1,2,3] RETURN j) RETURN i", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet1 : function () {
      var expected = [ 2010, 2011, 2012 ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] let quarters = ((for quarter IN [ 1, 2, 3, 4 ] return quarter)) RETURN year", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet2 : function () {
      var expected = [ [ 1, 2, 3, 4 ], [ 1, 2, 3, 4 ], [ 1, 2, 3, 4 ] ];
      var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] let quarters = ((for quarter IN [ 1, 2, 3, 4 ] return quarter)) RETURN quarters", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet3 : function () {
      var expected = [{ "a" : 1, "b" : [4, 5, 6], "c" : [7, 8, 9] }, { "a" : 2, "b" : [5, 6, 7], "c" : [8, 9, 10] }, { "a" : 3, "b" : [6, 7, 8], "c" : [9, 10, 11] }];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=(FOR x IN [3,4,5] RETURN x+i) LET k=(FOR y IN [6,7,8] RETURN y+i) RETURN { \"a\": i, \"b\" : j, \"c\" : k }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from let
////////////////////////////////////////////////////////////////////////////////

    testNestedLet4 : function () {
      var expected = [[[9, 8, 7], [9, 8, 7]], [[9, 8, 7], [9, 8, 7]], [[9, 8, 7], [9, 8, 7]]];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=(FOR x IN [2010,2011] LET k=(FOR y IN [9,8,7] RETURN y) RETURN k) RETURN j", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from simple let
////////////////////////////////////////////////////////////////////////////////

    testSimpleLet1 : function () {
      var expected = [ 1, 2, 3 ];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=i RETURN j", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return values from simple let
////////////////////////////////////////////////////////////////////////////////

    testSimpleLet1 : function () {
      var expected = [ "the fox", "the fox", "the fox" ];
      var actual = getQueryResults("FOR i IN [1,2,3] LET j=\"the fox\" RETURN j", true);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief return asterisk
////////////////////////////////////////////////////////////////////////////////

    testAsterisk : function () {
      var expected = [ { "r" : 2010, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] }, { "r" : 2011, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] }, { "r" : 2011, "x" : [ [ { "name" : "a" }, { "name" : "b" } ], [ { "name" : "c" }, { "name" : "d" } ] ] } ];
      var actual = getQueryResults("FOR r IN [ 2010, 2011, 2011] LET x = ((FOR f IN [ { \"names\" : [ { \"name\" : \"a\" }, { \"name\" : \"b\" } ] }, { \"names\": [ { \"name\" : \"c\" }, { \"name\": \"d\" } ] } ] return f)) return { \"r\" : r, \"x\" : x[*].names }", false);
      assertEqual(expected, actual);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test nesting (issue #97)
////////////////////////////////////////////////////////////////////////////////

    testMultiNesting : function () {
      var expected = [ 1 ]; // we're only interested in whether the below query can be parsed properly

      var actual = getQueryResults("FOR r IN [ 1 ] LET f = (FOR x IN [ 1 ] FILTER 1 == 1 FOR y IN [ 1 ] FOR z IN [ 1 ] RETURN 1) RETURN 1", true);
      assertEqual(expected, actual);
    },

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(ahuacatlQueryNonCollectionTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<|endoftext|>


<commit_before>import _ from 'underscore';
import React from 'react';
import Reflux from 'reflux';
import reactMixin from 'react-mixin';
import Select from 'react-select';
import autoBind from 'react-autobind';
import clonedeep from 'lodash.clonedeep';
import Checkbox from 'js/components/common/checkbox';
import Radio from 'js/components/common/radio';
import {bem} from 'js/bem';
import {actions} from 'js/actions';
import mixins from 'js/mixins';
import {notify} from 'utils';
import {
  SUBMISSION_ACTIONS_ID,
  DATA_TABLE_SETTING,
  DATA_TABLE_SETTINGS,
} from 'js/components/submissions/tableConstants';
import {getColumnLabel} from 'js/components/submissions/tableUtils';
import './tableSettings.scss';

class TableSettings extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      selectedColumns: [],
      frozenColumn: false,
      showGroupName: true,
      showHXLTags: false,
      translationIndex: 0,
    };

    let _sett = props.asset.settings;
    if (_sett[DATA_TABLE_SETTING]) {
      if (_sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SELECTED_COLUMNS] !== null) {
        this.state.selectedColumns = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SELECTED_COLUMNS];
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.FROZEN_COLUMN] !== 'undefined') {
        const cols = this.listColumns();
        this.state.frozenColumn = _.find(cols, (col) => col.value === _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.FROZEN_COLUMN]);
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP] !== 'undefined') {
        this.state.showGroupName = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP];
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION] !== 'undefined') {
        this.state.translationIndex = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION];
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL] !== 'undefined') {
        this.state.showHXLTags = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL];
      }
    }

    autoBind(this);
  }

  componentDidMount() {
    this.listenTo(actions.table.updateSettings.failed, this.onUpdateSettingsFailed);
  }

  saveTableColumns() {
    let s = this.state;

    // get whole asset settings as clone to avoid bugs
    const newSettings = clonedeep(this.props.asset.settings);

    if (!newSettings[DATA_TABLE_SETTING]) {
      newSettings[DATA_TABLE_SETTING] = {};
    }

    if (this.userCan('change_asset', this.props.asset)) {
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SELECTED_COLUMNS] = s.selectedColumns.length > 0 ? s.selectedColumns : null;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.FROZEN_COLUMN] = s.frozenColumn.value;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP] = s.showGroupName;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION] = s.translationIndex;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL] = s.showHXLTags;

      actions.table.updateSettings(this.props.asset.uid, newSettings);
    } else {
      // just update the state, since user cannot save settings
      let overrides = {
        showGroupName: s.showGroupName,
        translationIndex: s.translationIndex,
      };

      this.props.overrideLabelsAndGroups(overrides);
    }
  }

  toggleCheckboxChange(columnId) {
    const selectedColumns = this.state.selectedColumns;
    const idx = selectedColumns.indexOf(columnId);

    if (idx !== -1) {
      selectedColumns.splice(idx, 1);
    } else {
      selectedColumns.push(columnId);
    }

    this.setState({selectedColumns: selectedColumns});
  }

  setFrozenColumn(col) {
    this.setState({frozenColumn: col ? col : false});
  }

  updateGroupHeaderDisplay(isChecked) {
    this.setState({showGroupName: isChecked});
  }

  onHXLTagsChange(isChecked) {
    this.setState({showHXLTags: isChecked});
  }

  onLabelChange(name, value) {
    this.setState({translationIndex: parseInt(value)});
  }

  onUpdateSettingsFailed() {
    notify(t('There was an error, table settings could not be saved.'));
  }

  resetTableSettings() {
    // get whole asset settings as clone to avoid bugs
    const newSettings = clonedeep(this.props.asset.settings);

    if (newSettings[DATA_TABLE_SETTING]) {
      delete newSettings[DATA_TABLE_SETTING];
    }

    actions.table.updateSettings(this.props.asset.uid, newSettings);
  }

  listColumns() {
    let colsArray = this.props.columns.reduce((acc, col) => {
      if (col.id && col.id !== SUBMISSION_ACTIONS_ID) {
        let qParentGroup = [];
        if (col.id.includes('/')) {
          qParentGroup = col.id.split('/');
        }

        acc.push({
          value: col.id,
          label: getColumnLabel(
            this.props.asset.content.survey,
            col.id,
            col.question,
            qParentGroup,
            this.state.showGroupName,
            this.state.translationIndex
          ),
        });
      }
      return acc;
    }, []);

    return colsArray;
  }

  getDisplayedLabelOptions() {
    const options = [];
    options.push({
      value: -1,
      label: t('XML Values'),
    });
    this.props.asset.content.translations.map((trns, n) => {
      let label = t('Labels');
      if (trns) {
        label += ` - ${trns}`;
      }
      options.push({
        value: n,
        label: label,
      });
    });
    return options;
  }

  render() {
    let _this = this;

    return (

        <bem.FormModal__item m='translation-radios'>
          <Radio
            title={t('Display labels or XML values?')}
            options={this.getDisplayedLabelOptions()}
            selected={this.state.translationIndex}
            onChange={this.onLabelChange}
          />
        </bem.FormModal__item>
        <bem.FormModal__item m='group-headings'>
          <Checkbox
            checked={this.state.showGroupName}
            onChange={this.updateGroupHeaderDisplay}
            label={t('Show group names in table headers')}
          />
        </bem.FormModal__item>

        <bem.FormModal__item>
          <Checkbox
            checked={this.state.showHXLTags}
            onChange={this.onHXLTagsChange}
            label={t('Show HXL tags')}
          />
        </bem.FormModal__item>

        {this.userCan('change_asset', this.props.asset) &&
          <bem.FormModal__item m='advanced-table-options'>
            <bem.FormView__cell m='note'>
              {t('Note: Only users with the "edit form" permission can see the following two options. If other users can view submissions on this project, their table view will be restricted by the choices made below.')}
            </bem.FormView__cell>
            <bem.FormModal__item>
              <bem.FormView__cell m='label'>
                {t('Set a frozen first column in the table.')}
              </bem.FormView__cell>
              <Select
                value={this.state.frozenColumn}
                options={this.listColumns()}
                onChange={this.setFrozenColumn}
                className='kobo-select'
                classNamePrefix='kobo-select'
                menuPlacement='auto'
                isClearable
              />
            </bem.FormModal__item>
            <bem.FormModal__item>
              <bem.FormView__cell m='label'>
                {t('Restrict the visible columns in the table display')}
                {t('All columns are visible by default')}
              </bem.FormView__cell>

                {this.listColumns().map(function (col) {
                  return (

                      <Checkbox
                        checked={_this.state.selectedColumns.includes(col.value)}
                        onChange={_this.toggleCheckboxChange.bind(this, col.value)}
                        label={col.label}
                      />

                  );
                })}

            </bem.FormModal__item>
          </bem.FormModal__item>
        }
        <bem.Modal__footer>
          {this.userCan('change_asset', this.props.asset) &&
            <bem.KoboButton m='whitegray' onClick={this.resetTableSettings}>
              {t('Reset')}
            </bem.KoboButton>
          }

          <bem.KoboButton m='blue' onClick={this.saveTableColumns}>
            {t('Save')}
          </bem.KoboButton>
        </bem.Modal__footer>

    );
  }
}

reactMixin(TableSettings.prototype, Reflux.ListenerMixin);
reactMixin(TableSettings.prototype, mixins.permissions);

export default TableSettings;
<commit_msg>clanup tableSettings<commit_after>import React from 'react';
import Reflux from 'reflux';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import clonedeep from 'lodash.clonedeep';
import Checkbox from 'js/components/common/checkbox';
import Radio from 'js/components/common/radio';
import {bem} from 'js/bem';
import {actions} from 'js/actions';
import mixins from 'js/mixins';
import {notify} from 'utils';
import {
  DATA_TABLE_SETTING,
  DATA_TABLE_SETTINGS,
} from 'js/components/submissions/tableConstants';
import './tableSettings.scss';

class TableSettings extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      showGroupName: true,
      showHXLTags: false,
      translationIndex: 0,
    };

    let _sett = props.asset.settings;
    if (_sett[DATA_TABLE_SETTING]) {
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP] !== 'undefined') {
        this.state.showGroupName = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP];
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION] !== 'undefined') {
        this.state.translationIndex = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION];
      }
      if (typeof _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL] !== 'undefined') {
        this.state.showHXLTags = _sett[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL];
      }
    }

    autoBind(this);
  }

  componentDidMount() {
    this.listenTo(actions.table.updateSettings.failed, this.onUpdateSettingsFailed);
  }

  saveTableColumns() {
    let s = this.state;

    // get whole asset settings as clone to avoid bugs
    const newSettings = clonedeep(this.props.asset.settings);

    if (!newSettings[DATA_TABLE_SETTING]) {
      newSettings[DATA_TABLE_SETTING] = {};
    }

    if (this.userCan('change_asset', this.props.asset)) {
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP] = s.showGroupName;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION] = s.translationIndex;
      newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL] = s.showHXLTags;

      actions.table.updateSettings(this.props.asset.uid, newSettings);
    } else {
      // just update the state, since user cannot save settings
      let overrides = {
        showGroupName: s.showGroupName,
        translationIndex: s.translationIndex,
      };

      this.props.overrideLabelsAndGroups(overrides);
    }
  }

  updateGroupHeaderDisplay(isChecked) {
    this.setState({showGroupName: isChecked});
  }

  onHXLTagsChange(isChecked) {
    this.setState({showHXLTags: isChecked});
  }

  onLabelChange(name, value) {
    this.setState({translationIndex: parseInt(value)});
  }

  onUpdateSettingsFailed() {
    notify(t('There was an error, table settings could not be saved.'));
  }

  resetTableSettings() {
    // get whole asset settings as clone to avoid bugs
    const newSettings = clonedeep(this.props.asset.settings);

    if (newSettings[DATA_TABLE_SETTING]) {
      // Just delete the settings from this modal, i.e. leave frozen column
      // and selected columns intact.
      delete newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_GROUP];
      delete newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.TRANSLATION];
      delete newSettings[DATA_TABLE_SETTING][DATA_TABLE_SETTINGS.SHOW_HXL];
    }

    actions.table.updateSettings(this.props.asset.uid, newSettings);
  }

  getDisplayedLabelOptions() {
    const options = [];
    options.push({
      value: -1,
      label: t('XML Values'),
    });
    this.props.asset.content.translations.map((trns, n) => {
      let label = t('Labels');
      if (trns) {
        label += ` - ${trns}`;
      }
      options.push({
        value: n,
        label: label,
      });
    });
    return options;
  }

  render() {
    return (

        <bem.FormModal__item m='translation-radios'>
          <Radio
            title={t('Display labels or XML values?')}
            options={this.getDisplayedLabelOptions()}
            selected={this.state.translationIndex}
            onChange={this.onLabelChange}
          />
        </bem.FormModal__item>
        <bem.FormModal__item m='group-headings'>
          <Checkbox
            checked={this.state.showGroupName}
            onChange={this.updateGroupHeaderDisplay}
            label={t('Show group names in table headers')}
          />
        </bem.FormModal__item>

        <bem.FormModal__item>
          <Checkbox
            checked={this.state.showHXLTags}
            onChange={this.onHXLTagsChange}
            label={t('Show HXL tags')}
          />
        </bem.FormModal__item>

        <bem.Modal__footer>
          {this.userCan('change_asset', this.props.asset) &&
            <bem.KoboButton m='whitegray' onClick={this.resetTableSettings}>
              {t('Reset')}
            </bem.KoboButton>
          }

          <bem.KoboButton m='blue' onClick={this.saveTableColumns}>
            {t('Save')}
          </bem.KoboButton>
        </bem.Modal__footer>

    );
  }
}

reactMixin(TableSettings.prototype, Reflux.ListenerMixin);
reactMixin(TableSettings.prototype, mixins.permissions);

export default TableSettings;
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Cu = Components.utils;
  const Pref = Cc['@mozilla.org/preferences;1']
                 .getService(Ci.nsIPrefBranch);
  const Registry = Cu.import("resource://gre/modules/WindowsRegistry.jsm")
                     .WindowsRegistry;
  const kChromePath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
  const kPrefPrefix = "extensions.switch-link-external-handler@clear-code.com.";
  const kIEPatternsPref = kPrefPrefix +  "ie.patterns.";
  const kChromePatternsPref = kPrefPrefix + "chrome.patterns.";
  var SwitchLinkExternalHandler = {
    startIE: function startIE(aURL) {
      this.startExternalProcess(this.IECommandline, aURL);
    },

    startChrome: function startChrome(aURL) {
      this.startExternalProcess([kChromePath], aURL);
    },

    startExternalProcess: function startExternalProcess(aCommandLine, aURL) {
      var process = Cc["@mozilla.org/process/util;1"]
                      .createInstance(Ci.nsIProcess);
      var file = Cc["@mozilla.org/file/local;1"]
                   .createInstance(Ci.nsILocalFile);
      var exePath = aCommandLine[0];
      var args = [];
      var placeholderFound = false;
      for (var i = 1, length = aCommandLine.length; i < length; ++i) {
        if (aCommandLine[i].indexOf("%1") >= 0) {
          placeholderFound = true;
        }
        args.push(aCommandLine[i].replace("%1", aURL));
      }
      if (!placeholderFound) {
        args.push(aURL);
      }
      file.initWithPath(exePath);
      process.init(file);
      process.run(false, args, args.length);
    },

    get IECommandline() {
      delete this.IECommandline;
      var commandLine = Registry.readRegKey(
        Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
        "Applications\\iexplore.exe\\shell\\open\\command",
        ""
      );
      return this.IECommandline = this.shellSplit(commandLine);
    },

    onLinkClick: function onLinkClick(aEvent) {
      let href = hRefForClickEvent(aEvent);

      if (href.match(this.ChromeMatcher)) {
        this.startChrome(href);
      }
      else if (href.match(this.IEMatcher)) {
        this.startIE(href);
      }
      else {
        return;
      }
      aEvent.preventDefault();
      aEvent.stopPropagation();
    },

    get IEMatcher() {
      delete this.IEMatcher;
      return this.IEMatcher = this.urlMatcher(kIEPatternsPref);
    },

    get ChromeMatcher() {
      delete this.ChromeMatcher;
      return this.ChromeMatcher = this.urlMatcher(kChromePatternsPref);
    },

    urlMatcher: function urlMatcher(aPatternsPref) {
      var patterns = [];
      Pref.getChildList(aPatternsPref, {}).forEach(function(aPref) {
        try {
          if (Pref.getPrefType(aPref) != Pref.PREF_STRING)
            return;
          let pattern = this.getStringPref(aPref, '');
          pattern = pattern.trim();
          if (!pattern)
            return;

          pattern = pattern.replace(/([$^\|.{}\[\]()+\\])/g, "$1")
                           .replace(/\*/g, ".*")
                           .replace(/\?/g, ".");
          patterns.push(pattern);
        }
        catch(e) {
        }
      }, this);
      if (patterns.length > 0) {
        return new RegExp("^(" + patterns.join("|") + ")", "i");
      }
      else {
        return null;
      }
    },

    getStringPref: function getStringPref(aKey, aDefault) {
      try {
        return Pref.getComplexValue(aKey, Ci.nsISupportsString).data;
      }
      catch(e) {
      }
      return aDefault || '';
    },

    setStringPref: function setStringPref(aKey, aValue) {
      var str = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
      str.data = aValue;
      try {
        Pref.setComplexValue(aKey, Ci.nsISupportsString, str);
      }
      catch(e) {
      }
    },

    shellSplit: function shellSplit(aString) {
      var splitStrings = [];
      var buff = "";
      var inQuote = false;
      for (var i = 0, strLength = aString.length; i < strLength; ++i) {
        let char = aString.charAt(i);
        switch (char) {
        case '"':
          if (inQuote) {
            splitStrings.push(buff);
            buff = "";
            inQuote = false;
          }
          else {
            inQuote = true;
          }
          break;
        case ' ':
          if (inQuote) {
            buff += char;
          }
          else if (buff != "") {
            splitStrings.push(buff);
            buff = "";
          }
          break;
        default:
          buff += char;
        }
      }
      if (buff != '') {
        splitStrings.push(buff);
      }
      return splitStrings;
    }
  };

  var browser = document.getElementById("messagepane");
  browser.addEventListener("click", function onClick(aEvent) {
    SwitchLinkExternalHandler.onLinkClick(aEvent);
  }, true);
  aGlobal.SwitchLinkExternalHandler = SwitchLinkExternalHandler;
})(this);
<commit_msg>Add null check for href1<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  const Cc = Components.classes;
  const Ci = Components.interfaces;
  const Cu = Components.utils;
  const Pref = Cc['@mozilla.org/preferences;1']
                 .getService(Ci.nsIPrefBranch);
  const Registry = Cu.import("resource://gre/modules/WindowsRegistry.jsm")
                     .WindowsRegistry;
  const kChromePath = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
  const kPrefPrefix = "extensions.switch-link-external-handler@clear-code.com.";
  const kIEPatternsPref = kPrefPrefix +  "ie.patterns.";
  const kChromePatternsPref = kPrefPrefix + "chrome.patterns.";
  var SwitchLinkExternalHandler = {
    startIE: function startIE(aURL) {
      this.startExternalProcess(this.IECommandline, aURL);
    },

    startChrome: function startChrome(aURL) {
      this.startExternalProcess([kChromePath], aURL);
    },

    startExternalProcess: function startExternalProcess(aCommandLine, aURL) {
      var process = Cc["@mozilla.org/process/util;1"]
                      .createInstance(Ci.nsIProcess);
      var file = Cc["@mozilla.org/file/local;1"]
                   .createInstance(Ci.nsILocalFile);
      var exePath = aCommandLine[0];
      var args = [];
      var placeholderFound = false;
      for (var i = 1, length = aCommandLine.length; i < length; ++i) {
        if (aCommandLine[i].indexOf("%1") >= 0) {
          placeholderFound = true;
        }
        args.push(aCommandLine[i].replace("%1", aURL));
      }
      if (!placeholderFound) {
        args.push(aURL);
      }
      file.initWithPath(exePath);
      process.init(file);
      process.run(false, args, args.length);
    },

    get IECommandline() {
      delete this.IECommandline;
      var commandLine = Registry.readRegKey(
        Ci.nsIWindowsRegKey.ROOT_KEY_CLASSES_ROOT,
        "Applications\\iexplore.exe\\shell\\open\\command",
        ""
      );
      return this.IECommandline = this.shellSplit(commandLine);
    },

    onLinkClick: function onLinkClick(aEvent) {
      let href = hRefForClickEvent(aEvent);

      if (href && href.match(this.ChromeMatcher)) {
        this.startChrome(href);
      }
      else if (href && href.match(this.IEMatcher)) {
        this.startIE(href);
      }
      else {
        return;
      }
      aEvent.preventDefault();
      aEvent.stopPropagation();
    },

    get IEMatcher() {
      delete this.IEMatcher;
      return this.IEMatcher = this.urlMatcher(kIEPatternsPref);
    },

    get ChromeMatcher() {
      delete this.ChromeMatcher;
      return this.ChromeMatcher = this.urlMatcher(kChromePatternsPref);
    },

    urlMatcher: function urlMatcher(aPatternsPref) {
      var patterns = [];
      Pref.getChildList(aPatternsPref, {}).forEach(function(aPref) {
        try {
          if (Pref.getPrefType(aPref) != Pref.PREF_STRING)
            return;
          let pattern = this.getStringPref(aPref, '');
          pattern = pattern.trim();
          if (!pattern)
            return;

          pattern = pattern.replace(/([$^\|.{}\[\]()+\\])/g, "$1")
                           .replace(/\*/g, ".*")
                           .replace(/\?/g, ".");
          patterns.push(pattern);
        }
        catch(e) {
        }
      }, this);
      if (patterns.length > 0) {
        return new RegExp("^(" + patterns.join("|") + ")", "i");
      }
      else {
        return null;
      }
    },

    getStringPref: function getStringPref(aKey, aDefault) {
      try {
        return Pref.getComplexValue(aKey, Ci.nsISupportsString).data;
      }
      catch(e) {
      }
      return aDefault || '';
    },

    setStringPref: function setStringPref(aKey, aValue) {
      var str = Cc["@mozilla.org/supports-string;1"]
                  .createInstance(Ci.nsISupportsString);
      str.data = aValue;
      try {
        Pref.setComplexValue(aKey, Ci.nsISupportsString, str);
      }
      catch(e) {
      }
    },

    shellSplit: function shellSplit(aString) {
      var splitStrings = [];
      var buff = "";
      var inQuote = false;
      for (var i = 0, strLength = aString.length; i < strLength; ++i) {
        let char = aString.charAt(i);
        switch (char) {
        case '"':
          if (inQuote) {
            splitStrings.push(buff);
            buff = "";
            inQuote = false;
          }
          else {
            inQuote = true;
          }
          break;
        case ' ':
          if (inQuote) {
            buff += char;
          }
          else if (buff != "") {
            splitStrings.push(buff);
            buff = "";
          }
          break;
        default:
          buff += char;
        }
      }
      if (buff != '') {
        splitStrings.push(buff);
      }
      return splitStrings;
    }
  };

  var browser = document.getElementById("messagepane");
  browser.addEventListener("click", function onClick(aEvent) {
    SwitchLinkExternalHandler.onLinkClick(aEvent);
  }, true);
  aGlobal.SwitchLinkExternalHandler = SwitchLinkExternalHandler;
})(this);
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  var Ci = Components.interfaces;
  var Cc = Components.classes;
          //folder.getTotalMessages(true);

  var tbBug766495 = {
    collectDraftFolders: function() {
      return new Promise((function(aResolve, aReject) {
        var accountManager = MailServices.accounts;
        var allServers = accountManager.allServers;
        var folders = [];
        var expectedFoldersCount = 0;
        for (var i = 0,  maxi = allServers.length; i < maxi; ++i) {
          let currentServer = allServers.queryElementAt(i, Ci.nsIMsgIncomingServer);
          if (currentServer.type == "none")
            continue;

          var folderURI = currentServer.rootFolder.server.serverURI + "/" + "Drafts";
          var rdf = Cc['@mozilla.org/rdf/rdf-service;1']
                .getService(Ci.nsIRDFService);
          var folder = rdf.GetResource(folderURI).QueryInterface(Ci.nsIMsgFolder);
          expectedFoldersCount++;
          this.ensureUpdated(folder).then(function(folder) {
            folders.push(folder);
            if (folders.length >= expectedFoldersCount) {
              aResolve(folders);
            }
          });
        }
        if (expectedFoldersCount == 0) {
          aResolve(folders);
        }
      }).bind(this));
    },
    ensureUpdated: function(aFolder) {
      if (aFolder instanceof Ci.nsIMsgImapMailFolder) {
        return new Promise((function(aResolve, aReject) {
          var listener = this.createURLListenerForFolder(function () {
            aResolve(aFolder);
          });
          var imapDraftFolder = aFolder.QueryInterface(Ci.nsIMsgImapMailFolder);
          aFolder.updateFolderWithListener(msgWindow, listener);
        }).bind(this));
      } else {
        return Promise.resolve(aFolder);
      }
    },
    // nsIUrlListener
    createURLListenerForFolder: function createURLListenerForFolder(aCallBack) {
      return {
        OnStartRunningUrl: function OnStartRunningUrl(aURL) {
        },
        OnStopRunningUrl: function OnStopRunningUrl(aURL, aExitCode) {
          aCallBack();
        },
      };
    }
  };

  document.addEventListener("DOMContentLoaded", function onDOMContentLoaded(aEvent) {
    document.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
    tbBug766495.collectDraftFolders();
  });
  aGlobal.tbBug766495 = tbBug766495;
})(this);
<commit_msg>Import Promise module<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

(function (aGlobal) {
  var Ci = Components.interfaces;
  var Cc = Components.classes;
  const Cu = Components.utils;
  var { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});

  var tbBug766495 = {
    collectDraftFolders: function() {
      return new Promise((function(aResolve, aReject) {
        var accountManager = MailServices.accounts;
        var allServers = accountManager.allServers;
        var folders = [];
        var expectedFoldersCount = 0;
        for (var i = 0,  maxi = allServers.length; i < maxi; ++i) {
          let currentServer = allServers.queryElementAt(i, Ci.nsIMsgIncomingServer);
          if (currentServer.type == "none")
            continue;

          var folderURI = currentServer.rootFolder.server.serverURI + "/" + "Drafts";
          var rdf = Cc['@mozilla.org/rdf/rdf-service;1']
                .getService(Ci.nsIRDFService);
          var folder = rdf.GetResource(folderURI).QueryInterface(Ci.nsIMsgFolder);
          expectedFoldersCount++;
          this.ensureUpdated(folder).then(function(folder) {
            folders.push(folder);
            if (folders.length >= expectedFoldersCount) {
              aResolve(folders);
            }
          });
        }
        if (expectedFoldersCount == 0) {
          aResolve(folders);
        }
      }).bind(this));
    },
    ensureUpdated: function(aFolder) {
      if (aFolder instanceof Ci.nsIMsgImapMailFolder) {
        return new Promise((function(aResolve, aReject) {
          var listener = this.createURLListenerForFolder(function () {
            aResolve(aFolder);
          });
          var imapDraftFolder = aFolder.QueryInterface(Ci.nsIMsgImapMailFolder);
          aFolder.updateFolderWithListener(msgWindow, listener);
        }).bind(this));
      } else {
        return Promise.resolve(aFolder);
      }
    },
    // nsIUrlListener
    createURLListenerForFolder: function createURLListenerForFolder(aCallBack) {
      return {
        OnStartRunningUrl: function OnStartRunningUrl(aURL) {
        },
        OnStopRunningUrl: function OnStopRunningUrl(aURL, aExitCode) {
          aCallBack();
        },
      };
    }
  };

  document.addEventListener("DOMContentLoaded", function onDOMContentLoaded(aEvent) {
    document.removeEventListener("DOMContentLoaded", onDOMContentLoaded);
    tbBug766495.collectDraftFolders();
  });
  aGlobal.tbBug766495 = tbBug766495;
})(this);
<|endoftext|>


<commit_before>var loki = require('lokijs');
var jsgraph = require('jsgraph');
var irc  = require('tmi.js');
var uuid = require('uuid');
var express = require('express');

//var collections = require('./collections');
function Event(type, opts) {  //var e = new Event(type, args);
  this.id = uuid.v4();
  this.type = type;
  this.created = new Date();
  for (var key in opts) {
    this[key] = opts[key];
  }
  return this;
}

function DirectedGraph(name) {
  var res = jsgraph.directed.create();
  if (res.error) return console.error(res.error);
  var graph = res.result;
  graph.name = name;
  return graph;
}

module.exports = function(emitter, username, secret, config) {

  this.started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var nodes = socialdb.addCollection('nodes');
  var edges = socialdb.addCollection('edges');

  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel= function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        channel = JSON.parse(body);
        channels.insert(channel);
        var node = {
          type: "channel",
          id: channel._id,
          name: channel.name
        };
        vres = socialGraph.addVertex(u: channel._id, p: node);
        if (vres.error) return console.error(vres.error);
        nodes.insert(vres.result);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        user = JSON.parse(body);
        users.insert(user);
      });
    }
  }

  function onConnected(address, port) {
    var ts = new Date();
    var event = {
      created: ts.toUTCString(),
      type: "connected",
      address: address,
      port: port
    };
    events.insert(event);
    return true;
  }

  function onJoin(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> join", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "join",
      channel: channel,
      username: user
    };
    events.insert(event);
    cacheUser(user);
  }

  function onPart(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> part", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "part",
      channel: channel,
      username: user
    };
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = {
      created: ts.toUTCString(),
      type: "hosted",
      channel: channel,
      username: user,
      viewers: viewers
    };
    events.insert(event);
    cacheUser(user.username);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "chat",
      channel: channel,
      username: user.username,
      message: message
    });
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "action",
      channel: channel,
      user: user.username,
      message: message
    });
    cacheUser(user.username);
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });

  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  this.disconnect = function() {
    this.client.disconnect();
  };

  return this;
};
<commit_msg>missing brackets<commit_after>var loki = require('lokijs');
var jsgraph = require('jsgraph');
var irc  = require('tmi.js');
var uuid = require('uuid');
var express = require('express');

//var collections = require('./collections');
function Event(type, opts) {  //var e = new Event(type, args);
  this.id = uuid.v4();
  this.type = type;
  this.created = new Date();
  for (var key in opts) {
    this[key] = opts[key];
  }
  return this;
}

function DirectedGraph(name) {
  var res = jsgraph.directed.create();
  if (res.error) return console.error(res.error);
  var graph = res.result;
  graph.name = name;
  return graph;
}

module.exports = function(emitter, username, secret, config) {

  this.started = new Date();

  var lokiConfig = {
    autosave: true,
    autosaveInterval: 1000
  };

  var socialdb = new loki('twitch-' + username + '-social-graph.json');
  var nodes = socialdb.addCollection('nodes');
  var edges = socialdb.addCollection('edges');

  var socialGraph = new DirectedGraph("twitch-" + username + "-social");

  var channeldb = new loki('twitch-channel.json', lokiConfig);
  var channels  = channeldb.addCollection('channels');

  var userdb = new loki('twitch-' + username + '.json', lokiConfig);
  var users      = userdb.addCollection('users');
  var hosting    = userdb.addCollection('hosting');
  var follows    = userdb.addCollection('follow');
  var events     = userdb.addCollection('event');

  this.client = new irc.client(config);

  this.cacheChannel= function(name) {
    //var channel = channels.findObject({"name": {"$eq": name}});
    if (!channel) { // always cache channels for now
      client.api({
        url: "https://api.twitch.tv/kraken/channels/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        channel = JSON.parse(body);
        channels.insert(channel);
        var node = {
          type: "channel",
          id: channel._id,
          name: channel.name
        };
        vres = socialGraph.addVertex({u: channel._id, p: node});
        if (vres.error) return console.error(vres.error);
        nodes.insert(vres.result);
      });
      cacheChannelHosting(name);
      cacheChannelFollows(name);
    }
  }

  this.cacheChannelHosting = function(name) {

  }

  this.cacheChannelFollows = function(name) {

  }

  this.cacheUser = function(name) {
    var user = users.findObject({"username": {"$eq": name}});
    if (!user) {
      this.client.api({
        url: "https://api.twitch.tv/kraken/users/" + name
      }, function(err, res, body) {
        if (err) return console.error(err);
        user = JSON.parse(body);
        users.insert(user);
      });
    }
  }

  function onConnected(address, port) {
    var ts = new Date();
    var event = {
      created: ts.toUTCString(),
      type: "connected",
      address: address,
      port: port
    };
    events.insert(event);
    return true;
  }

  function onJoin(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> join", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "join",
      channel: channel,
      username: user
    };
    events.insert(event);
    cacheUser(user);
  }

  function onPart(channel, user) {
    var ts = new Date();
    console.log("%s [%s] <%s> part", ts.toUTCString(), channel, user);
    var event = {
      created: ts.toUTCString(),
      type: "part",
      channel: channel,
      username: user
    };
    events.insert(event);
  };

  function onHosted(channel, user, viewers) {
    var ts = new Date();
    console.log("%s [%s] <%s> host %s", ts.toUTCString(), channel, user, viewers);
    var event = {
      created: ts.toUTCString(),
      type: "hosted",
      channel: channel,
      username: user,
      viewers: viewers
    };
    events.insert(event);
    cacheUser(user.username);
  }

  function onChat(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "chat",
      channel: channel,
      username: user.username,
      message: message
    });
    cacheUser(user.username);
    var commandPattern = new RegExp("^!\\w+");
    if (commandPattern.test(message)) {
      var command = commandPattern.exec(message);
      emitter.emit("command-dispatch", command, user.username, message);
    }
  }

  function onAction(channel, user, message, self) {
    var ts = new Date();
    console.log("%s [%s] <*%s> %s", ts.toUTCString(), channel, user.username, message);
    events.insert({
      created: ts.toUTCString(),
      type: "action",
      channel: channel,
      user: user.username,
      message: message
    });
    cacheUser(user.username);
  };

  this.say = function(channel, message) {
    this.client.say(channel, message);
  }

  this.client.on("connected", onConnected);
  this.client.on("join", onJoin);
  this.client.on("part", onPart);
  this.client.on("hosted", onHosted);
  this.client.on("chat", onChat);
  this.client.on("action", onAction);

  emitter.on('cache-user', this.cacheUser);
  emitter.on('cache-channel', this.cacheChannel);
  emitter.on('cache-channel-hosting', this.cacheChannelHosting);
  emitter.on('cache-channel-follows', this.cacheChannelFollows);

  this.router = express.Router();
  this.router.get('/channel/:name', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(channels.find({"name": req.params.name}));
  });
  this.router.get('/channel/:name/hosting', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(hosting.find());
  });
  this.router.get('/channel/:name/follows', function(req, res) {
    cacheChannel(req.params.name);
    return res.json(follows.find());
  });

  // register new router for every new user identified so they get their own base loki file registered once

  this.connect = function() {
    this.client.connect();
  };

  this.disconnect = function() {
    this.client.disconnect();
  };

  return this;
};
<|endoftext|>


<commit_before>'use strict';

    var callButton = document.getElementById('callButton');
    var hangupButton = document.getElementById('hangupButton');
    callButton.disabled = false;
    //hangupButton.disabled = true;
    callButton.onclick = call;
    hangupButton.onclick = hangup;

    var startTime;
    var localVideo = document.getElementById('localVideo');
    var remoteVideo = document.getElementById('remoteVideo');

    var localStream;
    var connection;

    var sdpConstraints = {
        'mandatory': {
            'OfferToReceiveAudio': false,
            'OfferToReceiveVideo': true
        }
    };

    localVideo.addEventListener('loadedmetadata', function () {
        trace('Local video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.addEventListener('loadedmetadata', function () {
        trace('Remote video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.onresize = function () {
        trace('Remote video size changed to ' +
          remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
        // We'll use the first onsize callback as an indication that video has started playing out.
        if (startTime) {
            var elapsedTime = window.performance.now() - startTime;
            trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
            startTime = null;
        }
    };

    function start() {
        remoteVideo.hidden = true;
        hangupButton.disabled = true;
  // Call into getUserMedia via the polyfill (adapter.js).
  getUserMedia({
      audio: true,
      video: true
    }, gotStream,
    function(e) {
        //alert('getUserMedia() error: ' + e.name);
        alert('Sorry, you web cam is absent or unavailable');
        callButton.disabled = true;
    });
  //var servers = null;
  //var servers = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] };
  var servers = { 'iceServers': [{ 'url': 'stun:74.125.142.127:19302' }] };
  //var  _iceServers = [{ url: 'stun:74.125.142.127:19302' }], // stun.l.google.com - Firefox does not support DNS names.
  connection = new RTCPeerConnection(servers);
  //connection = new RTCPeerConnection({ iceServers: _iceServers });
  trace('Created connection object');
  connection.onicecandidate = function (e) {
      chat.server.iceCandidate(JSON.stringify({ "candidate": e.candidate }));
  };
  connection.onaddstream = function (e) {
      // Call the polyfill wrapper to attach the media stream to this element.
      callButton.disabled = true;
      //remoteVideo.hidden = false;
      attachMediaStream(remoteVideo, e.stream);
      trace('received remote stream');
  };
}

function call() {
    callButton.disabled = true;
    remoteVideo.hidden = false;
  hangupButton.disabled = false;
  trace('Starting call');
  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }

  if (localStream != null) {
      connection.addStream(localStream);
      trace('Added local stream to connection');
  }

  trace('connection createOffer start');
  connection.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError);
}

function answer(message) {
    remoteVideo.hidden = false;
    hangupButton.disabled = false;
    trace('send answer ' + message.sdp);
    connection.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
        trace('setRemoteDescription');
        if (localStream != null) {
            connection.addStream(localStream);
        }
            connection.createAnswer(function (desc) {
                connection.setLocalDescription(desc, function () {
                    chat.server.answer(JSON.stringify({ "sdp": desc}));
                });
            });
    });
}

function addIceCandidate(message) {
    if (message.candidate != null) {
        trace('add ice candidate');
        connection.addIceCandidate(new RTCIceCandidate(message.candidate));
    }
}

function getAnswer(message) {
    if (message.sdp != null) {
        trace('get answer');
        connection.setRemoteDescription(new RTCSessionDescription(message.sdp));
    }
}

function gotStream(stream) {
    trace('Received local stream');
    // Call the polyfill wrapper to attach the media stream to this element.
    attachMediaStream(localVideo, stream);
    localStream = stream;
    callButton.disabled = false;
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
    trace('Offer created');
  trace('setLocalDescription start');
  connection.setLocalDescription(desc, function () {
    chat.server.offer(JSON.stringify({ "sdp": desc }));
    onSetLocalSuccess(connection);
  });
}

function onSetLocalSuccess(connection) {
  trace(' setLocalDescription complete');
}

function onSetRemoteSuccess(connection) {
  trace(' setRemoteDescription complete');
}

function onCreateAnswerSuccess(desc) {
    trace('Answer from pc2:\n' + desc.sdp);
    trace('pc1 setRemoteDescription start');
  trace('pc2 setLocalDescription start');
  connection.setLocalDescription(desc, function () {
      onSetLocalSuccess(connection);
  });
}

function onAddIceCandidateSuccess(connection) {
  trace(' addIceCandidate success');
}

function onAddIceCandidateError(connection, error) {
  trace(' failed to add ICE Candidate: ' + error.toString());
}

function hangup() {
  trace('Ending call');
  connection.close();
  connection = null;
  start();
  //hangupButton.disabled = true;
  //callButton.disabled = false;
  //remoteVideo.hidden = true;
}
<commit_msg>hang up again<commit_after>'use strict';

    var callButton = document.getElementById('callButton');
    var hangupButton = document.getElementById('hangupButton');
    callButton.disabled = false;
    //hangupButton.disabled = true;
    callButton.onclick = call;
    hangupButton.onclick = hangup;

    var startTime;
    var localVideo = document.getElementById('localVideo');
    var remoteVideo = document.getElementById('remoteVideo');

    var localStream;
    var connection;

    var sdpConstraints = {
        'mandatory': {
            'OfferToReceiveAudio': false,
            'OfferToReceiveVideo': true
        }
    };

    localVideo.addEventListener('loadedmetadata', function () {
        trace('Local video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.addEventListener('loadedmetadata', function () {
        trace('Remote video currentSrc: ' + this.currentSrc +
          ', videoWidth: ' + this.videoWidth +
          'px,  videoHeight: ' + this.videoHeight + 'px');
    });

    remoteVideo.onresize = function () {
        trace('Remote video size changed to ' +
          remoteVideo.videoWidth + 'x' + remoteVideo.videoHeight);
        // We'll use the first onsize callback as an indication that video has started playing out.
        if (startTime) {
            var elapsedTime = window.performance.now() - startTime;
            trace('Setup time: ' + elapsedTime.toFixed(3) + 'ms');
            startTime = null;
        }
    };

    function start() {
        remoteVideo.hidden = true;
        hangupButton.disabled = true;
  // Call into getUserMedia via the polyfill (adapter.js).
  getUserMedia({
      audio: true,
      video: true
    }, gotStream,
    function(e) {
        //alert('getUserMedia() error: ' + e.name);
        alert('Sorry, you web cam is absent or unavailable');
        callButton.disabled = true;
    });
  //var servers = null;
  //var servers = { 'iceServers': [{ 'url': 'stun:stun.l.google.com:19302' }] };
  var servers = { 'iceServers': [{ 'url': 'stun:74.125.142.127:19302' }] };
  //var  _iceServers = [{ url: 'stun:74.125.142.127:19302' }], // stun.l.google.com - Firefox does not support DNS names.
  connection = new RTCPeerConnection(servers);
  //connection = new RTCPeerConnection({ iceServers: _iceServers });
  trace('Created connection object');
  connection.onicecandidate = function (e) {
      chat.server.iceCandidate(JSON.stringify({ "candidate": e.candidate }));
  };
  connection.onaddstream = function (e) {
      // Call the polyfill wrapper to attach the media stream to this element.
      callButton.disabled = true;
      //remoteVideo.hidden = false;
      attachMediaStream(remoteVideo, e.stream);
      trace('received remote stream');
  };
}

function call() {
    callButton.disabled = true;
    remoteVideo.hidden = false;
  hangupButton.disabled = false;
  trace('Starting call');
  startTime = window.performance.now();
  var videoTracks = localStream.getVideoTracks();
  var audioTracks = localStream.getAudioTracks();
  if (videoTracks.length > 0) {
    trace('Using video device: ' + videoTracks[0].label);
  }
  if (audioTracks.length > 0) {
    trace('Using audio device: ' + audioTracks[0].label);
  }

  if (localStream != null) {
      connection.addStream(localStream);
      trace('Added local stream to connection');
  }

  trace('connection createOffer start');
  connection.createOffer(onCreateOfferSuccess, onCreateSessionDescriptionError);
}

function answer(message) {
    remoteVideo.hidden = false;
    hangupButton.disabled = false;
    trace('send answer ' + message.sdp);
    connection.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
        trace('setRemoteDescription');
        if (localStream != null) {
            connection.addStream(localStream);
        }
            connection.createAnswer(function (desc) {
                connection.setLocalDescription(desc, function () {
                    chat.server.answer(JSON.stringify({ "sdp": desc}));
                });
            });
    });
}

function addIceCandidate(message) {
    if (message.candidate != null) {
        trace('add ice candidate');
        connection.addIceCandidate(new RTCIceCandidate(message.candidate));
    }
}

function getAnswer(message) {
    if (message.sdp != null) {
        trace('get answer');
        connection.setRemoteDescription(new RTCSessionDescription(message.sdp));
    }
}

function gotStream(stream) {
    trace('Received local stream');
    // Call the polyfill wrapper to attach the media stream to this element.
    attachMediaStream(localVideo, stream);
    localStream = stream;
    callButton.disabled = false;
}

function onCreateSessionDescriptionError(error) {
  trace('Failed to create session description: ' + error.toString());
}

function onCreateOfferSuccess(desc) {
    trace('Offer created');
  trace('setLocalDescription start');
  connection.setLocalDescription(desc, function () {
    chat.server.offer(JSON.stringify({ "sdp": desc }));
    onSetLocalSuccess(connection);
  });
}

function onSetLocalSuccess(connection) {
  trace(' setLocalDescription complete');
}

function onSetRemoteSuccess(connection) {
  trace(' setRemoteDescription complete');
}

function onCreateAnswerSuccess(desc) {
    trace('Answer from pc2:\n' + desc.sdp);
    trace('pc1 setRemoteDescription start');
  trace('pc2 setLocalDescription start');
  connection.setLocalDescription(desc, function () {
      onSetLocalSuccess(connection);
  });
}

function onAddIceCandidateSuccess(connection) {
  trace(' addIceCandidate success');
}

function onAddIceCandidateError(connection, error) {
  trace(' failed to add ICE Candidate: ' + error.toString());
}

function hangup() {
  trace('Ending call');
  //connection.close();
  //connection = null;
  //start();
  hangupButton.disabled = true;
  callButton.disabled = false;
  remoteVideo.hidden = true;
}
<|endoftext|>