<commit_before>var
  Vec2 = require('vec2'),
  Grid2;

Grid2 = function Grid2(config) {
  var
    size,
    cellSize,
    cellHalfSize,
    ids           = 1,
    objects       = {},
    cells         = {},
    objectCells   = {},
    cache         = { between : { dirty : 0, queries : {} } },
    dirty         = 1,
    idKey         = 'id',
    positionKey   = 'pos',
    halfSizeKey   = 'halfSize',
    UNIT          = new Vec2(1, 1),
    HALF_PI       = Math.PI / 2,

    Grid2Cell = function Grid2Cell(id, position) {
      this.id          = id;
      this.begPosition = position.clone();
      this.center      = this.begPosition.add(cellHalfSize, true);
      this.objects     = {};
      this.meta        = {};
    },

    cacheObjects = function cacheObjects(type, key, objects) {
      cache[type].dirty        = dirty;
      cache[type].queries[key] = objects;
    },

    getCached = function getCached(type, key) {
      if (cache[type].dirty === dirty && cache[type].queries[key]) {
        return cache[type].queries[key];
      }
    },

    getCacheKey = function getCacheKey(begPosition, endPosition) {
      return begPosition.toString() + '_' + endPosition.toString();
    },

    setDirty = function setDirty(dirt) {
      if (dirt) { dirty++; }
    },

    nextId = function nextId() {
      return ids++;
    },

    getObjectCells = function getObjectCells(object) {
      if (!objectCells[object[idKey]]) {
        objectCells[object[idKey]] = {};
      }

      return objectCells[object[idKey]];
    },

    getOrCreateCellByCellPosition = function getOrCreateCellByCellPosition(position) {
      var key = position.toString();

      if (!cells[key]) {
        cells[key] = new Grid2Cell(key, position);
      }

      return cells[key];
    },

    getOrCreateCellByPosition = function getOrCreateCellByPosition(position) {
      var key;

      position  = getCellBegPosition(position);
      key       = position.toString();

      if (!cells[key]) {
        cells[key] = new Grid2Cell(key, position);
      }

      return cells[key];
    },

    getOrCreateCellsByObject = function getOrCreateCellsByObject(object) {
      var
        key,
        position,
        objectCells       = {},
        objectBegPosition = object[positionKey].subtract(object[halfSizeKey], true),
        objectEndPosition = object[positionKey].add(object[halfSizeKey], true).subtract(UNIT),
        cellBegPosition   = getCellBegPosition(objectBegPosition),
        cellEndPosition   = getCellBegPosition(objectEndPosition);

      // As the lower/right side of a cell is considered as the beginning of the neighbour cell
      // we need to subtract one UNIT from the end position of the object, otherwise it will be registered
      // twice, see how objectEndPosition is calculated.
      // +) Not sure if this is the right way, but in my game use case, it is.

      for (position = cellBegPosition.clone(); position.x <= cellEndPosition.x; position.x += cellSize.x) {
        for (position.y = cellBegPosition.y; position.y <= cellEndPosition.y; position.y += cellSize.y) {
          key               = position.toString();
          objectCells[key]  = cells[key];

          if (!objectCells[key]) {
            objectCells[key] = cells[key] = new Grid2Cell(key, position);
          }
        }
      }

      return objectCells;
    },

    getCellBegPosition = function getCellBegPosition(position) {
      return new Vec2(
        Math.floor(position.x / cellSize.x) * cellSize.x,
        Math.floor(position.y / cellSize.y) * cellSize.y
      );
    },

    propagateCallbackOnGrid = function propagateCallbackOnGrid(cell, diff, cb, cbMemory) {
      if (cbMemory[cell.id]) { return; }

      cbMemory[cell.id] = true;

      if (!cb.call(cbMemory.cbThis, cbMemory.cbConfig, cell.center)) { return; }

      var nextCell,
          nextCellPosition  = cell.begPosition.add(diff, true),
          rotatedDiff       = diff.rotate(HALF_PI, false, true);

      // Use the given direction differnce and recall the function.
      nextCell = getOrCreateCellByCellPosition(nextCellPosition);
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);

      // Call to its neighbours too.
      nextCell = getOrCreateCellByCellPosition(nextCellPosition.add(rotatedDiff, true));
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);

      nextCell = getOrCreateCellByCellPosition(nextCellPosition.subtract(rotatedDiff, true));
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);
    },

    setObjId = function setObjId(object) {
      if (!object[idKey]) {
        object[idKey] = nextId();
      }
    },

    updateObjectCells = function updateObjectCells(object) {
      var
        cell,
        cellId,
        dirt     = false,
        oldCells = getObjectCells(object),
        newCells = getOrCreateCellsByObject(object);

      for (cellId in oldCells) {
        if (newCells[cellId]) {
          continue;
        }

        dirt  = true;
        cell  = oldCells[cellId];

        // Remove the object from the cell.
        delete cell.objects[object[idKey]];

        // Remove the cell from the objects-cell index.
        delete oldCells[cellId];
      }

      for (cellId in newCells) {
        if (oldCells[cellId]) {
          continue;
        }

        dirt                            = true;
        cell                            = newCells[cellId];
        cell.objects[object[idKey]]   = object;
        oldCells[cellId]                = cell;
      }

      setDirty(dirt);
    },

    /**
     * Public fns
     */

    addObject = function addObject(object) {
      setObjId(object);
      updateObjectCells(object);
      objects[object[idKey]] = object;
    },

    addObjects = function addObjects(objects) {
      for (var i = 0; i < objects.length; i++) {
        addObject(objects[i]);
      }
    },

    getMetaOn = function getMetaOn(position, key) {
      var cell;

      position = getCellBegPosition(position);
      cell = cells[position.toString()];

      return cell && cell.meta[key];
    },

    getObjects = function getObjects() {
      return objects;
    },

    getObjectsBetween = function getObjectsBetween(begPosition, endPosition) {
      var
        cell,
        position,
        objects             = {},
        betweenBegPosition  = getCellBegPosition(begPosition),
        betweenEndPosition  = getCellBegPosition(endPosition),
        cacheKey            = getCacheKey(betweenBegPosition, betweenEndPosition),
        cached              = getCached('between', cacheKey);

      if (cached) {
        return cached;
      }

      for (position = betweenBegPosition.clone(); position.x <= betweenEndPosition.x; position.x += cellSize.x) {
        for (position.y = betweenBegPosition.y; position.y <= betweenEndPosition.y; position.y += cellSize.y) {
          cell = cells[position.toString()];

          if (!cell) { continue; }

          for (var id in cell.objects) {
            objects[id] = cell.objects[id];
          }
        }
      }

      cacheObjects('between', cacheKey, objects);

      return objects;
    },

    getObjectsOn = function getObjectsOn(position) {
        var cell;

        position = getCellBegPosition(position);
        cell = cells[position.toString()];

        return cell && cell.objects;
    },

    getCellSize = function getCellSize() {
      return cellSize.clone();
    },

    getSize = function getSize() {
      return size.clone();
    },

    hasObjectsOn = function hasObjectsOn(position) {
      var objects = getObjectsOn(position);

      return !!(objects && Object.keys(objects).length);
    },

    propagateCallbackFromPoint = function propagateCallbackFromPoint(position, cb, cbThis, cbConfig) {
      var
        cell          = getOrCreateCellByPosition(position),
        cbMemory      = { cbConfig : cbConfig, cbThis : cbThis };

      propagateCallbackOnGrid(cell, new Vec2( cellSize.x, 0), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(-cellSize.x, 0), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(0,  cellSize.y), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(0, -cellSize.y), cb, cbMemory);
    },

    updateObject = function updateObject(object) {
      updateObjectCells(object);
    },

    updateObjects = function updateObjects(objects) {
      for (var i = 0; i < objects.length; i++) {
        updateObject(objects[i]);
      }
    },

    setMetaOn = function setMetaOn(position, key, val) {
      var cell = getOrCreateCellByPosition(position);

      cell.meta[key] = val;
    },

    debug = function debug() {
      this.objects  = objects;
      this.cells    = cells;
    },

    init = function init(config) {
      size          = config.size.clone();
      cellSize      = config.cellSize.clone();
      cellHalfSize  = cellSize.multiply(0.5, true);
    };

  init(config);

  this.addObject                  = addObject;
  this.addObjects                 = addObjects;
  this.debug                      = debug;
  this.getMetaOn                  = getMetaOn;
  this.getObjects                 = getObjects;
  this.getObjectsBetween          = getObjectsBetween;
  this.getObjectsOn               = getObjectsOn;
  this.getCellSize                = getCellSize;
  this.getSize                    = getSize;
  this.hasObjectsOn               = hasObjectsOn;
  this.propagateCallbackFromPoint = propagateCallbackFromPoint;
  this.updateObject               = updateObject;
  this.updateObjects              = updateObjects;
  this.setMetaOn                  = setMetaOn;
};

