<commit_before>var remoteServer = "//higlass.io/api/v1";
var localServer = "/api/v1";
//var usedServer = localServer;
var usedServer = remoteServer;

export const testViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
    usedServer
  ],
  "exportViewUrl": "//higlass.io/api/v1/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
300000000
      ],
      "autocompleteSource": "//higlass.io/api/v1/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": usedServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          }
        /*
        ,
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
          */
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "horizontal-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": usedServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "vertical-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": usedServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ,
              {
                "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
                "type": "2d-chromosome-labels",
                "position": "center",
                "name": "Chromosome Labels (hg19)"
              }
            ,
              {
                "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
                "type": "2d-chromosome-grid",
                "position": "center",
                "name": "Chromosome Grid (hg19)"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const deployViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      remoteServer
  ],
  "exportViewUrl": "/api/v1/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": "//higlass.io/api/v1/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": remoteServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          },
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": remoteServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": remoteServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const localViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      localServer
  ],
  "exportViewUrl": localServer + "/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": localServer + "/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": localServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          },
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": localServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": localServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const defaultViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      "/api/v1"
  ],
  "exportViewUrl": localServer + "/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": "/api/v1/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "OHJakQICQD6gTD7skx4EWA",
            "server": "/api/v1",
            "position": "top",
            "uid": "OHJakQICQD6gTD7skx4EWA",
            "name": "Gene Annotations",
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "horizontal-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "OHJakQICQD6gTD7skx4EWA",
            "server": "/api/v1",
            "position": "left",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "vertical-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "server": "/api/v1",
                "tilesetUid": "CQMd6V_cRw6iCI_-Unl3PQ",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                }
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};
<commit_msg>Use the new gene annotations uid for the suggestions<commit_after>var remoteServer = "//higlass.io/api/v1";
var localServer = "/api/v1";
//var usedServer = localServer;
var usedServer = remoteServer;

export const testViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
    usedServer
  ],
  "exportViewUrl": "//higlass.io/api/v1/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
300000000
      ],
      "autocompleteSource": "//higlass.io/api/v1/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": usedServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          }
        /*
        ,
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
          */
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "horizontal-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": usedServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "vertical-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": usedServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ,
              {
                "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
                "type": "2d-chromosome-labels",
                "position": "center",
                "name": "Chromosome Labels (hg19)"
              }
            ,
              {
                "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
                "type": "2d-chromosome-grid",
                "position": "center",
                "name": "Chromosome Grid (hg19)"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const deployViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      remoteServer
  ],
  "exportViewUrl": "/api/v1/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": "//higlass.io/api/v1/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": remoteServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          },
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": remoteServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": remoteServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const localViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      localServer
  ],
  "exportViewUrl": localServer + "/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": localServer + "/suggest/?d=dd&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "dd",
            "server": localServer,
            "position": "top",
            "uid": "D00ffLKHSGqwp4r64sSU7A",
            "name": "Gene Annotations",
          },
          {
            "type": "top-axis",
            "height": 60,
            "position": "top",
            "name": "Top Axis",
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "dd",
            "server": localServer,
            "position": "left",
            "uid": "SVYNEiMITAe_K60zCH_7vQ",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "uid": "hm1",
                "server": localServer,
                "tilesetUid": "aa",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                },
                "name": "Dixon2015-H1_TB-HindIII-allreps-filtered.10kb.multires.cool"
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};