module.exports = Grid2;
<commit_msg>Improves code formatting.<commit_after>var
  Vec2 = require('vec2'),
  Grid2;

Grid2 = function Grid2(config) {
  var
    size,
    cellSize,
    cellHalfSize,
    ids           = 1,
    objects       = {},
    cells         = {},
    objectCells   = {},
    cache         = { between : { dirty : 0, queries : {} } },
    dirty         = 1,
    idKey         = 'id',
    positionKey   = 'pos',
    halfSizeKey   = 'halfSize',
    UNIT          = new Vec2(1, 1),
    HALF_PI       = Math.PI / 2,

    Grid2Cell = function Grid2Cell(id, position) {
      this.id          = id;
      this.begPosition = position.clone();
      this.center      = this.begPosition.add(cellHalfSize, true);
      this.objects     = {};
      this.meta        = {};
    },

    cacheObjects = function cacheObjects(type, key, objects) {
      cache[type].dirty        = dirty;
      cache[type].queries[key] = objects;
    },

    getCached = function getCached(type, key) {
      if (cache[type].dirty === dirty && cache[type].queries[key]) {
        return cache[type].queries[key];
      }
    },

    getCacheKey = function getCacheKey(begPosition, endPosition) {
      return begPosition.toString() + '_' + endPosition.toString();
    },

    setDirty = function setDirty(dirt) {
      if (dirt) { dirty++; }
    },

    nextId = function nextId() {
      return ids++;
    },

    getObjectCells = function getObjectCells(object) {
      if (!objectCells[object[idKey]]) {
        objectCells[object[idKey]] = {};
      }

      return objectCells[object[idKey]];
    },

    getOrCreateCellByCellPosition = function getOrCreateCellByCellPosition(position) {
      var key = position.toString();

      if (!cells[key]) {
        cells[key] = new Grid2Cell(key, position);
      }

      return cells[key];
    },

    getOrCreateCellByPosition = function getOrCreateCellByPosition(position) {
      var key;

      position  = getCellBegPosition(position);
      key       = position.toString();

      if (!cells[key]) {
        cells[key] = new Grid2Cell(key, position);
      }

      return cells[key];
    },

    getOrCreateCellsByObject = function getOrCreateCellsByObject(object) {
      var
        key,
        position,
        objectCells       = {},
        objectBegPosition = object[positionKey].subtract(object[halfSizeKey], true),
        objectEndPosition = object[positionKey].add(object[halfSizeKey], true).subtract(UNIT),
        cellBegPosition   = getCellBegPosition(objectBegPosition),
        cellEndPosition   = getCellBegPosition(objectEndPosition);

      // As the lower/right side of a cell is considered as the beginning of the neighbour cell
      // we need to subtract one UNIT from the end position of the object, otherwise it will be registered
      // twice, see how objectEndPosition is calculated.
      // +) Not sure if this is the right way, but in my game use case, it is.

      for (position = cellBegPosition.clone(); position.x <= cellEndPosition.x; position.x += cellSize.x) {
        for (position.y = cellBegPosition.y; position.y <= cellEndPosition.y; position.y += cellSize.y) {
          key               = position.toString();
          objectCells[key]  = cells[key];

          if (!objectCells[key]) {
            objectCells[key] = cells[key] = new Grid2Cell(key, position);
          }
        }
      }

      return objectCells;
    },

    getCellBegPosition = function getCellBegPosition(position) {
      return new Vec2(
        Math.floor(position.x / cellSize.x) * cellSize.x,
        Math.floor(position.y / cellSize.y) * cellSize.y
      );
    },

    propagateCallbackOnGrid = function propagateCallbackOnGrid(cell, diff, cb, cbMemory) {
      if (cbMemory[cell.id]) { return; }

      cbMemory[cell.id] = true;

      if (!cb.call(cbMemory.cbThis, cbMemory.cbConfig, cell.center)) { return; }

      var nextCell,
          nextCellPosition  = cell.begPosition.add(diff, true),
          rotatedDiff       = diff.rotate(HALF_PI, false, true);

      // Use the given direction differnce and recall the function.
      nextCell = getOrCreateCellByCellPosition(nextCellPosition);
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);

      // Call to its neighbours too.
      nextCell = getOrCreateCellByCellPosition(nextCellPosition.add(rotatedDiff, true));
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);

      nextCell = getOrCreateCellByCellPosition(nextCellPosition.subtract(rotatedDiff, true));
      propagateCallbackOnGrid(nextCell, diff.clone(), cb, cbMemory);
    },

    setObjId = function setObjId(object) {
      if (!object[idKey]) {
        object[idKey] = nextId();
      }
    },

    updateObjectCells = function updateObjectCells(object) {
      var
        cell,
        cellId,
        dirt     = false,
        oldCells = getObjectCells(object),
        newCells = getOrCreateCellsByObject(object);

      for (cellId in oldCells) {
        if (newCells[cellId]) {
          continue;
        }

        dirt  = true;
        cell  = oldCells[cellId];

        // Remove the object from the cell.
        delete cell.objects[object[idKey]];

        // Remove the cell from the objects-cell index.
        delete oldCells[cellId];
      }

      for (cellId in newCells) {
        if (oldCells[cellId]) {
          continue;
        }

        dirt                            = true;
        cell                            = newCells[cellId];
        cell.objects[object[idKey]]   = object;
        oldCells[cellId]                = cell;
      }

      setDirty(dirt);
    },

    /**
     * Public fns
     */

    addObject = function addObject(object) {
      setObjId(object);
      updateObjectCells(object);
      objects[object[idKey]] = object;
    },

    addObjects = function addObjects(objects) {
      var
        i;

      for (i = 0; i < objects.length; i++) {
        addObject(objects[i]);
      }
    },

    getMetaOn = function getMetaOn(position, key) {
      var cell;

      position = getCellBegPosition(position);
      cell = cells[position.toString()];

      return cell && cell.meta[key];
    },

    getObjects = function getObjects() {
      return objects;
    },

    getObjectsBetween = function getObjectsBetween(begPosition, endPosition) {
      var
        id,
        cell,
        position,
        objects             = {},
        betweenBegPosition  = getCellBegPosition(begPosition),
        betweenEndPosition  = getCellBegPosition(endPosition),
        cacheKey            = getCacheKey(betweenBegPosition, betweenEndPosition),
        cached              = getCached('between', cacheKey);

      if (cached) {
        return cached;
      }

      for (position = betweenBegPosition.clone(); position.x <= betweenEndPosition.x; position.x += cellSize.x) {
        for (position.y = betweenBegPosition.y; position.y <= betweenEndPosition.y; position.y += cellSize.y) {
          cell = cells[position.toString()];

          if (!cell) { continue; }

          for (id in cell.objects) {
            objects[id] = cell.objects[id];
          }
        }
      }

      cacheObjects('between', cacheKey, objects);

      return objects;
    },

    getObjectsOn = function getObjectsOn(position) {
        var cell;

        position = getCellBegPosition(position);
        cell = cells[position.toString()];

        return cell && cell.objects;
    },

    getCellSize = function getCellSize() {
      return cellSize.clone();
    },

    getSize = function getSize() {
      return size.clone();
    },

    hasObjectsOn = function hasObjectsOn(position) {
      var objects = getObjectsOn(position);

      return !!(objects && Object.keys(objects).length);
    },

    propagateCallbackFromPoint = function propagateCallbackFromPoint(position, cb, cbThis, cbConfig) {
      var
        cell          = getOrCreateCellByPosition(position),
        cbMemory      = { cbConfig : cbConfig, cbThis : cbThis };

      propagateCallbackOnGrid(cell, new Vec2( cellSize.x, 0), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(-cellSize.x, 0), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(0,  cellSize.y), cb, cbMemory);
      delete cbMemory[cell.id];

      propagateCallbackOnGrid(cell, new Vec2(0, -cellSize.y), cb, cbMemory);
    },

    updateObject = function updateObject(object) {
      updateObjectCells(object);
    },

    updateObjects = function updateObjects(objects) {
      var
        i;

      for (i = 0; i < objects.length; i++) {
        updateObject(objects[i]);
      }
    },

    setMetaOn = function setMetaOn(position, key, val) {
      var cell = getOrCreateCellByPosition(position);

      cell.meta[key] = val;
    },

    debug = function debug() {
      this.objects  = objects;
      this.cells    = cells;
    },

    init = function init(config) {
      size          = config.size.clone();
      cellSize      = config.cellSize.clone();
      cellHalfSize  = cellSize.multiply(0.5, true);
    };

  init(config);

  this.addObject                  = addObject;
  this.addObjects                 = addObjects;
  this.debug                      = debug;
  this.getMetaOn                  = getMetaOn;
  this.getObjects                 = getObjects;
  this.getObjectsBetween          = getObjectsBetween;
  this.getObjectsOn               = getObjectsOn;
  this.getCellSize                = getCellSize;
  this.getSize                    = getSize;
  this.hasObjectsOn               = hasObjectsOn;
  this.propagateCallbackFromPoint = propagateCallbackFromPoint;
  this.updateObject               = updateObject;
  this.updateObjects              = updateObjects;
  this.setMetaOn                  = setMetaOn;
};

module.exports = Grid2;
<|endoftext|>


<commit_before>// Source : https://oj.leetcode.com/problems/add-two-numbers/
// Author : Xiao Shi
// Date   : 2015-06-01

/**********************************************************************************
 *
 * You are given two linked lists representing two non-negative numbers.
 * The digits are stored in reverse order and each of their nodes contain a single digit.
 * Add the two numbers and return it as a linked list.
 *
 * Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
 * Output: 7 -> 0 -> 8
 *
 **********************************************************************************/

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
// Solution 1:
// Runtime: 276 ms
var addTwoNumbers = function(l1, l2) {
    var arr = [];
    (l1 || l2) && f(arr, l1, l2, 0);
    return arr;
};

function f(arr, l1, l2, n) {
    if (!l1 && !l2) {
        n && arr.push(n);
        return;
    }
    var sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + n;
    var m = ~~(sum / 10); // OR (sum / 10) >> 0 OR (sum / 10) | 0

    arr.push(sum % 10);
    f(arr, l1 && l1.next, l2 && l2.next, m);
}

// Solution 2:
// Runtime: 272
// var addTwoNumbers = function(l1, l2) {
//     var sum = l1.val + l2.val;
//     var next1 = l1.next;
//     var next2 = l2.next;
//     var result = new ListNode(sum % 10);
//     var node = result;
//     sum = ~~(sum / 10);

//     while (next1 || next2 || sum !== 0) {
//         if (!next1) {
//             next1 = {
//                 val: 0,
//                 next: null
//             };
//         }
//         if (!next2) {
//             next2 = {
//                 val: 0,
//                 next: null
//             };
//         }
//         sum += next1.val + next2.val;
//         next1 = next1.next;
//         next2 = next2.next;
//         node.next = new ListNode(sum % 10);
//         node = node.next;
//         sum = ~~(sum / 10);
//     }
//     return result;
// };

// Test Case
// ************ Note ************
// It only works for 1st solution
// ******************************
var l1 = {
    val: 2,
    next: {
        val: 4,
        next: {
            val: 3,
            next: null
        }
    }
};

var l2 = {
    val: 5,
    next: {
        val: 6,
        next: {
            val: 4,
            next: null
        }
    }
};

var result = addTwoNumbers(l1, l2);

console.log(result.toString() === '7,0,8');
<commit_msg>Updated AddTwoNumbers<commit_after>// Source : https://oj.leetcode.com/problems/add-two-numbers/
// Author : Xiao Shi
// Date   : 2015-06-01

/**********************************************************************************
 *
 * You are given two linked lists representing two non-negative numbers.
 * The digits are stored in reverse order and each of their nodes contain a single digit.
 * Add the two numbers and return it as a linked list.
 *
 * Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
 * Output: 7 -> 0 -> 8
 *
 **********************************************************************************/

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
// Solution 1:
// Runtime: 276 ms
var addTwoNumbers = function(l1, l2) {
    var arr = [];
    (l1 || l2) && f(arr, l1, l2, 0);
    return arr;
};

function f(arr, l1, l2, n) {
    if (!l1 && !l2) {
        n && arr.push(n);
        return;
    }
    var sum = (l1 ? l1.val : 0) + (l2 ? l2.val : 0) + n;
    var m = ~~(sum / 10); // OR (sum / 10) >> 0 OR (sum / 10) | 0

    arr.push(sum % 10);
    f(arr, l1 && l1.next, l2 && l2.next, m);
}

// Solution 2:
// Runtime: 272
// var addTwoNumbers = function(l1, l2) {
//     var sum = l1.val + l2.val;
//     var next1 = l1.next;
//     var next2 = l2.next;
//     var result = new ListNode(sum % 10);
//     var node = result;
//     sum = ~~(sum / 10);

//     while (next1 || next2 || sum !== 0) {
//         if (!next1) {
//             next1 = {
//                 val: 0,
//                 next: null
//             };
//         }
//         if (!next2) {
//             next2 = {
//                 val: 0,
//                 next: null
//             };
//         }
//         sum += next1.val + next2.val;
//         next1 = next1.next;
//         next2 = next2.next;
//         node.next = new ListNode(sum % 10);
//         node = node.next;
//         sum = ~~(sum / 10);
//     }
//     return result;
// };

// Test Case
// ************ Note ************
// It only works for 1st solution
// ******************************
var l1 = {
    val: 2,
    next: {
        val: 4,
        next: {
            val: 3,
            next: null
        }
    }
};

var l2 = {
    val: 5,
    next: {
        val: 6,
        next: {
            val: 4,
            next: null
        }
    }
};

var result = addTwoNumbers(l1, l2);

console.log(result.toString() === '7,0,8'); // true
<|endoftext|>


<commit_before>/**
 * Created by ate on 27.09.16.
 */
angular.module('ubirchAdminCrudApp')
  .constant('settings', {
    UBIRCH_API_HOST : "http://localhost:8080",
    DEFAULT_DEVICE_TYPE_KEY: "unknownDeviceType"
  });
<commit_msg>prod settings for testing with prod db<commit_after>/**
 * Created by ate on 27.09.16.
 */
angular.module('ubirchAdminCrudApp')
  .constant('settings', {
    UBIRCH_API_HOST : "http://mwc-api.dev.ubirch.com:8080",
    DEFAULT_DEVICE_TYPE_KEY: "unknownDeviceType"
  });
<|endoftext|>