export const defaultViewConfig = {
  "editable": true,
  "zoomFixed": false,
  "trackSourceServers": [
      "/api/v1"
  ],
  "exportViewUrl": localServer + "/viewconfs/",
  "views": [
    {
      "uid": "aa",
      "initialXDomain": [
        0,
        3100000000
      ],
      "autocompleteSource": "/api/v1/suggest/?d=OHJakQICQD6gTD7skx4EWA&",
      "genomePositionSearchBoxVisible": true,
      "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
      "tracks": {
        "top": [
          {
            "type": "horizontal-gene-annotations",
            "height": 60,
            "tilesetUid": "OHJakQICQD6gTD7skx4EWA",
            "server": "/api/v1",
            "position": "top",
            "uid": "OHJakQICQD6gTD7skx4EWA",
            "name": "Gene Annotations",
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "horizontal-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "left": [
          {
            "type": "vertical-gene-annotations",
            "width": 60,
            "tilesetUid": "OHJakQICQD6gTD7skx4EWA",
            "server": "/api/v1",
            "position": "left",
            "name": "Gene Annotations",
            "options": {
                "labelPosition": "bottomRight"
            }
          }
            ,
          {
            "chromInfoPath": "//s3.amazonaws.com/pkerp/data/hg19/chromSizes.tsv",
            "type": "vertical-chromosome-labels",
            "position": "top",
            "name": "Chromosome Labels (hg19)"
          }
        ],
        "center": [
          {
            "uid": "c1",
            "type": "combined",
            "height": 200,
            "contents": [
              {
                "server": "/api/v1",
                "tilesetUid": "CQMd6V_cRw6iCI_-Unl3PQ",
                "type": "heatmap",
                "position": "center",
                "options": {
                  "colorRange": [
                    "#FFFFFF",
                    "#F8E71C",
                    "#F5A623",
                    "#D0021B"
                  ],
                  "maxZoom": null
                }
              }
            ],
            "position": "center"
          }
        ],
        "right": [],
        "bottom": []
      }
    }
  ],
  "zoomLocks": {
    "locksByViewUid": {},
    "zoomLocksDict": {}
  }
};
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

var favController = require('../controllers/favController');

router.get('/', favController.getGroupFavs);

router.post('/', favController.addGroupFav);

router.post('/user', favController.addUserFav);

router.delete('/', favController.removeGroupFav);

router.delete('/user', favController.removeUserFav);

module.exports = router;<commit_msg>(fix) leftover code from rebase<commit_after><|endoftext|>


<commit_before>var async = require('async');
var Blog = require('blog');
var url_parser = require('url').parse;
var dns = require('dns');
var BLOT_IP = '54.191.179.131';

var RESULT;

function capitalize (str) {
  return str[0].toUpperCase() + str.slice(1);
}

// Add new sites at the start

var sites = [

  ["www.matthewbattles.org", "Matthew Battles directs metaLAB at Harvard. Matthew’s website"],
  ["hannautkin.com", "Hanna Utkin is a filmmaker and producer from New York City. Hanna’s website"],
  ["sportsmanfc.com", "The Sportsman Flying Club is a pigeon‐racing club in Toft Hill, UK. Their website"],

  ["asw.fyi", "Adam Wood is a writer based in Reykjavík. Adam’s website"],
  ["fagerheimen.no", "Fagerheimen Borettslag is an apartment building in Oslo, Norway. Its website"],
  ["www.rebeccatron.com", "Rebecca Kukshtel is an engineer at Betterment in New York City. Rebecca "],

  ["www.jacobyyoung.com/about", "Jacoby Young works at Kapālama Elementary School in Hawaii. Jacoby"],
  ["www.kristianhjelle.com", "Kristian Hjelle is a designer and partner at Bakken & Bæck. Kristian"],
  ["www.alexgibson.nyc", "Alexander Gibson is an artist from New York City. Alexander’s website"],

  ["tf2.blot.im", "Theo Francis is a reporter at The Wall Street Journal. Theo’s blog"],
  ["john.pavlusoffice.com", "John Pavlus is a nonfiction writer and filmmaker based in Portland, Oregon. John"],
  ["www.theliminal.co", "The Liminal is a podcast about metaphysical speculation. Their website"],

  ["blog.aurynn.com", "Aurynn Shaw is a writer and engineer from New Zealand. Aurynn’s site"],
  ["iiiiiiiii.in", "Ishtaarth Dalmia writes about internet culture and economics from Bengaluru, India. Ishtaarth"],
  ["kiefer.design", "Kiefer Sutherland is a freelance designer and art director. Kiefer’s website"],
  ["www.failuretolerated.com", "Sean McCoy is a game designer from Dallas, Texas. Sean’s site"],
  ["mrfris.by", "Stuart Frisby is the Director of Design at Booking.com. Stuart’s site"],
  ["www.querlin.com", "Querlin Ricci is a designer in Atlanta. Querlin’s website"],
];

module.exports = function (callback) {

  if (RESULT) return callback(null, RESULT);

  var result = [];

  // Only show the first 9 sites on the homepage
  sites = sites.slice(0, 12);

  async.eachSeries(sites, function(site, next){

    var parsed_URL = url_parser('https://' + site[0]);

    var domain = parsed_URL.host;
    var description = site[1].trim();
    var template;
    var is_subdomain = domain.indexOf('blot.im') !== -1;
    var url, display_url, protocol, by, custom_template, template_name;

    dns.lookup(domain, function(err, res) {

      if (!is_subdomain && res !== BLOT_IP) {
        console.log(domain, 'is not on blot it points to', res);
        return next();
      }

      if (is_subdomain) {
        by = {handle: domain.split('.blot.im').join('')};
      } else {
        by = {domain: domain};
      }

      Blog.get(by, function(err, blog){

        if (err || !blog) {
          console.log(domain, 'is not in the database');
          return next();
        }

        template = blog.template;

        protocol = domain.indexOf('blot.im') > -1 ? 'http' : 'https';

        url = protocol + '://' + site[0];
        display_url = domain.split('www.').join('');
        custom_template = template.indexOf('SITE') === -1;
        template_name = custom_template ? 'custom' : capitalize(template.split('SITE:').join(''));

        description += ' uses ';
        description += custom_template ? 'a' : 'the';
        description += ' ' + template_name + ' template.';

        // Redirect
        if (display_url === 'john.pavlusoffice.com') display_url = 'johnpavlus.com';

        result.push({
          url: url,
          first: result.length === 0,
          clear: (result.length + 1) % 3 === 0,
          display_url: display_url,
          description: description,
          domain: domain,
          template: template
        });

        next();
      });
    });

  }, function(){

    RESULT = result;
    callback(null, result);
  });
};<commit_msg>Added Kerim to websites<commit_after>var async = require('async');
var Blog = require('blog');
var url_parser = require('url').parse;
var dns = require('dns');
var BLOT_IP = '54.191.179.131';

var RESULT;

function capitalize (str) {
  return str[0].toUpperCase() + str.slice(1);
}

// Add new sites at the start

var sites = [

  ["keywords.oxus.net", "Kerim Friedman teaches anthropology in Taiwan. Kerim"],
  ["asw.fyi", "Adam Wood is a writer based in Reykjavík. Adam’s website"],
  ["hannautkin.com", "Hanna Utkin is a filmmaker and producer from New York City. Hanna"],

  ["www.rebeccatron.com", "Rebecca Kukshtel is an engineer based in New York City. Rebecca "],
  ["www.matthewbattles.org", "Matthew Battles directs metaLAB at Harvard. Matthew’s website"],
  ["fagerheimen.no", "Fagerheimen Borettslag is an apartment building in Oslo, Norway. Its website"],

  ["www.alexgibson.nyc", "Alexander Gibson is an artist from New York City. Alexander’s website"],
  ["www.jacobyyoung.com/about", "Jacoby Young works at Kapālama Elementary School in Hawaii. Jacoby"],
  ["www.kristianhjelle.com", "Kristian Hjelle is a designer and partner at Bakken & Bæck. Kristian"],

  ["tf2.blot.im", "Theo Francis is a reporter at The Wall Street Journal. Theo’s blog"],
  ["john.pavlusoffice.com", "John Pavlus is a writer and filmmaker based in Portland, Oregon. John"],
  ["sportsmanfc.com", "The Sportsman Flying Club is a pigeon‐racing club in Toft Hill, UK. Their website"],

  ["www.theliminal.co", "The Liminal is a podcast about metaphysical speculation. Their website"],
  ["blog.aurynn.com", "Aurynn Shaw is a writer and engineer from New Zealand. Aurynn’s site"],
  ["iiiiiiiii.in", "Ishtaarth Dalmia writes about internet culture and economics from Bengaluru, India. Ishtaarth"],
  ["kiefer.design", "Kiefer Sutherland is a freelance designer and art director. Kiefer’s website"],
  ["www.failuretolerated.com", "Sean McCoy is a game designer from Dallas, Texas. Sean’s site"],
  ["mrfris.by", "Stuart Frisby is the Director of Design at Booking.com. Stuart’s site"],
  ["www.querlin.com", "Querlin Ricci is a designer in Atlanta. Querlin’s website"],
];

module.exports = function (callback) {

  if (RESULT) return callback(null, RESULT);

  var result = [];

  // Only show the first 9 sites on the homepage
  sites = sites.slice(0, 12);

  async.eachSeries(sites, function(site, next){

    var parsed_URL = url_parser('https://' + site[0]);

    var domain = parsed_URL.host;
    var description = site[1].trim();
    var template;
    var is_subdomain = domain.indexOf('blot.im') !== -1;
    var url, display_url, protocol, by, custom_template, template_name;

    dns.lookup(domain, function(err, res) {

      if (!is_subdomain && res !== BLOT_IP) {
        console.log(domain, 'is not on blot it points to', res);
        return next();
      }

      if (is_subdomain) {
        by = {handle: domain.split('.blot.im').join('')};
      } else {
        by = {domain: domain};
      }

      Blog.get(by, function(err, blog){

        if (err || !blog) {
          console.log(domain, 'is not in the database');
          return next();
        }

        template = blog.template;

        protocol = domain.indexOf('blot.im') > -1 ? 'http' : 'https';

        url = protocol + '://' + site[0];
        display_url = domain.split('www.').join('');
        custom_template = template.indexOf('SITE') === -1;
        template_name = custom_template ? 'custom' : capitalize(template.split('SITE:').join(''));

        description += ' uses ';
        description += custom_template ? 'a' : 'the';
        description += ' ' + template_name + ' template.';

        // Redirect
        if (display_url === 'john.pavlusoffice.com') display_url = 'johnpavlus.com';

        result.push({
          url: url,
          first: result.length === 0,
          clear: (result.length + 1) % 3 === 0,
          display_url: display_url,
          description: description,
          domain: domain,
          template: template
        });

        next();
      });
    });

  }, function(){

    RESULT = result;
    callback(null, result);
  });
};<|endoftext|>


<commit_before>var Template=require('../util/Template');
var Logger=require('../util/Logger');
var Network=require('../Network');

module.exports=function(Marionette,_){

  return Marionette.ItemView.extend({
    template:Template('ConsoleView'),

    ui:{
      console:"#console",
      command_input:"#commands-input",
      info_toggle:"#console-info-toggle",
      debug_toggle:"#console-debug-toggle",
      error_toggle:"#console-error-toggle",
      warn_toggle:"#console-warn-toggle",
      data_toggle:"#console-data-toggle"
    },

    events:{
      'click #scroll-bottom-button':'enableScrollBottom',
      'click #send-command-button':'sendCommand',
      'submit #commands-input-form':'sendCommand',
      'keyup #commands-input':'cycleCommandHistory',
      'change #console-info-toggle':'toggleInfoMessages',
      'change #console-debug-toggle':'toggleDebugMessages',
      'change #console-error-toggle':'toggleErrorMessages',
      'change #console-warn-toggle':'toggleWarnMessages',
      'change #console-data-toggle':'toggleDataMessages',
    },

    className:'consoleView',

    initialize: function(){
      this.scroll_bottom=true;
      this.command_history=[];
      this.command_history_index=0;

      //whats kinds of messages we want displayed in the console
      this.infoMessages=!true;
      this.debugMessages=!true;
      this.errorMessages=!true;
      this.warnMessages=!true;
      this.dataMessages=!false;

      this.network_callbacks={}; //we need to keep a reference to these so that we can safely detach the network listener if we need to
    },

    onRender:function(){
      this.toggleInfoMessages();
      this.toggleDebugMessages();
      this.toggleErrorMessages();
      this.toggleWarnMessages();
      this.toggleDataMessages();

      this.detectScrollUp();
    },
    toggleInfoMessages:function(){
      if(this.infoMessages){
        if(this.network_callbacks['info']){
          Logger.removeListener('info',this.network_callbacks['info']);
        }
        this.infoMessages=false;
      }else{
        var callback=this.addInfoMessage.bind(this);
        this.network_callbacks['info']=callback;
        Logger.addListener('info',callback);
        this.infoMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.info_toggle.prop('checked',this.infoMessages);
    },
    toggleDebugMessages:function(){
      if(this.debugMessages){
        if(this.network_callbacks['debug']){
          Logger.removeListener('debug',this.network_callbacks['debug']);
        }
        this.debugMessages=false;
      }else{
        var callback=this.addDebugMessage.bind(this);
        this.network_callbacks['debug']=callback;
        Logger.addListener('debug',callback);
        this.debugMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.debug_toggle.prop('checked',this.debugMessages);
    },
    toggleErrorMessages:function(){
      if(this.errorMessages){
        if(this.network_callbacks['error-log']){
          Logger.removeListener('error-log',this.network_callbacks['error-log']);
        }
        this.errorMessages=false;
      }else{
        var callback=this.addErrorMessage.bind(this);
        this.network_callbacks['error-log']=callback;
        Logger.addListener('error-log',callback);
        this.errorMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.error_toggle.prop('checked',this.errorMessages);
    },
    toggleWarnMessages:function(){
      if(this.warnMessages){
        if(this.network_callbacks['warn']){
          Logger.removeListener('warn',this.network_callbacks['warn']);
        }
        this.warnMessages=false;
      }else{
        var callback=this.addWarnMessage.bind(this);
        this.network_callbacks['warn']=callback;
        Logger.addListener('warn',callback);
        this.warnMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.warn_toggle.prop('checked',this.warnMessages);
    },
    toggleDataMessages:function(){
      if(this.dataMessages){
        if(this.network_callbacks['data']){
          Logger.removeListener('data',this.network_callbacks['data']);
        }
        this.dataMessages=false;
      }else{
        var callback=this.addDataMessage.bind(this);
        this.network_callbacks['data']=callback;
        Logger.addListener('data',callback);
        this.dataMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.data_toggle.prop('checked',this.dataMessages);
    },
    addInfoMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addDebugMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addErrorMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addWarnMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addDataMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    sendCommand: function(e){
      if(e) e.preventDefault();
      var command=this.ui.command_input.val();
      if(command && command.trim()){
        Network.connections['data_relay'].write(command.trim());
        this.ui.command_input.val('');
        this.command_history.push(command.trim());
        this.command_history_index=0;
      }
    },
    cycleCommandHistory: function(e){
      if(e.keyCode===38){ //keyup
        this.ui.command_input.val(this.command_history[this.command_history.length-1-this.command_history_index]);
        this.command_history.length-1-this.command_history_index>0 ? this.command_history_index++:this.command_history_index;
      }
      else if (e.keyCode===40){ //keydown
        this.ui.command_input.val(this.command_history[this.command_history.length-1-this.command_history_index]);
        this.command_history_index>0 ? this.command_history_index--:this.command_history_index;
      }
    },
    detectScrollUp:function(){ //disables scrollToBottom if it detects an upward scroll
      var lastScrollTop = 0;
      var that=this;
      this.ui.console.scroll(function(event){
         var scrolltop = event.target.scrollTop;
         if (scrolltop < lastScrollTop){
            // upscroll code
            that.scroll_bottom=false;
          }
         lastScrollTop = scrolltop;
      });
    },
    enableScrollBottom:function(e){
      e.preventDefault();
      this.scroll_bottom=true;
      this.scrollToBottom();
    },
    scrollToBottom: function(){
      if(this.scroll_bottom){
        this.ui.console[0].scrollTop=this.ui.console[0].scrollHeight;
      }
    }
  });
}<commit_msg>Comments on the ConsoleView class<commit_after>var Template=require('../util/Template');
var Logger=require('../util/Logger');
var Network=require('../Network');

module.exports=function(Marionette,_){

  return Marionette.ItemView.extend({
    template:Template('ConsoleView'),

    ui:{
      console:"#console",
      command_input:"#commands-input",
      info_toggle:"#console-info-toggle",
      debug_toggle:"#console-debug-toggle",
      error_toggle:"#console-error-toggle",
      warn_toggle:"#console-warn-toggle",
      data_toggle:"#console-data-toggle"
    },

    events:{
      'click #scroll-bottom-button':'enableScrollBottom',
      'click #send-command-button':'sendCommand',
      'submit #commands-input-form':'sendCommand',
      'keyup #commands-input':'cycleCommandHistory',
      'change #console-info-toggle':'toggleInfoMessages',
      'change #console-debug-toggle':'toggleDebugMessages',
      'change #console-error-toggle':'toggleErrorMessages',
      'change #console-warn-toggle':'toggleWarnMessages',
      'change #console-data-toggle':'toggleDataMessages',
    },

    className:'consoleView',

    initialize: function(){
      this.scroll_bottom=true; //whether the console will scroll down upon new message arrival

      this.command_history=[];
      this.command_history_index=0;

      //whats kinds of messages we want displayed in the console
      this.infoMessages=!true; //the ! is there because we will call this.infoMessageToggle which will flip this to true in the render function
      this.debugMessages=!true;
      this.errorMessages=!true;
      this.warnMessages=!true;
      this.dataMessages=!false;

      this.logger_callbacks={}; //we need to keep a reference to the add message callbacks so that we can properly remove off the logger listener
    },

    onRender:function(){
      //Turns on the logger listeners
      this.toggleInfoMessages();
      this.toggleDebugMessages();
      this.toggleErrorMessages();
      this.toggleWarnMessages();
      this.toggleDataMessages();

      this.detectScrollUp();
    },
    onBeforeDestroy:function(){
      //Remove any logger listeners
      if(this.logger_callbacks['info']){
        Logger.removeListener('info',this.logger_callbacks['info']);
      }
      if(this.logger_callbacks['debug']){
        Logger.removeListener('debug',this.logger_callbacks['debug']);
      }
      if(this.logger_callbacks['error-log']){
        Logger.removeListener('error-log',this.logger_callbacks['error-log']);
      }
      if(this.logger_callbacks['warn']){
        Logger.removeListener('warn',this.logger_callbacks['warn']);
      }
      if(this.logger_callbacks['data']){
        Logger.removeListener('data',this.logger_callbacks['data']);
      }
    },
    toggleInfoMessages:function(){
      if(this.infoMessages){ //if we should stop listening to info level messages
        if(this.logger_callbacks['info']){
          Logger.removeListener('info',this.logger_callbacks['info']); //stop listening to info level messages from the Logger
        }
        this.infoMessages=false;
      }else{ //if we should start listening to info level messages
        var callback=this.addInfoMessage.bind(this);
        this.logger_callbacks['info']=callback;
        Logger.addListener('info',callback); //call this.addInfoMessage every time an info level message comes in
        this.infoMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.info_toggle.prop('checked',this.infoMessages);
    },
    toggleDebugMessages:function(){
      if(this.debugMessages){
        if(this.logger_callbacks['debug']){
          Logger.removeListener('debug',this.logger_callbacks['debug']);
        }
        this.debugMessages=false;
      }else{
        var callback=this.addDebugMessage.bind(this);
        this.logger_callbacks['debug']=callback;
        Logger.addListener('debug',callback);
        this.debugMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.debug_toggle.prop('checked',this.debugMessages);
    },
    toggleErrorMessages:function(){
      if(this.errorMessages){
        if(this.logger_callbacks['error-log']){
          Logger.removeListener('error-log',this.logger_callbacks['error-log']);
        }
        this.errorMessages=false;
      }else{
        var callback=this.addErrorMessage.bind(this);
        this.logger_callbacks['error-log']=callback;
        Logger.addListener('error-log',callback);
        this.errorMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.error_toggle.prop('checked',this.errorMessages);
    },
    toggleWarnMessages:function(){
      if(this.warnMessages){
        if(this.logger_callbacks['warn']){
          Logger.removeListener('warn',this.logger_callbacks['warn']);
        }
        this.warnMessages=false;
      }else{
        var callback=this.addWarnMessage.bind(this);
        this.logger_callbacks['warn']=callback;
        Logger.addListener('warn',callback);
        this.warnMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.warn_toggle.prop('checked',this.warnMessages);
    },
    toggleDataMessages:function(){
      if(this.dataMessages){
        if(this.logger_callbacks['data']){
          Logger.removeListener('data',this.logger_callbacks['data']);
        }
        this.dataMessages=false;
      }else{
        var callback=this.addDataMessage.bind(this);
        this.logger_callbacks['data']=callback;
        Logger.addListener('data',callback);
        this.dataMessages=true;
        this.scroll_bottom=true;
      }
      this.ui.data_toggle.prop('checked',this.dataMessages);
    },
    addInfoMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addDebugMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addErrorMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addWarnMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    addDataMessage:function(time,text){
      this.ui.console.append('
<time>'+time+'</time>'+text+'
');
      this.scrollToBottom();
    },
    sendCommand: function(e){
      if(e) e.preventDefault();
      var command=this.ui.command_input.val();
      if(command && command.trim()){
        Network.connections['data_relay'].write(command.trim());
        this.ui.command_input.val('');
        this.command_history.push(command.trim());
        this.command_history_index=0;
      }
    },
    cycleCommandHistory: function(e){
      if(e.keyCode===38){ //keyup
        this.ui.command_input.val(this.command_history[this.command_history.length-1-this.command_history_index]);
        this.command_history.length-1-this.command_history_index>0 ? this.command_history_index++:this.command_history_index;
      }
      else if (e.keyCode===40){ //keydown
        this.ui.command_input.val(this.command_history[this.command_history.length-1-this.command_history_index]);
        this.command_history_index>0 ? this.command_history_index--:this.command_history_index;
      }
    },
    detectScrollUp:function(){ //disables scrollToBottom if it detects an upward scroll
      var lastScrollTop = 0;
      var that=this;
      this.ui.console.scroll(function(event){
         var scrolltop = event.target.scrollTop;
         if (scrolltop < lastScrollTop){
            // upscroll code
            that.scroll_bottom=false;
          }
         lastScrollTop = scrolltop;
      });
    },
    enableScrollBottom:function(e){
      e.preventDefault();
      this.scroll_bottom=true;
      this.scrollToBottom();
    },
    scrollToBottom: function(){
      if(this.scroll_bottom){
        this.ui.console[0].scrollTop=this.ui.console[0].scrollHeight;
      }
    }
  });
};<|endoftext|>


<commit_before>'use strict';

angular.module('classBrowserUHApp.class', ['ngRoute']).
config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/class', {
        templateUrl: 'views/class/class.html',
        controller: 'ClassCtrl'
    });
}]).
controller('ClassCtrl', ['$scope', '$http', function ($scope, $http) {
    $scope.selectedCountLimit = 'count > 3';
    $scope.rowCollection = [];

    $scope.departments = {
        availableOptions: []
    };

    var populateDepartments = function() {
        var url = $scope.apiUrl + '/department';
        $http.get(url)
            .success(function (data) {
                console.log("Retrieved " + data.numberOfRows + " departments.");
                console.log(data);
                $scope.departments.availableOptions = data.result;
            })
            .error(function (data) {
                alert("Unable to retrieve the data.");
            })
    };

    populateDepartments();

    $scope.creditHours = {
        availableOptions: [
            {creditHours: 1},
            {creditHours: 1.5},
            {creditHours: 2},
            {creditHours: 3},
            {creditHours: 4},
            {creditHours: 5},
            {creditHours: 6},
            {creditHours: 7},
            {creditHours: 8},
            {creditHours: 9},
            {creditHours: 10}
        ]
    };

    $scope.coreCategories = {
        availableOptions: [
            {categoryNumber: 1, categoryName: "Communication"},
            {categoryNumber: 2, categoryName : "Mathematics"},
            {categoryNumber: 3, categoryName : "Life and Physical Sciences"},
            {categoryNumber: 4, categoryName : "Language, Philosophy & Culture"},
            {categoryNumber: 5, categoryName : "Creative Arts"},
            {categoryNumber: 6, categoryName : "American History"},
            {categoryNumber: 7, categoryName : "Government/Political Science"},
            {categoryNumber: 8, categoryName : "Social & Behavioral Sciences"},
            {categoryNumber: 9, categoryName : "Mathematics/Reasoning"},
            {categoryNumber: 10, categoryName : "Writing in the Disciplines"}
        ]
    };

    var generateMessage = function(model, type, objToPluck) {
        if(model == undefined || model.length == 0) {
            return "";
        }
        else {
            return " " + type + ": " + _.pluck(model, objToPluck).join(', ') + "
";
        }
    };

    $scope.populateClasses = function(){
        $scope.rowCollection = [];
        var apiUrl = buildApiUrlsFromModel($scope.departmentModel, $scope.creditHourModel, $scope.coreModel);
        console.log("Populating Classes...");

        console.log("Retrieved api URLs: " + apiUrl);

        $scope.numberOfRows = 0;
        for(var i = 0; i < apiUrl.length; i++) {
            console.log("Got URL: " + apiUrl[i]);
            $http
                .get(apiUrl[i])
                .success(function (data) {
                    $scope.numberOfRows += data.numberOfRows;
                    $scope.rowCollection = $scope.rowCollection.concat(data.result);
                    populateFields();
                })
                .error(function (data) {
                    alert("Unable to retrieve the data.");
                });
        }
    };

    var populateFields = function(){
        $scope.hasNoResults = false;
        console.log("In populate fields");
        console.log("Row Collection = " + $scope.rowCollection);
        if($scope.rowCollection.length > 0) {
            $scope.showDiv = true;

            $scope.numberOfRowsMessage = "Retrieved " + $scope.numberOfRows + " class";
            $scope.numberOfRowsMessage += $scope.numberOfRows == 1 ? " ." : "es.";

            if([$scope.departmentModel, $scope.creditHourModel, $scope.coreModel].allParametersUndefinedOrNull()) {
                $scope.parametersMessage = "No parameters were chosen, so all classes have been retrieved.";
            }
            else {
                $scope.parametersMessage = "";
                $scope.subjectMessage = generateMessage($scope.departmentModel, 'Subjects', 'departmentFullName');
                $scope.creditHoursMessage = generateMessage($scope.creditHourModel, 'Credit Hours', 'creditHours');
                $scope.coreMessage = generateMessage($scope.coreModel, 'Core Categories', 'categoryName');
                $scope.parametersMessage = $scope.subjectMessage +
                $scope.creditHoursMessage +
                $scope.coreMessage;
            }
        }
        else {
            $scope.hasNoResults = true;
        }
    };

    var isArrayIsUndefinedOrNull = function(arr) {
        return !(_.isUndefined(arr) || _.isNull(arr) || _.isEmpty(arr));
    };

    Array.prototype.allParametersUndefinedOrNull = function() {
        for(var i = 0; i < this.length; i++) {
            if(isArrayIsUndefinedOrNull(this[i])) {
                return false;
            }
        }
        return true;
    };

    var buildApiUrlsFromModel = function(department, creditHour, core) {
        console.log("Building API URL...");
        var baseUrl = $scope.apiUrl + '/information?';
        var allParametersFromScope = [department, creditHour, core];

        if(allParametersFromScope.allParametersUndefinedOrNull()) {
            console.log("All parameters are empty...");
            return [baseUrl];
        }

        console.log("User has selected some categories. Preparing API URL.");
        department = _.pluck(department, 'departmentName');
        creditHour = _.pluck(creditHour, 'creditHours');
        core = _.pluck(core, 'categoryNumber');

        console.log("dept " + department);
        console.log("crh " + creditHour);
        console.log("core " + core);

        var expandArrayValues = function(arr, parameter) {
            arr.forEach(function (part, index, arr) {
                arr[index] = parameter + "=" + part;
            });
        };

        if(isArrayIsUndefinedOrNull(department)) {
            expandArrayValues(department, 'department');
        }
        if(isArrayIsUndefinedOrNull(creditHour)) {
            expandArrayValues(creditHour, 'credit-hours');
        }
        if(isArrayIsUndefinedOrNull(core)) {
            expandArrayValues(core, 'core');
        }

        var appendApiUrl = function(arr) {
            arr.forEach(function (part, index, arr) {
                arr[index] = baseUrl + part;
            });
        };

        var allParameters = [department,creditHour,core].filter(nonEmpty).reduce(productAdd);

        appendApiUrl(allParameters);

        function nonEmpty(xs) {
            return xs.length > 0;
        }

        function productAdd(xs, ys) {
            return product(add, xs, ys);
        }

        function add(a, b) {
            return a + "&" + b;
        }

        function product(f, xs, ys) {
            var zs = [];

            var m = xs.length;
            var n = ys.length;

            for (var i = 0; i < m; i++) {
                var x = xs[i];

                for (var j = 0; j < n; j++)
                    zs.push(f(x, ys[j]));
            }

            return zs;
        }

        console.log("Retrieved parameters from cartesian product: " + allParameters);
        return allParameters;
    };

    $scope.goBack = function() {
        console.log('going back');
        $scope.showDiv = false;
    };

    $scope.clearForms = function() {
        $scope.hasNoResults = false;
        console.log('clearing forms...');
        console.log($scope.departmentModel);
        console.log($scope.creditHourModel);
        console.log($scope.coreModel);
        $scope.departmentModel = undefined;
        $scope.creditHourModel = undefined;
        $scope.coreModel = undefined;
        $scope.parametersMessage = undefined;
        $scope.rowCollection = undefined;
    };

}]);<commit_msg>Used a local JSON file instead of querying the departments api endpoint. Edited log messages. Removed unnecessary log messages.<commit_after>'use strict';

angular.module('classBrowserUHApp.class', ['ngRoute']).
config(['$routeProvider', function($routeProvider) {
    $routeProvider.when('/class', {
        templateUrl: 'views/class/class.html',
        controller: 'ClassCtrl'
    });
}]).
controller('ClassCtrl', ['$scope', '$http', function ($scope, $http) {
    $scope.selectedCountLimit = 'count > 3';
    $scope.rowCollection = [];

    $scope.departments = {
        availableOptions: []
    };

    var populateDepartments = function() {
        $http.get('resources/subjects.json')
            .success(function (data) {
                console.log("Retrieved " + data.numberOfRows + " departments.");
                $scope.departments.availableOptions = data.departments;
            })
            .error(function (data) {
                alert("Unable to retrieve the data.");
            })
    };

    populateDepartments();

    $scope.creditHours = {
        availableOptions: [
            {creditHours: 1},
            {creditHours: 1.5},
            {creditHours: 2},
            {creditHours: 3},
            {creditHours: 4},
            {creditHours: 5},
            {creditHours: 6},
            {creditHours: 7},
            {creditHours: 8},
            {creditHours: 9},
            {creditHours: 10}
        ]
    };

    $scope.coreCategories = {
        availableOptions: [
            {categoryNumber: 1, categoryName: "Communication"},
            {categoryNumber: 2, categoryName : "Mathematics"},
            {categoryNumber: 3, categoryName : "Life and Physical Sciences"},
            {categoryNumber: 4, categoryName : "Language, Philosophy & Culture"},
            {categoryNumber: 5, categoryName : "Creative Arts"},
            {categoryNumber: 6, categoryName : "American History"},
            {categoryNumber: 7, categoryName : "Government/Political Science"},
            {categoryNumber: 8, categoryName : "Social & Behavioral Sciences"},
            {categoryNumber: 9, categoryName : "Mathematics/Reasoning"},
            {categoryNumber: 10, categoryName : "Writing in the Disciplines"}
        ]
    };

    var generateMessage = function(model, type, objToPluck) {
        if(model == undefined || model.length == 0) {
            return "";
        }
        else {
            return " " + type + ": " + _.pluck(model, objToPluck).join(', ') + "
";
        }
    };

    $scope.populateClasses = function(){
        $scope.rowCollection = [];
        var apiUrl = buildApiUrlsFromModel($scope.departmentModel, $scope.creditHourModel, $scope.coreModel);
        console.log("Populating Classes...");

        $scope.numberOfRows = 0;
        for(var i = 0; i < apiUrl.length; i++) {
            console.log("Got URL: " + apiUrl[i]);
            $http
                .get(apiUrl[i])
                .success(function (data) {
                    $scope.numberOfRows += data.numberOfRows;
                    $scope.rowCollection = $scope.rowCollection.concat(data.result);
                    populateFields();
                })
                .error(function (data) {
                    alert("Unable to retrieve the data.");
                });
        }
    };

    var populateFields = function(){
        $scope.hasNoResults = false;
        console.log("In populate fields");
        console.log("Row Collection = " + $scope.rowCollection);
        if($scope.rowCollection.length > 0) {
            $scope.showDiv = true;

            $scope.numberOfRowsMessage = "Retrieved " + $scope.numberOfRows + " class";
            $scope.numberOfRowsMessage += $scope.numberOfRows == 1 ? " ." : "es.";

            if([$scope.departmentModel, $scope.creditHourModel, $scope.coreModel].allParametersUndefinedOrNull()) {
                $scope.parametersMessage = "No parameters were chosen, so all classes have been retrieved.";
            }
            else {
                $scope.parametersMessage = "";
                $scope.subjectMessage = generateMessage($scope.departmentModel, 'Subjects', 'departmentFullName');
                $scope.creditHoursMessage = generateMessage($scope.creditHourModel, 'Credit Hours', 'creditHours');
                $scope.coreMessage = generateMessage($scope.coreModel, 'Core Categories', 'categoryName');
                $scope.parametersMessage = $scope.subjectMessage +
                    $scope.creditHoursMessage +
                    $scope.coreMessage;
            }
        }
        else {
            $scope.hasNoResults = true;
        }
    };

    var isArrayIsUndefinedOrNull = function(arr) {
        return !(_.isUndefined(arr) || _.isNull(arr) || _.isEmpty(arr));
    };

    Array.prototype.allParametersUndefinedOrNull = function() {
        for(var i = 0; i < this.length; i++) {
            if(isArrayIsUndefinedOrNull(this[i])) {
                return false;
            }
        }
        return true;
    };

    var buildApiUrlsFromModel = function(department, creditHour, core) {
        console.log("Building API URL...");
        var baseUrl = $scope.apiUrl + '/information?';
        var allParametersFromScope = [department, creditHour, core];

        if(allParametersFromScope.allParametersUndefinedOrNull()) {
            console.log("All parameters are empty...");
            return [baseUrl];
        }

        console.log("User has selected some categories. Preparing API URL(s).");
        department = _.pluck(department, 'departmentName');
        creditHour = _.pluck(creditHour, 'creditHours');
        core = _.pluck(core, 'categoryNumber');

        console.log("dept " + department);
        console.log("crh " + creditHour);
        console.log("core " + core);

        var expandArrayValues = function(arr, parameter) {
            arr.forEach(function (part, index, arr) {
                arr[index] = parameter + "=" + part;
            });
        };

        if(isArrayIsUndefinedOrNull(department)) {
            expandArrayValues(department, 'department');
        }
        if(isArrayIsUndefinedOrNull(creditHour)) {
            expandArrayValues(creditHour, 'credit-hours');
        }
        if(isArrayIsUndefinedOrNull(core)) {
            expandArrayValues(core, 'core');
        }

        var appendApiUrl = function(arr) {
            arr.forEach(function (part, index, arr) {
                arr[index] = baseUrl + part;
            });
        };

        var allParameters = [department,creditHour,core].filter(nonEmpty).reduce(productAdd);

        appendApiUrl(allParameters);

        function nonEmpty(xs) {
            return xs.length > 0;
        }

        function productAdd(xs, ys) {
            return product(add, xs, ys);
        }

        function add(a, b) {
            return a + "&" + b;
        }

        function product(f, xs, ys) {
            var zs = [];

            var m = xs.length;
            var n = ys.length;

            for (var i = 0; i < m; i++) {
                var x = xs[i];

                for (var j = 0; j < n; j++)
                    zs.push(f(x, ys[j]));
            }

            return zs;
        }

        console.log("Retrieved parameters from cartesian product: " + allParameters);
        return allParameters;
    };

    $scope.goBack = function() {
        console.log('Going back to class search page.');
        $scope.showDiv = false;
    };

    $scope.clearForms = function() {
        $scope.hasNoResults = false;
        console.log('Clearing forms and fields.');
        console.log($scope.departmentModel);
        console.log($scope.creditHourModel);
        console.log($scope.coreModel);
        $scope.departmentModel = undefined;
        $scope.creditHourModel = undefined;
        $scope.coreModel = undefined;
        $scope.parametersMessage = undefined;
        $scope.rowCollection = undefined;
    };

}]);<|endoftext|>


<commit_before>document.addEventListener("click", function (event) {
  var name = event.target.tagName;
  if (name == "A" || name == "BUTTON" ||
      (name == "INPUT" && event.target.getAttribute("type") == "submit")) {
    event.preventDefault();
    return false;
  }
}, true);

document.addEventListener("focus", function (event) {
  event.preventDefault();
}, true);

// #239 adding transparent div on the body if you're a spectator so you can't do anything on the page.
var transparentDiv = document.createElement("div");
transparentDiv.id = 'hotdishTransparentDiv';
transparentDiv.style.cssText = 'position:fixed;width:100%;height:100%;z-index:9999;background:none;top:0;right:0;bottom:0;left:0;';
transparentDiv.jsmirrorHide = true;
document.getElementsByTagName('body')[0].appendChild(transparentDiv);
transparentDiv.innerHTML = "";

var style = document.createElement("style");
style.textContent = [
  //If you're a spectator, your cursor should :not-allowed
  "* {",
  "  cursor: not-allowed;",
  "}",
  ".togetherjs-cursor {",
  // I don't think this does anything:
  "  cursor: not-allowed;",
  "}"
].join("\n");
style.jsmirrorHide = true;
document.head.appendChild(style);

var viewer = document.getElementById("viewer");
viewer.parentNode.removeChild(viewer);
<commit_msg>fixed #276<commit_after>document.addEventListener("click", function (event) {
  var name = event.target.tagName;
  if (name == "A" || name == "BUTTON" ||
      (name == "INPUT" && event.target.getAttribute("type") == "submit")) {
    event.preventDefault();
    return false;
  }
}, true);

document.addEventListener("focus", function (event) {
  event.preventDefault();
}, true);

// #239 adding transparent div on the body if you're a spectator so you can't do anything on the page.
// var transparentDiv = document.createElement("div");
// transparentDiv.id = 'hotdishTransparentDiv';
// transparentDiv.style.cssText = 'position:fixed;width:100%;height:100%;z-index:9999;background:none;top:0;right:0;bottom:0;left:0;';
// transparentDiv.jsmirrorHide = true;
// document.getElementsByTagName('body')[0].appendChild(transparentDiv);
// transparentDiv.innerHTML = "";

var style = document.createElement("style");
style.textContent = [
  //If you're a spectator, your cursor should :not-allowed
  "* {",
  "  cursor: not-allowed;",
  "}",
  ".togetherjs-cursor {",
  // I don't think this does anything:
  "  cursor: not-allowed;",
  "}"
].join("\n");
style.jsmirrorHide = true;
document.head.appendChild(style);

var viewer = document.getElementById("viewer");
viewer.parentNode.removeChild(viewer);
<|endoftext|>


<commit_before>var Chat = {
    left : null,
    right: null,
    room: null,
    previous: null,
    addSmiley: function(element) {
        var n = document.querySelector('#chat_textarea');
        n.value = n.value + element.dataset.emoji;
        n.focus();
        Dialog.clear();
    },
    sendMessage: function(jid, muc)
    {
        var n = document.querySelector('#chat_textarea');
        var text = n.value;
        n.value = "";
        n.focus();
        Chat_ajaxSendMessage(jid, encodeURIComponent(text), muc);
    },
    appendTextarea: function(value)
    {
    },
    notify : function(title, body, image)
    {
        if(document_focus == false) {
            movim_title_inc();
            movim_desktop_notification(title, body, image);
        }
    },
    empty : function()
    {
        Chat_ajaxGet();
    },
    setBubbles : function(left, right, room) {
        var div = document.createElement('div');

        div.innerHTML = left;
        Chat.left = div.firstChild;
        div.innerHTML = right;
        Chat.right = div.firstChild;
        div.innerHTML = room;
        Chat.room = div.firstChild;
    },
    appendMessages : function(messages) {
        if(messages) {
	    for(var i = 0, len = messages.length; i < len; ++i ) {
                Chat.appendMessage(messages[i]);
            }
	}
    },
    appendMessage : function(message) {
        if(message.body == '') return;

        var bubble = null;
        var id = null;

        var scrolled = MovimTpl.isPanelScrolled();

        if(message.type == 'groupchat') {
            bubble = Chat.room.cloneNode(true);

            id = message.jidfrom + '_conversation';

            if(message.body.match(/^\/me/)) {
                bubble.querySelector('div').className = 'quote';
                message.body = message.body.substr(4);
            }

            bubble.querySelector('div').innerHTML = message.body;
            bubble.querySelector('span.info').innerHTML = message.published;
            bubble.querySelector('span.user').className = 'user ' + message.color;

            bubble.querySelector('span.user').onclick = function(n) {
                var textarea = document.querySelector('#chat_textarea');
                textarea.value = this.innerHTML + ', ' + textarea.value;
                textarea.focus();
            };

            bubble.querySelector('span.user').innerHTML = message.resource;
            var conversation = document.getElementById(id);
            if(conversation) {
		conversation.appendChild(bubble);
	    }

	    bubble.querySelector('div').className = '';
        } else if(Chat.left != null) {
            if(message.session == message.jidfrom) {
                bubble = Chat.right.cloneNode(true);
                if(Chat.previous == 'right') {
                    bubble.className += ' same';
                }

                Chat.previous = 'right';
                id = message.jidto + '_conversation';
            } else {
                bubble = Chat.left.cloneNode(true);
                if(Chat.previous == 'left') {
                    bubble.className += ' same';
                }

                Chat.previous = 'left';
                id = message.jidfrom + '_conversation';
            }

            if(message.body.match(/^\/me/)) {
                bubble.querySelector('div.bubble').className = 'bubble quote';
                message.body = message.body.substr(4);
            }

            if(bubble) {
                bubble.querySelector('div.bubble div').innerHTML = message.body;

                bubble.querySelector('div.bubble span.info').innerHTML = message.published;

                movim_append(id, bubble.outerHTML);
                bubble.querySelector('div.bubble').className = 'bubble';
            }
        }

        if(scrolled) MovimTpl.scrollPanel();
    }
}

MovimWebsocket.attach(function() {
    var jid = document.querySelector('#chat_widget').dataset.jid;
    if(jid) {
        MovimTpl.showPanel();
        Chat_ajaxGet(jid);
    }
});

Upload.attach(function() {
    var textarea = document.querySelector('#chat_textarea');
    textarea.value = Upload.get + ' ' + textarea.value;
    textarea.focus();
    movim_textarea_autoheight(textarea);
});

var state = 0;
<commit_msg>Add an autoheight on the textarea to fix a height issue when sending a message on mobile Scroll when sending a message in Chat<commit_after>var Chat = {
    left : null,
    right: null,
    room: null,
    previous: null,
    addSmiley: function(element) {
        var n = document.querySelector('#chat_textarea');
        n.value = n.value + element.dataset.emoji;
        n.focus();
        Dialog.clear();
    },
    sendMessage: function(jid, muc)
    {
        var n = document.querySelector('#chat_textarea');
        var text = n.value;
        n.value = "";
        n.focus();
        movim_textarea_autoheight(n);
        Chat_ajaxSendMessage(jid, encodeURIComponent(text), muc);
    },
    appendTextarea: function(value)
    {
    },
    notify : function(title, body, image)
    {
        if(document_focus == false) {
            movim_title_inc();
            movim_desktop_notification(title, body, image);
        }
    },
    empty : function()
    {
        Chat_ajaxGet();
    },
    setBubbles : function(left, right, room) {
        var div = document.createElement('div');

        div.innerHTML = left;
        Chat.left = div.firstChild;
        div.innerHTML = right;
        Chat.right = div.firstChild;
        div.innerHTML = room;
        Chat.room = div.firstChild;
    },
    appendMessages : function(messages) {
        if(messages) {
	    for(var i = 0, len = messages.length; i < len; ++i ) {
                Chat.appendMessage(messages[i]);
            }
	}
    },
    appendMessage : function(message) {
        if(message.body == '') return;

        var bubble = null;
        var id = null;

        var scrolled = MovimTpl.isPanelScrolled();

        if(message.type == 'groupchat') {
            bubble = Chat.room.cloneNode(true);

            id = message.jidfrom + '_conversation';

            if(message.body.match(/^\/me/)) {
                bubble.querySelector('div').className = 'quote';
                message.body = message.body.substr(4);
            }

            bubble.querySelector('div').innerHTML = message.body;
            bubble.querySelector('span.info').innerHTML = message.published;
            bubble.querySelector('span.user').className = 'user ' + message.color;

            bubble.querySelector('span.user').onclick = function(n) {
                var textarea = document.querySelector('#chat_textarea');
                textarea.value = this.innerHTML + ', ' + textarea.value;
                textarea.focus();
            };

            bubble.querySelector('span.user').innerHTML = message.resource;
            var conversation = document.getElementById(id);
            if(conversation) {
                conversation.appendChild(bubble);
            }

            bubble.querySelector('div').className = '';
        } else if(Chat.left != null) {
            if(message.session == message.jidfrom) {
                bubble = Chat.right.cloneNode(true);
                if(Chat.previous == 'right') {
                    bubble.className += ' same';
                }

                Chat.previous = 'right';
                id = message.jidto + '_conversation';
            } else {
                bubble = Chat.left.cloneNode(true);
                if(Chat.previous == 'left') {
                    bubble.className += ' same';
                }

                Chat.previous = 'left';
                id = message.jidfrom + '_conversation';
            }

            if(message.body.match(/^\/me/)) {
                bubble.querySelector('div.bubble').className = 'bubble quote';
                message.body = message.body.substr(4);
            }

            if(bubble) {
                bubble.querySelector('div.bubble div').innerHTML = message.body;

                bubble.querySelector('div.bubble span.info').innerHTML = message.published;

                movim_append(id, bubble.outerHTML);
                bubble.querySelector('div.bubble').className = 'bubble';

                if(bubble.className.contains('oppose')) MovimTpl.scrollPanel();
            }
        }

        if(scrolled) MovimTpl.scrollPanel();
    }
}

MovimWebsocket.attach(function() {
    var jid = document.querySelector('#chat_widget').dataset.jid;
    if(jid) {
        MovimTpl.showPanel();
        Chat_ajaxGet(jid);
    }
});

Upload.attach(function() {
    var textarea = document.querySelector('#chat_textarea');
    textarea.value = Upload.get + ' ' + textarea.value;
    textarea.focus();
    movim_textarea_autoheight(textarea);
});

var state = 0;
<|endoftext|>


<commit_before>var fetch = require('node-fetch')
var cheerio = require('cheerio')
var camelcase = require('lodash.camelcase')

module.exports = parseWikiTable

function parseWikiTable(title, cb) {
    var apiUrl = wikiApiQueryUrl(title)

    fetch(apiUrl, {
            headers: {
                'User-Agent': 'Wikitable-To-JSON/1.0 (https://github.com/barberboy/wikitable-to-json'
            }
        })
        .then(function(res) {
            return res.json()
        })
        .then(extractPageContent)
        .then(convertWikitables)
        .then(cb)
}

function wikiApiQueryUrl(title) {
    return 'https://en.wikipedia.org/w/api.php?action=query&titles=' + encodeURIComponent(title) + '&prop=revisions&rvprop=content&format=json&rvparse'
}

function extractPageContent(json) {
    // so brittle!
    var pages = json.query.pages
    var content = pages[Object.keys(pages)[0]].revisions[0]['*']
    return content
}

function convertWikitables(content) {
    var $ = cheerio.load(content)
    var tables = []

    $('.wikitable').each(function(i, t) {
        var table = {
            title: '',
            headers: [],
            rows: []
        }

        t = $(t)

        // clean up the table
        t.find('.sortkey').remove()

        // title - use caption element if present, otherwise the closest heading
        table.title = clean(t.children('caption')) ||
            clean(t.prevAll('h1, h2, h3, h4, h5, h6').eq(0))

        // headers
        t.find('> tr > th').each(function(i, h) {
            var header = clean($(h))
            table.headers.push({
                key: key(header),
                text: header
            })
        })

        // rows
        t.find('> tr').each(function(idx, r) {
            var data = {}

            $(r).find('> td').each(function(idx, d) {
                // Store as key:data if we have headers, otherwise idx:data
                var k = table.headers[idx] ? table.headers[idx].key : idx
                data[k] = clean($(d))
            })

            // Don't store empty rows (or if we only had th)
            if (Object.keys(data).length) {
                table.rows.push(data)
            }
        })

        tables.push(table)
    })

    return tables
}

function clean(el) {
    return (el && el.text() || '').replace('[edit]', '').trim();
}

function key(heading) {
    return camelcase(heading)
}
<commit_msg>Fix user agent format<commit_after>var fetch = require('node-fetch')
var cheerio = require('cheerio')
var camelcase = require('lodash.camelcase')

module.exports = parseWikiTable

function parseWikiTable(title, cb) {
    var apiUrl = wikiApiQueryUrl(title)

    fetch(apiUrl, {
            headers: {
                'User-Agent': 'Wikitable-To-JSON/1.0 (https://github.com/barberboy/wikitable-to-json)'
            }
        })
        .then(function(res) {
            return res.json()
        })
        .then(extractPageContent)
        .then(convertWikitables)
        .then(cb)
}

function wikiApiQueryUrl(title) {
    return 'https://en.wikipedia.org/w/api.php?action=query&titles=' + encodeURIComponent(title) + '&prop=revisions&rvprop=content&format=json&rvparse'
}

function extractPageContent(json) {
    // so brittle!
    var pages = json.query.pages
    var content = pages[Object.keys(pages)[0]].revisions[0]['*']
    return content
}

function convertWikitables(content) {
    var $ = cheerio.load(content)
    var tables = []

    $('.wikitable').each(function(i, t) {
        var table = {
            title: '',
            headers: [],
            rows: []
        }

        t = $(t)

        // clean up the table
        t.find('.sortkey').remove()

        // title - use caption element if present, otherwise the closest heading
        table.title = clean(t.children('caption')) ||
            clean(t.prevAll('h1, h2, h3, h4, h5, h6').eq(0))

        // headers
        t.find('> tr > th').each(function(i, h) {
            var header = clean($(h))
            table.headers.push({
                key: key(header),
                text: header
            })
        })

        // rows
        t.find('> tr').each(function(idx, r) {
            var data = {}

            $(r).find('> td').each(function(idx, d) {
                // Store as key:data if we have headers, otherwise idx:data
                var k = table.headers[idx] ? table.headers[idx].key : idx
                data[k] = clean($(d))
            })

            // Don't store empty rows (or if we only had th)
            if (Object.keys(data).length) {
                table.rows.push(data)
            }
        })

        tables.push(table)
    })

    return tables
}

function clean(el) {
    return (el && el.text() || '').replace('[edit]', '').trim();
}

function key(heading) {
    return camelcase(heading)
}
<|endoftext|>


<commit_before>const { shell } = require('electron')
const path = require('path')
const fs = require('fs')

const layout = require('dw-layout')
const logger = require('dw-log')
const Database = require('dw-database')

layout.window.setTitle('Song Library Check')

const Songs = new Database.Group('songs', {
    load: true,
    parse: true,

    transform: data => {
        data._missing = []
        data._problems = []

        if (typeof data.name !== 'string') {
            data._missing.push('name')
        } else if (data.name.trim() === '') {
            data._problems.push('Name empty')
        }
        if (typeof data.author !== 'string') {
            data._missing.push('author')
        } else if (data.author.trim() === '') {
            data._problems.push('Author empty')
        }
        if (typeof data.copyright !== 'string') {
            data._missing.push('copyright')
        } else if (data.copyright.trim() === '') {
            data._problems.push('Copyright empty')
        }

        if (!Array.isArray(data.playOrder)) {
            data._missing.push('play order')
        } else if (data.playOrder.length === 0) {
            data._problems.push('Play order empty')
        } else if (
            typeof data.sections === 'object' &&
            !Array.isArray(data.sections) &&
            data.sections !== null
        ) {
            let missing = []

            for (let i = 0; i < data.playOrder.length; i++) {
                if (!data.sections.hasOwnProperty(data.playOrder[i])) {
                    missing.push(data.playOrder[i])
                }
            }

            if (missing.length > 0) {
                data._problems.push(
                    'Play order includes ' +
                        missing.length.toString() +
                        ' missing sections'
                )
            }
        }

        if (
            typeof data.sections !== 'object' ||
            Array.isArray(data.sections) ||
            data.sections === null
        ) {
            data._missing.push('sections')
        } else if (Object.keys(data.sections).length === 0) {
            data._problems.push('No sections')
        }

        if (typeof data.group !== 'string') {
            data._missing.push('group')
        }
        if (typeof data.groupID !== 'number') {
            data._missing.push('group ID')
        }

        return data
    }
})

const showImportantButton = new layout.Button({
    text: 'Important'
})
const showAllButton = new layout.Button({
    text: 'All'
})
const showFileButton = new layout.Button({
    text: 'Invalid Files'
})

const infoText = new layout.Text({}, { align: 'center' })

const songList = new layout.TableList(
    {
        columns: 4,
        columnWidths: ['13ch', '100%', '3ch', '5ch']
    },
    {
        margin: 4,

        border: true
    }
)

const fixMissingButton = new layout.Button({
    text: 'Fix Missing'
})

layout.change(layout.body, {
    direction: 'vertical',

    padding: 4
})

layout.body.add(
    new layout.Block(
        {
            childSpacing: 8,
            items: [
                showImportantButton,
                showAllButton,
                new layout.Text({ text: '│' }, { align: 'center' }),
                showFileButton,
                new layout.Text({ text: '│' }, { align: 'center' }),
                infoText
            ]
        },
        {
            direction: 'horizontal',
            padding: 0,

            shrink: false,
            grow: false
        }
    )
)
layout.body.add(songList)
layout.body.add(
    new layout.Block(
        {
            childSpacing: 8,
            items: [fixMissingButton]
        },
        {
            direction: 'horizontal',
            shrink: false,
            grow: false,

            padding: 0
        }
    )
)

const filters = {
    important: song => song._missing.length > 0,
    all: song => song._missing.length > 0 || song._problems.length > 0
}

const errorFiles = []

let searchFilter = filters.important

function updateList() {
    let errorSongs = Songs.list
        .filter(searchFilter)
        .sort((a, b) => a._ID - b._ID)

    songList.clear()

    for (let i = 0; i < errorSongs.length; i++) {
        if (errorSongs[i]._missing.length > 0) {
            songList.add([
                errorSongs[i]._filename,
                'Missing ' +
                    errorSongs[i]._missing.join(', ') +
                    (errorSongs[i]._problems.length > 0
                        ? '. Problems: ' +
                          errorSongs[i]._problems.join(', ') +
                          '.'
                        : '.'),
                errorSongs[i]._group,
                errorSongs[i]._ID
            ])
        } else {
            songList.add([
                errorSongs[i]._filename,
                'Problems: ' + errorSongs[i]._problems.join(', ') + '.',
                errorSongs[i]._group,
                errorSongs[i]._ID
            ])
        }
    }

    infoText.text =
        errorSongs.length.toString() +
        ' song' +
        (errorSongs.length === 1 ? '.' : 's.') +
        (errorSongs.length > 0 ? ' Click to edit' : '')
}

function showErrorList() {
    songList.clear()

    for (let i = 0; i < errorFiles.length; i++) {
        songList.add([
            errorFiles[i].filename,
            '(' + errorFiles[i].type + ') ' + errorFiles[i].error.message,
            errorFiles[i].group,
            errorFiles[i].ID
        ])
    }

    infoText.text =
        errorFiles.length.toString() +
        ' file' +
        (errorFiles.length === 1 ? '.' : 's.') +
        (errorFiles.length > 0 ? ' Click to resolve.' : '')
}

songList.onEvent('select', event => {
    if (showFileButton.active) {
        layout.dialog.showQuestion(
            {
                title: 'Resolve',
                message: 'Do you want to',
                options: ['Open', 'Delete', 'Cancel']
            },
            (error, choice) => {
                if (error) {
                    return false
                }

                if (choice === 'Open') {
                    let opened = shell.openItem(
                        path.join(Songs.directory, event.text[0])
                    )

                    if (!opened) {
                        layout.dialog.showNotification({
                            type: 'error',
                            message: "Couldn't open file!"
                        })
                    }
                } else if (choice === 'Delete') {
                    fs.unlink(
                        path.join(Songs.directory, event.text[0]),
                        error => {
                            let index = errorFiles.indexOf(
                                file => file.filename === event.text[0]
                            )

                            if (index !== -1) {
                                errorFiles.splice(index, 1)

                                showErrorList()
                            }

                            if (error) {
                                layout.dialog.showNotification({
                                    type: 'error',
                                    message:
                                        "Couldn't delete file!\n" +
                                        error.toString()
                                })

                                logger.error(
                                    "Couldn't delete invalid song library file (" +
                                        event.text[0] +
                                        '):',
                                    error
                                )
                            }
                        }
                    )
                }
            }
        )

        return
    }

    layout.window.openWindow('songDatabase', [
        'show-song',
        event.text[2],
        parseInt(event.text[3])
    ])
})

showImportantButton.onEvent('click', () => {
    showImportantButton.active = true
    showAllButton.active = false
    showFileButton.active = false

    searchFilter = filters.important

    updateList()
})
showAllButton.onEvent('click', () => {
    showImportantButton.active = false
    showAllButton.active = true
    showFileButton.active = false

    searchFilter = filters.all

    updateList()
})
showFileButton.onEvent('click', () => {
    showImportantButton.active = false
    showAllButton.active = false
    showFileButton.active = true

    showErrorList()
})

fixMissingButton.onEvent('click', () => {
    let missingList = Songs.list.filter(song => song._missing.length > 0)

    if (missingList.length === 0) {
        return
    }

    layout.showLoader(layout.body, 'Fixing')

    let fixNext = () => {
        if (missingList.length === 0) {
            layout.hideLoader(layout.body)

            return
        }

        let song = missingList.pop()

        delete song._missing
        delete song._problems

        if (typeof song.name !== 'string') {
            song.name = ''
        }
        if (typeof song.author !== 'string') {
            song.author = ''
        }
        if (typeof song.copyright !== 'string') {
            song.copyright = ''
        }

        if (!Array.isArray(song.playOrder)) {
            song.playOrder = []
        }

        if (
            typeof song.sections !== 'object' ||
            Array.isArray(song.sections) ||
            song.sections === null
        ) {
            song.sections = {}
        }

        if (typeof song.group !== 'string') {
            song.group = song._group
        }
        if (typeof song.groupID !== 'number') {
            song.groupID = song._ID
        }

        Songs.save(song._group, song._ID, song, fixNext)
    }

    fixNext()
})

Songs.onEvent('update', () => {
    updateList()

    fixMissingButton.disabled = !Songs.list.find(
        song => song._missing.length > 0
    )
})

Songs.onEvent('file-error', (error, file) => {
    let index = errorFiles.findIndex(errorFile => {
        return errorFile.filename === file.filename
    })

    if (index === -1) {
        file.error = error
        errorFiles[index].type = 'file'
        errorFiles.push(file)
    } else {
        errorFiles[index].type = 'file'
        errorFiles[index].error = error
    }
})
Songs.onEvent('transform-error', (error, file) => {
    let index = errorFiles.findIndex(errorFile => {
        return errorFile.filename === file.filename
    })

    if (index === -1) {
        file.error = error
        errorFiles[index].type = 'transform'
        errorFiles.push(file)
    } else {
        errorFiles[index].type = 'transform'
        errorFiles[index].error = error
    }
})

Songs.onEvent('error', error => {
    layout.dialog.showNotification({
        type: 'error',
        autoHide: false,

        message: 'There is an error with the Song database!\n' + error.message
    })
})

showImportantButton.active = true
<commit_msg>Removed unneeded setTitle call<commit_after>const { shell } = require('electron')
const path = require('path')
const fs = require('fs')

const layout = require('dw-layout')
const logger = require('dw-log')
const Database = require('dw-database')

const Songs = new Database.Group('songs', {
    load: true,
    parse: true,

    transform: data => {
        data._missing = []
        data._problems = []

        if (typeof data.name !== 'string') {
            data._missing.push('name')
        } else if (data.name.trim() === '') {
            data._problems.push('Name empty')
        }
        if (typeof data.author !== 'string') {
            data._missing.push('author')
        } else if (data.author.trim() === '') {
            data._problems.push('Author empty')
        }
        if (typeof data.copyright !== 'string') {
            data._missing.push('copyright')
        } else if (data.copyright.trim() === '') {
            data._problems.push('Copyright empty')
        }

        if (!Array.isArray(data.playOrder)) {
            data._missing.push('play order')
        } else if (data.playOrder.length === 0) {
            data._problems.push('Play order empty')
        } else if (
            typeof data.sections === 'object' &&
            !Array.isArray(data.sections) &&
            data.sections !== null
        ) {
            let missing = []

            for (let i = 0; i < data.playOrder.length; i++) {
                if (!data.sections.hasOwnProperty(data.playOrder[i])) {
                    missing.push(data.playOrder[i])
                }
            }

            if (missing.length > 0) {
                data._problems.push(
                    'Play order includes ' +
                        missing.length.toString() +
                        ' missing sections'
                )
            }
        }

        if (
            typeof data.sections !== 'object' ||
            Array.isArray(data.sections) ||
            data.sections === null
        ) {
            data._missing.push('sections')
        } else if (Object.keys(data.sections).length === 0) {
            data._problems.push('No sections')
        }

        if (typeof data.group !== 'string') {
            data._missing.push('group')
        }
        if (typeof data.groupID !== 'number') {
            data._missing.push('group ID')
        }

        return data
    }
})

const showImportantButton = new layout.Button({
    text: 'Important'
})
const showAllButton = new layout.Button({
    text: 'All'
})
const showFileButton = new layout.Button({
    text: 'Invalid Files'
})

const infoText = new layout.Text({}, { align: 'center' })

const songList = new layout.TableList(
    {
        columns: 4,
        columnWidths: ['13ch', '100%', '3ch', '5ch']
    },
    {
        margin: 4,

        border: true
    }
)

const fixMissingButton = new layout.Button({
    text: 'Fix Missing'
})

layout.change(layout.body, {
    direction: 'vertical',

    padding: 4
})

layout.body.add(
    new layout.Block(
        {
            childSpacing: 8,
            items: [
                showImportantButton,
                showAllButton,
                new layout.Text({ text: '│' }, { align: 'center' }),
                showFileButton,
                new layout.Text({ text: '│' }, { align: 'center' }),
                infoText
            ]
        },
        {
            direction: 'horizontal',
            padding: 0,

            shrink: false,
            grow: false
        }
    )
)
layout.body.add(songList)
layout.body.add(
    new layout.Block(
        {
            childSpacing: 8,
            items: [fixMissingButton]
        },
        {
            direction: 'horizontal',
            shrink: false,
            grow: false,

            padding: 0
        }
    )
)

const filters = {
    important: song => song._missing.length > 0,
    all: song => song._missing.length > 0 || song._problems.length > 0
}

const errorFiles = []

let searchFilter = filters.important

function updateList() {
    let errorSongs = Songs.list
        .filter(searchFilter)
        .sort((a, b) => a._ID - b._ID)

    songList.clear()

    for (let i = 0; i < errorSongs.length; i++) {
        if (errorSongs[i]._missing.length > 0) {
            songList.add([
                errorSongs[i]._filename,
                'Missing ' +
                    errorSongs[i]._missing.join(', ') +
                    (errorSongs[i]._problems.length > 0
                        ? '. Problems: ' +
                          errorSongs[i]._problems.join(', ') +
                          '.'
                        : '.'),
                errorSongs[i]._group,
                errorSongs[i]._ID
            ])
        } else {
            songList.add([
                errorSongs[i]._filename,
                'Problems: ' + errorSongs[i]._problems.join(', ') + '.',
                errorSongs[i]._group,
                errorSongs[i]._ID
            ])
        }
    }

    infoText.text =
        errorSongs.length.toString() +
        ' song' +
        (errorSongs.length === 1 ? '.' : 's.') +
        (errorSongs.length > 0 ? ' Click to edit' : '')
}

function showErrorList() {
    songList.clear()

    for (let i = 0; i < errorFiles.length; i++) {
        songList.add([
            errorFiles[i].filename,
            '(' + errorFiles[i].type + ') ' + errorFiles[i].error.message,
            errorFiles[i].group,
            errorFiles[i].ID
        ])
    }

    infoText.text =
        errorFiles.length.toString() +
        ' file' +
        (errorFiles.length === 1 ? '.' : 's.') +
        (errorFiles.length > 0 ? ' Click to resolve.' : '')
}

songList.onEvent('select', event => {
    if (showFileButton.active) {
        layout.dialog.showQuestion(
            {
                title: 'Resolve',
                message: 'Do you want to',
                options: ['Open', 'Delete', 'Cancel']
            },
            (error, choice) => {
                if (error) {
                    return false
                }

                if (choice === 'Open') {
                    let opened = shell.openItem(
                        path.join(Songs.directory, event.text[0])
                    )

                    if (!opened) {
                        layout.dialog.showNotification({
                            type: 'error',
                            message: "Couldn't open file!"
                        })
                    }
                } else if (choice === 'Delete') {
                    fs.unlink(
                        path.join(Songs.directory, event.text[0]),
                        error => {
                            let index = errorFiles.indexOf(
                                file => file.filename === event.text[0]
                            )

                            if (index !== -1) {
                                errorFiles.splice(index, 1)

                                showErrorList()
                            }

                            if (error) {
                                layout.dialog.showNotification({
                                    type: 'error',
                                    message:
                                        "Couldn't delete file!\n" +
                                        error.toString()
                                })

                                logger.error(
                                    "Couldn't delete invalid song library file (" +
                                        event.text[0] +
                                        '):',
                                    error
                                )
                            }
                        }
                    )
                }
            }
        )

        return
    }

    layout.window.openWindow('songDatabase', [
        'show-song',
        event.text[2],
        parseInt(event.text[3])
    ])
})

showImportantButton.onEvent('click', () => {
    showImportantButton.active = true
    showAllButton.active = false
    showFileButton.active = false

    searchFilter = filters.important

    updateList()
})
showAllButton.onEvent('click', () => {
    showImportantButton.active = false
    showAllButton.active = true
    showFileButton.active = false

    searchFilter = filters.all

    updateList()
})
showFileButton.onEvent('click', () => {
    showImportantButton.active = false
    showAllButton.active = false
    showFileButton.active = true

    showErrorList()
})

fixMissingButton.onEvent('click', () => {
    let missingList = Songs.list.filter(song => song._missing.length > 0)

    if (missingList.length === 0) {
        return
    }

    layout.showLoader(layout.body, 'Fixing')

    let fixNext = () => {
        if (missingList.length === 0) {
            layout.hideLoader(layout.body)

            return
        }

        let song = missingList.pop()

        delete song._missing
        delete song._problems

        if (typeof song.name !== 'string') {
            song.name = ''
        }
        if (typeof song.author !== 'string') {
            song.author = ''
        }
        if (typeof song.copyright !== 'string') {
            song.copyright = ''
        }

        if (!Array.isArray(song.playOrder)) {
            song.playOrder = []
        }

        if (
            typeof song.sections !== 'object' ||
            Array.isArray(song.sections) ||
            song.sections === null
        ) {
            song.sections = {}
        }

        if (typeof song.group !== 'string') {
            song.group = song._group
        }
        if (typeof song.groupID !== 'number') {
            song.groupID = song._ID
        }

        Songs.save(song._group, song._ID, song, fixNext)
    }

    fixNext()
})

Songs.onEvent('update', () => {
    updateList()

    fixMissingButton.disabled = !Songs.list.find(
        song => song._missing.length > 0
    )
})

Songs.onEvent('file-error', (error, file) => {
    let index = errorFiles.findIndex(errorFile => {
        return errorFile.filename === file.filename
    })

    if (index === -1) {
        file.error = error
        errorFiles[index].type = 'file'
        errorFiles.push(file)
    } else {
        errorFiles[index].type = 'file'
        errorFiles[index].error = error
    }
})
Songs.onEvent('transform-error', (error, file) => {
    let index = errorFiles.findIndex(errorFile => {
        return errorFile.filename === file.filename
    })

    if (index === -1) {
        file.error = error
        errorFiles[index].type = 'transform'
        errorFiles.push(file)
    } else {
        errorFiles[index].type = 'transform'
        errorFiles[index].error = error
    }
})

Songs.onEvent('error', error => {
    layout.dialog.showNotification({
        type: 'error',
        autoHide: false,

        message: 'There is an error with the Song database!\n' + error.message
    })
})

showImportantButton.active = true
<|endoftext|>