<commit_before>const DISEASE_PANEL_BROWSER_SETTINGS = {
    /**
     *  Full settings
     */
    menu: {
        // merge criterium: internal sections and filters are used to hydrates the external filters list for each section (which is a list of string). Sections and filter order is respected.
        sections: [
            {
                filters: ["id", "disorders", "feature", "region", "categories", "tags", "date"]
            }
        ],
        // merge criterium: full outer join-like. it adds objects presents in internal array only and in external array only. In case of same id, the external value overwrite the internal.
        examples: []
    },
    table: {
        // merge criterium: spread operator
        toolbar: {
            showColumns: true,
            showExport: false,
            showDownload: true
            // columns list for the dropdown will be added in grid components based on settings.table.columns
        },
        // merge criterium: uses this array as filter for internal 1D/2D array. It handles row/col span
        // It is supported either columns[] or hiddenColumns[].
        columns: ["id", "name", "stats", "source"]
    },
    // merge criterium: uses this array as filter for internal 1D array.
    details: ["disease-panel-view", "disease-panel-genes", "disease-panel-regions", "disease-panel-variants", "json-view"]
};

export default DISEASE_PANEL_BROWSER_SETTINGS;

<commit_msg>disease-panel-browser.settings.js changed in default config #430<commit_after>const DISEASE_PANEL_BROWSER_SETTINGS = {
    /**
     *  Full settings
     */
    menu: {
        // merge criterium: internal sections and filters are used to hydrates the external filters list for each section (which is a list of string). Sections and filter order is respected.
        sections: [
            {
                filters: ["id", "disorders", "feature", "region", "categories", "tags", "date"]
            }
        ],
        // merge criterium: full outer join-like. it adds objects presents in internal array only and in external array only. In case of same id, the external value overwrite the internal.
        examples: []
    },
    table: {
        // merge criterium: spread operator
        toolbar: {
            showColumns: true,
            showExport: false,
            showDownload: true
            // columns list for the dropdown will be added in grid components based on settings.table.columns
        },
        // merge criterium: uses this array as filter for internal 1D/2D array. It handles row/col span
        // It is supported either columns[] or hiddenColumns[].
        columns: ["id", "name", "stats", "source"]
    },
    // merge criterium: uses this array as filter for internal 1D array.
    details: ["disease-panel-view", "disease-panel-genes", "disease-panel-regions", "disease-panel-variants", "json-view"]
};

<|endoftext|>


<commit_before>var yalla = (function () {

    "use strict";
    var yalla = {
        utils: {},
        framework: {},
        log: {},
        components: {}
    };

    var log = yalla.log;

    log.debug = function (message) {
        console.log('%c' + message, 'font-size:0.9em;color:#999999;font-family=verdana');
    };

    log.info = function (message) {
        console.log('%c' + message, 'font-size:1.2em;color:#666666;font-family=verdana');
    };

    log.error = function (message) {
        console.log('%c' + message, 'font-size:1.2em;color:red;font-family=verdana');
        showErrorInBrowser(message);
    };

    function showErrorInBrowser(message){
        var errorDiv = document.createElement('div');
        errorDiv.style = 'background:#000;color: red;padding:10px;position:fixed;bottom:0px;right:0px;left:0px;z-index:10000;';
        var deleteButton = document.createElement('button');
        deleteButton.innerText = 'OK';
        deleteButton.style = 'float:right;background-color: #4CAF50; /* Green */ border: none; padding:5px; color: white; text-align: center; text-decoration: none; display: inline-block; font-size: 12px;';
        deleteButton.onclick = function(event){
            event.target.parentNode.remove();
        };
        var messageDiv = document.createElement('div');
        messageDiv.innerText = message;
        messageDiv.style = 'font-size:20px';
        errorDiv.appendChild(deleteButton);
        errorDiv.appendChild(messageDiv);
        document.body.appendChild(errorDiv);
    }

    var utils = yalla.utils;

    utils.nonEmptyArray = function (array) {
        return Array.isArray(array) && array.length > 0;
    };

    utils.firstItemInArray = function (array) {
        if (utils.nonEmptyArray(array)) {
            return array[0];
        }
        return false;
    };

    utils.argumentsToArray = function (array) {
        var result = [];
        for (var arg = 0; arg < array.length; ++arg) {
            var item = array[arg];
            result.push(item);
        }
        return result;
    };

    utils.assertNotNull = function () {
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] == null || arguments[i] == undefined) {
                return false;
            }
        }
        return true;
    };

    utils.fetch = function (url, postData) {

        var XMLHttpFactories = [
            function () {
                return new XMLHttpRequest()
            },
            function () {
                return new ActiveXObject("Msxml2.XMLHTTP")
            },
            function () {
                return new ActiveXObject("Msxml3.XMLHTTP")
            },
            function () {
                return new ActiveXObject("Microsoft.XMLHTTP")
            }
        ];

        function createXMLHTTPObject() {
            var xmlhttp = false;
            for (var i = 0; i < XMLHttpFactories.length; i++) {
                try {
                    xmlhttp = XMLHttpFactories[i]();
                }
                catch (e) {
                    continue;
                }
                break;
            }
            return xmlhttp;
        }

        return new Promise(function (resolve, reject) {
            var req = createXMLHTTPObject();
            req.timeout = 2000;
            if (!req) return;
            var method = (postData) ? "POST" : "GET";
            req.open(method, url, true);
            if (postData) {
                req.setRequestHeader('Content-type', 'application/json');
            }
            req.ontimeout = function (e) {
                reject(req);
            };
            req.onreadystatechange = function () {
                if (req.readyState != 4) return;
                if (req.status != 200 && req.status != 304) {
                    reject(req);
                    return;
                }
                resolve(req);
            };
            if (req.readyState == 4) {
                return;
            }
            req.send(JSON.stringify(postData));
        });
    };

    var framework = yalla.framework;
    framework.filePrefix = '.js';
    framework.base = 'src';
    framework.componentLoadListener = {};
    framework.createInjector = function (componentPath) {
        var relativePath = componentPath.substring(0, componentPath.lastIndexOf("/"));

        function Injector(inject) {
            var path = relativePath + "/" + inject;
            if (inject.charAt(0) == '/') {
                path = framework.composePathFromBase(inject)
            }
            return yalla.components[path];
        }

        return Injector;
    };

    framework.addComponent = function (name, component) {
        yalla.components[name] = component;
        var path = name + framework.filePrefix;
        if (path in framework.componentLoadListener) {
            framework.componentLoadListener[path].call();
        }
    };

    framework.attachScriptToDocument = function (url) {
        var componentPath = url.substring(0, url.length - ".js".length);
        if (componentPath in yalla.components) {
            return Promise.resolve(true);
        }
        if (url in framework.componentLoadListener) {
            return Promise.resolve(true);
        }
        return new Promise(function (resolve) {
            var s = document.createElement('script');
            s.setAttribute("src", '.' + url);
            document.head.appendChild(s);
            framework.componentLoadListener[url] = function () {
                resolve(url);
                delete framework.componentLoadListener[url];
            };
        });
    };

    framework.composePathFromBase = function (component) {
        var fromRoot = (component.charAt(0) == '/');
        return "/" + framework.base + (fromRoot ? '' : '/') + component;
    };

    framework.loadScriptAndDependency = function (component) {
        if(component.indexOf('.')>0){
            log.error('Invalid dependency : '+component);
            return Promise.reject();
        }
        var componentPath = framework.composePathFromBase(component);
        if (componentPath in yalla.components) {
            return Promise.resolve(true);
        }
        var url = componentPath + framework.filePrefix;

        var relativePath = component.substring(0, component.lastIndexOf("/") + 1);
        return new Promise(function (resolve) {
            utils.fetch('.' + url).then(function (req) {
                var injects = (req.responseText.match(/\$inject\(.*?\)/g) || []).map(function (inject) {
                    return inject.substring('$inject("'.length, inject.length - 2);
                });

                if (utils.nonEmptyArray(injects)) {
                    var injectsPromise = injects.map(function (inject) {
                        if (inject.charAt(0) != '/') {
                            inject = "/" + inject;
                        }
                        return framework.loadScriptAndDependency(inject);
                    });
                    Promise.all(injectsPromise).then(function () {
                        framework.attachScriptToDocument(url).then(function () {
                            resolve(url);
                        });
                    });
                } else {
                    framework.attachScriptToDocument(url).then(function () {
                        resolve(url);
                    });
                }
            }, function () {
                log.error('Unable to fetch ' + url);
            });
        });
    };

    framework.getParentComponent = function(node){
        var _node = node;
        do{
            if('element' in _node.attributes || _node.nodeName == 'BODY'){
                return _node;
            }
            _node = _node.parentNode;
        }while(_node)
        return null;
    };

    framework.renderChain = function (address) {
        return address.reduceRight(function (current, path) {
            return current.then(function () {
                return new Promise(function (resolve) {
                    framework.loadScriptAndDependency(path).then(function () {
                        resolve(true);
                    });
                });
            });
        }, Promise.resolve(false));
    };

    framework.start = function () {
        var scripts = document.querySelector("script[src$='yalla.js']") || [];
        if (!utils.assertNotNull(scripts.attributes['yalla-component'], scripts.attributes['yalla-base'], scripts.attributes['yalla-domtarget'])) {
            throw new Error("script tag should contain attributes 'yalla-component', 'yalla-base' and 'yalla-domtarget'");
        }
        var component = scripts.attributes['yalla-component'].nodeValue;
        var base = scripts.attributes['yalla-base'].nodeValue;
        var domTarget = scripts.attributes['yalla-domtarget'].nodeValue;
        var routingCallback = scripts.attributes['yalla-routing'] ? scripts.attributes['yalla-routing'].nodeValue : false;

        framework.base = base;
        framework.domTarget = domTarget;
        framework.defaultComponent = component;
        framework.beforeRenderToScreen = function(){
            return new Promise(function (resolve){
                if(routingCallback && typeof window[routingCallback] == 'function'){
                    var path = window.location.hash;
                    window[routingCallback](path).then(function(newPath){
                        if(newPath && newPath != path){
                            resolve(false);
                            log.info('Re-routing path to new location');
                            window.location.hash = newPath;
                        }else{
                            resolve(true);
                        }
                    });
                }else{
                    resolve(true);
                }
            });
        };
        framework.renderToScreen();
    };

    function patchGlobal() {
        var address = [framework.defaultComponent];
        var addressString = '';
        var googleEscapedFragment = '?_escaped_fragment_=';
        if (window.location.hash != "") {
            addressString = window.location.hash.substring(1, window.location.hash.length);
        }else if(window.location.search.indexOf(googleEscapedFragment) == 0){
            addressString = decodeURIComponent(window.location.search.substring(googleEscapedFragment.length,window.location.search.length));
        }
        if(addressString && addressString.length > 0){
            address = addressString.split("/").map(function(addr){
                if(addr && addr.indexOf('!') == 0 && addr.length > 1){
                    addr = addr.substring(1,addr.length);
                }
                return addr;
            }).filter(function(addr){
                if(addr && addr.length > 0 && addr.indexOf('!') < 0){
                    return true;
                }
                return false;
            });
        }

        var componentAndParams = address.map(function (pathQuery) {
            var valParams = pathQuery.split(':');
            var path = valParams[0].replace(/\./g, '/');
            valParams.splice(0, 1);
            var params = valParams.reduce(function (current, param) {
                var parVal = param.split('=');
                current[parVal[0]] = parVal[1];
                return current;
            }, {});
            return {
                componentPath: path,
                params: params
            }
        });
        var addressToChain = componentAndParams.map(function (item) {
            return item.componentPath;
        });

        framework.renderChain(addressToChain).then(function () {
            var render = componentAndParams.reduceRight(function (slotView, compAndParam) {
                var component = compAndParam;
                var path = framework.composePathFromBase(component.componentPath);
                var comp = yalla.components[path];
                return function (slotName) {
                    if (undefined == slotName || slotName == 'default') {
                        comp.render(component.params, slotView);
                    }
                }
            }, function () {
            });
            IncrementalDOM.patch(document.querySelector(framework.domTarget), function () {
                render();
            });
        }).catch(function (err) {
            log.error(err.stack);
        });
    }

    framework.renderToScreen = function () {
        var args = arguments;
        framework.beforeRenderToScreen().then(function (ok) {
            if(!ok){
                return;
            }
            if (args.length == 2) {
                IncrementalDOM.patch(args[0], args[1]);
            } else {
                patchGlobal();
            }
        });
    };

    framework.beforeRenderToScreen = function () {
        return Promise.resolve(true);
    };

    var attributes = IncrementalDOM.attributes;
    // html5 boolean attributes
    /*
     checked             (input type=checkbox/radio)
     selected            (option)
     disabled            (input, textarea, button, select, option, optgroup)
     readonly            (input type=text/password, textarea)
     multiple            (select)
     ismap     isMap     (img, input type=image)

     defer               (script)
     declare             (object; never used)
     noresize  noResize  (frame)
     nowrap    noWrap    (td, th; deprecated)
     noshade   noShade   (hr; deprecated)
     compact             (ul, ol, dl, menu, dir; deprecated)
     */
    ['checked','selected','disabled','readonly','required','multiple','ismap'].forEach(function(key){
        attributes[key] = function (element, name, value) {
            if (value) {
                element.setAttribute(key, true);
            } else {
                element.removeAttribute(key);
            }
        };
    });

    // BUG Fix for the attributes.value not updating in IncrementalDOM
    attributes.value = function(element,name,value){
        element.value = value;
    };

    IncrementalDOM.notifications.nodesCreated = function (nodes) {
        nodes.forEach(function (node) {
            if (node.oncreated) {
                node.oncreated.call(node, node)
            }
        });
    };

    IncrementalDOM.notifications.nodesDeleted = function (nodes) {
        nodes.forEach(function (node) {
            if (node.ondeleted) {
                node.ondeleted.call(node, node);
            }
        });
    };

    return yalla;
})();

window.onload = function () {
    yalla.framework.start();
};

if ("onhashchange" in window) {
    window.onhashchange = function () {
        yalla.framework.renderToScreen();
    }
} else {
    alert('Browser not supported');
}<commit_msg>Enrich this object in .trigger. Todo for .bind and expression<commit_after>var yalla = (function () {

    "use strict";
    var yalla = {
        utils: {},
        framework: {},
        log: {},
        components: {}
    };

    var log = yalla.log;

    log.debug = function (message) {
        console.log('%c' + message, 'font-size:0.9em;color:#999999;font-family=verdana');
    };

    log.info = function (message) {
        console.log('%c' + message, 'font-size:1.2em;color:#666666;font-family=verdana');
    };

    log.error = function (message) {
        console.log('%c' + message, 'font-size:1.2em;color:red;font-family=verdana');
        showErrorInBrowser(message);
    };

    function showErrorInBrowser(message){
        var errorDiv = document.createElement('div');
        errorDiv.style = 'background:#000;color: red;padding:10px;position:fixed;bottom:0px;right:0px;left:0px;z-index:10000;';
        var deleteButton = document.createElement('button');
        deleteButton.innerText = 'OK';
        deleteButton.style = 'float:right;background-color: #4CAF50; /* Green */ border: none; padding:5px; color: white; text-align: center; text-decoration: none; display: inline-block; font-size: 12px;';
        deleteButton.onclick = function(event){
            event.target.parentNode.remove();
        };
        var messageDiv = document.createElement('div');
        messageDiv.innerText = message;
        messageDiv.style = 'font-size:20px';
        errorDiv.appendChild(deleteButton);
        errorDiv.appendChild(messageDiv);
        document.body.appendChild(errorDiv);
    }

    var utils = yalla.utils;

    utils.nonEmptyArray = function (array) {
        return Array.isArray(array) && array.length > 0;
    };

    utils.firstItemInArray = function (array) {
        if (utils.nonEmptyArray(array)) {
            return array[0];
        }
        return false;
    };

    utils.argumentsToArray = function (array) {
        var result = [];
        for (var arg = 0; arg < array.length; ++arg) {
            var item = array[arg];
            result.push(item);
        }
        return result;
    };

    utils.assertNotNull = function () {
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] == null || arguments[i] == undefined) {
                return false;
            }
        }
        return true;
    };

    utils.fetch = function (url, postData) {

        var XMLHttpFactories = [
            function () {
                return new XMLHttpRequest()
            },
            function () {
                return new ActiveXObject("Msxml2.XMLHTTP")
            },
            function () {
                return new ActiveXObject("Msxml3.XMLHTTP")
            },
            function () {
                return new ActiveXObject("Microsoft.XMLHTTP")
            }
        ];

        function createXMLHTTPObject() {
            var xmlhttp = false;
            for (var i = 0; i < XMLHttpFactories.length; i++) {
                try {
                    xmlhttp = XMLHttpFactories[i]();
                }
                catch (e) {
                    continue;
                }
                break;
            }
            return xmlhttp;
        }

        return new Promise(function (resolve, reject) {
            var req = createXMLHTTPObject();
            req.timeout = 2000;
            if (!req) return;
            var method = (postData) ? "POST" : "GET";
            req.open(method, url, true);
            if (postData) {
                req.setRequestHeader('Content-type', 'application/json');
            }
            req.ontimeout = function (e) {
                reject(req);
            };
            req.onreadystatechange = function () {
                if (req.readyState != 4) return;
                if (req.status != 200 && req.status != 304) {
                    reject(req);
                    return;
                }
                resolve(req);
            };
            if (req.readyState == 4) {
                return;
            }
            req.send(JSON.stringify(postData));
        });
    };

    var framework = yalla.framework;
    framework.filePrefix = '.js';
    framework.base = 'src';
    framework.componentLoadListener = {};
    framework.createInjector = function (componentPath) {
        var relativePath = componentPath.substring(0, componentPath.lastIndexOf("/"));

        function Injector(inject) {
            var path = relativePath + "/" + inject;
            if (inject.charAt(0) == '/') {
                path = framework.composePathFromBase(inject)
            }
            return yalla.components[path];
        }

        return Injector;
    };

    framework.addComponent = function (name, component) {
        yalla.components[name] = component;
        var path = name + framework.filePrefix;
        if (path in framework.componentLoadListener) {
            framework.componentLoadListener[path].call();
        }
    };

    framework.attachScriptToDocument = function (url) {
        var componentPath = url.substring(0, url.length - ".js".length);
        if (componentPath in yalla.components) {
            return Promise.resolve(true);
        }
        if (url in framework.componentLoadListener) {
            return Promise.resolve(true);
        }
        return new Promise(function (resolve) {
            var s = document.createElement('script');
            s.setAttribute("src", '.' + url);
            document.head.appendChild(s);
            framework.componentLoadListener[url] = function () {
                resolve(url);
                delete framework.componentLoadListener[url];
            };
        });
    };

    framework.composePathFromBase = function (component) {
        var fromRoot = (component.charAt(0) == '/');
        return "/" + framework.base + (fromRoot ? '' : '/') + component;
    };

    framework.loadScriptAndDependency = function (component) {
        if(component.indexOf('.')>0){
            log.error('Invalid dependency : '+component);
            return Promise.reject();
        }
        var componentPath = framework.composePathFromBase(component);
        if (componentPath in yalla.components) {
            return Promise.resolve(true);
        }
        var url = componentPath + framework.filePrefix;

        var relativePath = component.substring(0, component.lastIndexOf("/") + 1);
        return new Promise(function (resolve) {
            utils.fetch('.' + url).then(function (req) {
                var injects = (req.responseText.match(/\$inject\(.*?\)/g) || []).map(function (inject) {
                    return inject.substring('$inject("'.length, inject.length - 2);
                });

                if (utils.nonEmptyArray(injects)) {
                    var injectsPromise = injects.map(function (inject) {
                        if (inject.charAt(0) != '/') {
                            inject = "/" + inject;
                        }
                        return framework.loadScriptAndDependency(inject);
                    });
                    Promise.all(injectsPromise).then(function () {
                        framework.attachScriptToDocument(url).then(function () {
                            resolve(url);
                        });
                    });
                } else {
                    framework.attachScriptToDocument(url).then(function () {
                        resolve(url);
                    });
                }
            }, function () {
                log.error('Unable to fetch ' + url);
            });
        });
    };

    framework.getParentComponent = function(node){
        var _node = node;
        do{
            if('element' in _node.attributes || _node.nodeName == 'BODY'){
                return _node;
            }
            _node = _node.parentNode;
        }while(_node);
        return null;
    };

    framework.renderChain = function (address) {
        return address.reduceRight(function (current, path) {
            return current.then(function () {
                return new Promise(function (resolve) {
                    framework.loadScriptAndDependency(path).then(function () {
                        resolve(true);
                    });
                });
            });
        }, Promise.resolve(false));
    };

    framework.start = function () {
        var scripts = document.querySelector("script[src$='yalla.js']") || [];
        if (!utils.assertNotNull(scripts.attributes['yalla-component'], scripts.attributes['yalla-base'], scripts.attributes['yalla-domtarget'])) {
            throw new Error("script tag should contain attributes 'yalla-component', 'yalla-base' and 'yalla-domtarget'");
        }
        var component = scripts.attributes['yalla-component'].nodeValue;
        var base = scripts.attributes['yalla-base'].nodeValue;
        var domTarget = scripts.attributes['yalla-domtarget'].nodeValue;
        var routingCallback = scripts.attributes['yalla-routing'] ? scripts.attributes['yalla-routing'].nodeValue : false;

        framework.base = base;
        framework.domTarget = domTarget;
        framework.defaultComponent = component;
        framework.beforeRenderToScreen = function(){
            return new Promise(function (resolve){
                if(routingCallback && typeof window[routingCallback] == 'function'){
                    var path = window.location.hash;
                    window[routingCallback](path).then(function(newPath){
                        if(newPath && newPath != path){
                            resolve(false);
                            log.info('Re-routing path to new location');
                            window.location.hash = newPath;
                        }else{
                            resolve(true);
                        }
                    });
                }else{
                    resolve(true);
                }
            });
        };
        framework.renderToScreen();
    };

    function patchGlobal() {
        var address = [framework.defaultComponent];
        var addressString = '';
        var googleEscapedFragment = '?_escaped_fragment_=';
        if (window.location.hash != "") {
            addressString = window.location.hash.substring(1, window.location.hash.length);
        }else if(window.location.search.indexOf(googleEscapedFragment) == 0){
            addressString = decodeURIComponent(window.location.search.substring(googleEscapedFragment.length,window.location.search.length));
        }
        if(addressString && addressString.length > 0){
            address = addressString.split("/").map(function(addr){
                if(addr && addr.indexOf('!') == 0 && addr.length > 1){
                    addr = addr.substring(1,addr.length);
                }
                return addr;
            }).filter(function(addr){
                if(addr && addr.length > 0 && addr.indexOf('!') < 0){
                    return true;
                }
                return false;
            });
        }

        var componentAndParams = address.map(function (pathQuery) {
            var valParams = pathQuery.split(':');
            var path = valParams[0].replace(/\./g, '/');
            valParams.splice(0, 1);
            var params = valParams.reduce(function (current, param) {
                var parVal = param.split('=');
                current[parVal[0]] = parVal[1];
                return current;
            }, {});
            return {
                componentPath: path,
                params: params
            }
        });
        var addressToChain = componentAndParams.map(function (item) {
            return item.componentPath;
        });

        framework.renderChain(addressToChain).then(function () {
            var render = componentAndParams.reduceRight(function (slotView, compAndParam) {
                var component = compAndParam;
                var path = framework.composePathFromBase(component.componentPath);
                var comp = yalla.components[path];
                return function (slotName) {
                    if (undefined == slotName || slotName == 'default') {
                        comp.render(component.params, slotView);
                    }
                }
            }, function () {
            });
            IncrementalDOM.patch(document.querySelector(framework.domTarget), function () {
                render();
            });
        }).catch(function (err) {
            log.error(err.stack);
        });
    }

    framework.renderToScreen = function () {
        var args = arguments;
        framework.beforeRenderToScreen().then(function (ok) {
            if(!ok){
                return;
            }
            if (args.length == 2) {
                IncrementalDOM.patch(args[0], args[1]);
            } else {
                patchGlobal();
            }
        });
    };

    framework.beforeRenderToScreen = function () {
        return Promise.resolve(true);
    };

    var attributes = IncrementalDOM.attributes;
    // html5 boolean attributes
    /*
     checked             (input type=checkbox/radio)
     selected            (option)
     disabled            (input, textarea, button, select, option, optgroup)
     readonly            (input type=text/password, textarea)
     multiple            (select)
     ismap     isMap     (img, input type=image)

     defer               (script)
     declare             (object; never used)
     noresize  noResize  (frame)
     nowrap    noWrap    (td, th; deprecated)
     noshade   noShade   (hr; deprecated)
     compact             (ul, ol, dl, menu, dir; deprecated)
     */
    ['checked','selected','disabled','readonly','required','multiple','ismap'].forEach(function(key){
        attributes[key] = function (element, name, value) {
            if (value) {
                element.setAttribute(key, true);
            } else {
                element.removeAttribute(key);
            }
        };
    });

    // BUG Fix for the attributes.value not updating in IncrementalDOM
    attributes.value = function(element,name,value){
        element.value = value;
    };

    IncrementalDOM.notifications.nodesCreated = function (nodes) {
        nodes.forEach(function (node) {
            if (node.oncreated) {
                node.oncreated.call(node, node)
            }
        });
    };

    IncrementalDOM.notifications.nodesDeleted = function (nodes) {
        nodes.forEach(function (node) {
            if (node.ondeleted) {
                node.ondeleted.call(node, node);
            }
        });
    };

    return yalla;
})();

window.onload = function () {
    yalla.framework.start();
};

if ("onhashchange" in window) {
    window.onhashchange = function () {
        yalla.framework.renderToScreen();
    }
} else {
    alert('Browser not supported');
}<|endoftext|>


<commit_before>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// TO DO: Use a single Query for the following 3 date queries
// selects all years for crash data, used by MonthYearSelector.js component
export const crashesYearRangeSQL = () => sls`
  SELECT DISTINCT year
  FROM ${nyc_crashes}
  ORDER BY year DESC
`;

// selects min and max year-month formatted like "YYYY-MM"
// used by MonthYearSelector.js component
export const minMaxDateRange = () => sls`
  SELECT
  min(year::text || '-' || LPAD(month::text, 2, '0')),
  max(year::text || '-' || LPAD(month::text, 2, '0'))
  FROM ${nyc_crashes}
`;

// selects max date, used for DownloadData.js component's "last updated on" msg
export const crashesMaxDate = () => sls`
  SELECT max(date_val) as max_date
  FROM ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Date Range"
// @param {object} startDate, a moment.js object
// @param {object} endDate, a moment.js object
const filterByDateWhereClause = (startDate, endDate) =>
  sls`
      (
        year::text || LPAD(month::text, 2, '0') <=
        '${endDate.year()}' || LPAD(${endDate.month() + 1}::text, 2, '0')
      )
    AND
      (
        year::text || LPAD(month::text, 2, '0') >=
        '${startDate.year()}' || LPAD(${startDate.month() + 1}::text, 2, '0')
      )
  `;

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
export const filterAreaBtnTableMap = {
  Borough: undefined,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// name mappings for Carto table nyc_borough "identifier" to borough name
const boroughs = ['manhattan', 'bronx', 'brooklyn', 'queens', 'staten island'];

// Creates the WHERE clause for boundary table identifier
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
// @param {number || string} identifier, unique id of boundary polygon
// @param {string} geo, name of boundary table identifier column belongs to
const filterByIdentifierWhereClause = (identifier, geo) => {
  if (geo !== 'Borough' && identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  } else if (geo === 'Borough' && identifier) {
    return `AND c.borough ilike '%${boroughs[identifier - 1]}%'`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT * FROM
    (
      SELECT
        c.the_geom as the_geom,
        c.the_geom_webmercator as the_geom_webmercator,
        c.on_street_name as on_street_name,
        c.cross_street_name as cross_street_name,
        COUNT(c.crash_count) as total_crashes,
        SUM(c.number_of_cyclist_injured) as cyclist_injured,
        SUM(c.number_of_cyclist_killed) as cyclist_killed,
        SUM(c.number_of_motorist_injured) as motorist_injured,
        SUM(c.number_of_motorist_killed) as motorist_killed,
        SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
        SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
        SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
        SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
      AND
        c.the_geom IS NOT NULL
      GROUP BY
        c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
    ) _
    ORDER BY
    CASE WHEN (persons_killed > 0) THEN 3
    WHEN (persons_injured > 0) THEN 2
    WHEN (total_crashes > 11) THEN 1
    ELSE 0
    END
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        unnest(c.contributing_factor) as factor
      FROM
      ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.socrata_id,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date_time,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.crash_count,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      array_to_string(c.contributing_factor, ',') as contributing_factors,
      array_to_string(c.vehicle_type, ',') as vehicle_types
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};
<commit_msg>filter by area sql query includes the_geom for geojson response #52<commit_after>import sls from 'single-line-string';

import { cartoTables } from './app_config';

const { nyc_borough,
  nyc_city_council,
  nyc_community_board,
  nyc_neighborhood,
  nyc_nypd_precinct,
  nyc_zip_code,
  nyc_crashes } = cartoTables;

// Links each boundary filter name to a SQL query
export const filterByAreaSQL = {
  Borough: sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom,
      the_geom_webmercator
    FROM
      ${nyc_borough}
    ORDER BY
      identifier
  `,

  'City Council District': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_city_council}
    ORDER BY
      identifier
  `,

  'Community Board': sls`
    SELECT DISTINCT
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_community_board}
    ORDER BY
      identifier
  `,

  'Neighborhood (NTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_neighborhood}
    ORDER BY
      borough, identifier
  `,

  'NYPD Precinct': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_nypd_precinct}
    ORDER BY
      borough, identifier
  `,

  'Zipcode (ZCTA)': sls`
    SELECT DISTINCT
      borough,
      identifier,
      cartodb_id,
      the_geom_webmercator
    FROM
      ${nyc_zip_code}
    ORDER BY
      borough, identifier
  `,
};

// TO DO: Use a single Query for the following 3 date queries
// selects all years for crash data, used by MonthYearSelector.js component
export const crashesYearRangeSQL = () => sls`
  SELECT DISTINCT year
  FROM ${nyc_crashes}
  ORDER BY year DESC
`;

// selects min and max year-month formatted like "YYYY-MM"
// used by MonthYearSelector.js component
export const minMaxDateRange = () => sls`
  SELECT
  min(year::text || '-' || LPAD(month::text, 2, '0')),
  max(year::text || '-' || LPAD(month::text, 2, '0'))
  FROM ${nyc_crashes}
`;

// selects max date, used for DownloadData.js component's "last updated on" msg
export const crashesMaxDate = () => sls`
  SELECT max(date_val) as max_date
  FROM ${nyc_crashes}
`;

/*
***************************** SQL HELPERS **************************************
*/

// Generates the SQL WHERE clause for "Filter by Date Range"
// @param {object} startDate, a moment.js object
// @param {object} endDate, a moment.js object
const filterByDateWhereClause = (startDate, endDate) =>
  sls`
      (
        year::text || LPAD(month::text, 2, '0') <=
        '${endDate.year()}' || LPAD(${endDate.month() + 1}::text, 2, '0')
      )
    AND
      (
        year::text || LPAD(month::text, 2, '0') >=
        '${startDate.year()}' || LPAD(${startDate.month() + 1}::text, 2, '0')
      )
  `;

// Generates the SQL WHERE clause for "Filter by Type"
// @param {object} the store.filterType piece of state
const filterByTypeWhereClause = (filterType) => {
  const { injury, fatality, noInjuryFatality } = filterType;
  let whereClause = '';

  const mapTypes = (personTypes, harmType) =>
    Object.keys(personTypes).filter((type) => {
      const val = personTypes[type];
      if (val) return type;
      return false;
    })
    .map((type) => {
      const hurtTerm = harmType === 'injury' ? 'injured' : 'killed';
      return ` number_of_${type}_${hurtTerm} > 0 `;
    })
    .join('OR');

  const typesInjuredMapped = mapTypes(injury, 'injury');
  const typesKilledMapped = mapTypes(fatality, 'fatality');

  if (typesInjuredMapped.length > 0 && typesKilledMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped} OR ${typesKilledMapped})`;
  } else if (typesInjuredMapped.length > 0) {
    whereClause += `AND (${typesInjuredMapped})`;
  } else if (typesKilledMapped.length > 0) {
    whereClause += `AND (${typesKilledMapped})`;
  } else if (noInjuryFatality) {
    whereClause += sls`AND
      number_of_cyclist_injured = 0 AND
      number_of_cyclist_killed = 0 AND
      number_of_motorist_injured = 0 AND
      number_of_motorist_killed = 0 AND
      number_of_pedestrian_injured = 0 AND
      number_of_pedestrian_killed = 0 AND
      number_of_persons_injured = 0 AND
      number_of_persons_killed = 0
    `;
  }

  return whereClause;
};

// Links the Filter by Boundary button name to corresponding Carto table name
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
export const filterAreaBtnTableMap = {
  Borough: undefined,
  'Community Board': nyc_community_board,
  'City Council District': nyc_city_council,
  'Neighborhood (NTA)': nyc_neighborhood,
  'NYPD Precinct': nyc_nypd_precinct,
  'Zipcode (ZCTA)': nyc_zip_code,
};

// Creates the spatial join clause with a boundary table geom
// @param {string} areaName, name of boundary, eg 'Borough' or 'City Council Districts'
const joinToGeoTableClause = (areaName) => {
  const geoTable = filterAreaBtnTableMap[areaName];
  if (geoTable) {
    return sls`
      JOIN ${geoTable} a
      ON ST_Within(c.the_geom, a.the_geom)
    `;
  }
  return '';
};

// name mappings for Carto table nyc_borough "identifier" to borough name
const boroughs = ['manhattan', 'bronx', 'brooklyn', 'queens', 'staten island'];

// Creates the WHERE clause for boundary table identifier
// NOTE: Deliberately not using Borough, because when > 1 year of data is selected
// the spatial join will time out on Borough polygons
// @param {number || string} identifier, unique id of boundary polygon
// @param {string} geo, name of boundary table identifier column belongs to
const filterByIdentifierWhereClause = (identifier, geo) => {
  if (geo !== 'Borough' && identifier) {
    return `AND a.identifier = $$${identifier}$$`;
  } else if (geo === 'Borough' && identifier) {
    return `AND c.borough ilike '%${boroughs[identifier - 1]}%'`;
  }
  return '';
};

// Creates the PostGIS query for selecting crash data by custom area created by Leaflet.Draw
// @param {array} lonLatArray, an array of longitude, latitude arrays that form an enclosed polygon
const filterByCustomAreaClause = (lonLatArray) => {
  if (lonLatArray && lonLatArray.length) {
    // PostGIS GeomFromText expects lon lat coords like (-73.91 40.74, -73.89 40.73, ...)
    const coordinates = lonLatArray.map(lonLat => lonLat.join(' '));
    return sls`
      AND
        ST_Contains(
          ST_GeomFromText(
            'POLYGON(( ${coordinates} ))',
          4326),
          c.the_geom
        )
    `;
  }
  return '';
};

/*
 ********************************** MAP ****************************************
 */

// Generates the SQL query for the Carto layer based on filter params & app element
// @param {object} params: key values associated with filters derived from app state
// @param {string} startDate: min date; required
// @param {string} endDate: max date; required
// @param {string} harm: crash type, one of 'ALL', 'cyclist', 'motorist', 'ped'
// @param {string} persona: crash type, of of 'ALL', 'fatality', 'injury', 'no inj/fat'
export const configureMapSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT * FROM
    (
      SELECT
        c.the_geom as the_geom,
        c.the_geom_webmercator as the_geom_webmercator,
        c.on_street_name as on_street_name,
        c.cross_street_name as cross_street_name,
        COUNT(c.crash_count) as total_crashes,
        SUM(c.number_of_cyclist_injured) as cyclist_injured,
        SUM(c.number_of_cyclist_killed) as cyclist_killed,
        SUM(c.number_of_motorist_injured) as motorist_injured,
        SUM(c.number_of_motorist_killed) as motorist_killed,
        SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
        SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
        SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
        SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
      FROM
        ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
      AND
        c.the_geom IS NOT NULL
      GROUP BY
        c.the_geom, c.the_geom_webmercator, c.on_street_name, c.cross_street_name
    ) _
    ORDER BY
    CASE WHEN (persons_killed > 0) THEN 3
    WHEN (persons_injured > 0) THEN 2
    WHEN (total_crashes > 11) THEN 1
    ELSE 0
    END
  `;
};

/*
 ******************************* STATS *****************************************
 */

export const configureStatsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    SELECT
      COUNT(c.cartodb_id) as total_crashes,
      SUM(c.number_of_cyclist_injured) as cyclist_injured,
      SUM(c.number_of_cyclist_killed) as cyclist_killed,
      SUM(c.number_of_motorist_injured) as motorist_injured,
      SUM(c.number_of_motorist_killed) as motorist_killed,
      SUM(c.number_of_pedestrian_injured) as pedestrian_injured,
      SUM(c.number_of_pedestrian_killed) as pedestrian_killed,
      SUM(c.number_of_pedestrian_injured + c.number_of_cyclist_injured + c.number_of_motorist_injured) as persons_injured,
      SUM(c.number_of_pedestrian_killed + c.number_of_cyclist_killed + c.number_of_motorist_killed) as persons_killed
    FROM
      ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};

/*
 *************************** CONTRIBUTING FACTORS ******************************
*/

export const configureFactorsSQL = (params) => {
  const { startDate, endDate, filterType, geo, identifier, lngLats } = params;

  return sls`
    WITH all_factors as (
      SELECT
        unnest(c.contributing_factor) as factor
      FROM
      ${nyc_crashes} c
      ${joinToGeoTableClause(geo)}
      WHERE
      ${filterByDateWhereClause(startDate, endDate)}
      ${filterByCustomAreaClause(lngLats)}
      ${filterByTypeWhereClause(filterType)}
      ${filterByIdentifierWhereClause(identifier, geo)}
    )
    SELECT
     COUNT(af.factor) as count_factor,
     af.factor
    FROM
      all_factors af
    GROUP BY
      af.factor
    ORDER BY
      count_factor desc
  `;
};

/*
 ******************************* Download Data *********************************
*/

// Creates the SQL query for "Download Data" buttons
export const configureDownloadDataSQL = (params) => {
  const { startDate, endDate, filterArea, filterType } = params;
  const { geo, lngLats, identifier } = filterArea;

  return sls`
    SELECT
      c.cartodb_id,
      c.socrata_id,
      c.the_geom,
      c.on_street_name,
      c.cross_street_name,
      c.date_val AS date_time,
      c.latitude,
      c.longitude,
      c.borough,
      c.zip_code,
      c.crash_count,
      c.number_of_cyclist_injured,
      c.number_of_cyclist_killed,
      c.number_of_motorist_injured,
      c.number_of_motorist_killed,
      c.number_of_pedestrian_injured,
      c.number_of_pedestrian_killed,
      c.number_of_persons_injured,
      c.number_of_persons_killed,
      array_to_string(c.contributing_factor, ',') as contributing_factors,
      array_to_string(c.vehicle_type, ',') as vehicle_types
    FROM ${nyc_crashes} c
    ${joinToGeoTableClause(geo)}
    WHERE
    ${filterByDateWhereClause(startDate, endDate)}
    ${filterByCustomAreaClause(lngLats)}
    ${filterByTypeWhereClause(filterType)}
    ${filterByIdentifierWhereClause(identifier, geo)}
  `;
};
<|endoftext|>