<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.boot',
  name: 'DAOConfigSummaryView',
  extends: 'foam.u2.Controller',

  documentation: 'Data Management UI for browsing all DAOs.',

  classes: [
    {
      name: 'DAOUpdateControllerView',
      extends: 'foam.comics.DAOUpdateControllerView',

      documentation: 'Same as regular UpdateController except it starts in EDIT mode',

      properties: [
        {
          name: 'controllerMode',
          factory: function() {
            return this.ControllerMode.EDIT;
          }
        }
      ]
    },

    {
      name: 'BackBorder',
      extends: 'foam.u2.Element',

      imports: [ 'memento', 'stack' ],

      css: `
        ^title {
          padding: 25px;
          font-size: 26px;
        }
        ^title a {
          color: blue;
          text-decoration: underline;
        }
      `,

      properties: [
        'title',
        {
          class: 'foam.u2.ViewSpec',
          name: 'inner'
        }
      ],

      methods: [
        function initE() {
          this.SUPER();

          this.
            start().
              addClass(this.myClass('title')).
              start('a').
                add('Data Management').on('click', () => this.memento = '').
              end().
              add(' / ', this.title).
            end().
            tag(this.inner);
        }
      ]
    }
  ],

  css: `
    ^ {
      padding: 6px;
    }
    ^dao, ^header {
      display: inline-block;
      font-size: smaller;
      margin: 2px;
      padding: 2px;
      width: 220px;
    }
    ^dao {
      color: #555;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    ^dao:hover {
      background: lightgray;
    }
    ^section {
      display: inline-grid;
    }
    ^header {
      background: /*%BLACK%*/ #1e1f21;
      color: white;
      font-weight: 800;
    }
  `,

  requires: [ 'foam.comics.BrowserView', 'foam.nanos.boot.NSpec' ],

  implements: [ 'foam.mlang.Expressions' ],

  imports: [ 'memento', 'nSpecDAO', 'stack' ],

  properties: [
    {
      name: 'data',
      factory: function() {
       return this.nSpecDAO;
      }
    },
    {
      name: 'filteredDAO',
      factory: function() {
       return this.data.where(
         this.AND(
           this.ENDS_WITH(foam.nanos.boot.NSpec.ID, 'DAO'),
           this.EQ(foam.nanos.boot.NSpec.SERVE,     true)
         ));
      }
    },
    {
      class: 'String',
      name: 'search',
      view: {
       class: 'foam.u2.TextField',
       type: 'search',
       onKey: true
      }
    }
  ],

  methods: [
    function initE() {
      this.SUPER();

      var self          = this;
      var currentLetter = '';
      var section;

      this.addClass(this.myClass()).
      start().
        style({ 'height': '56px'}).
        start().
          style({ 'font-size': '26px', 'width': 'fit-content', 'float': 'left', 'padding-top': '10px' }).
          add('Data Management').
        end()
        .start()
        .style({ 'width': 'fit-content', 'float': 'right', 'margin-right': '4%', 'margin-top': '6px' })
            .start(this.SEARCH).focus().end()
            .addClass('foam-u2-search-TextSearchView')
            .addClass(this.myClass('foam-u2-search-TextSearchView'))
          .end()
        .end()
      .end();

      var updateSections = [];
      var i = 0;

      this.filteredDAO.select().then(function(specs) {
        specs.array.sort(function(o1, o2) { return foam.String.compare(o1.id.toUpperCase(), o2.id.toUpperCase())}).forEach(function(spec) {
          var label = foam.String.capitalize(spec.id.substring(0, spec.id.length-3));
          var l     = label.charAt(0);

          if ( l != currentLetter ) {
            let lSection;
            let showSection = foam.core.SimpleSlot.create({value: true});
            i = updateSections.length;

            var updateSlot = foam.core.SimpleSlot.create({value: false});
            updateSections.push(updateSlot);

            updateSections[i].sub(function() {
              // first child is a header
              for ( var j = 1 ; j < lSection.childNodes.length ; j++ ) {
                if ( lSection.childNodes[j].shown ) {
                  showSection.set(true);
                  return;
                }
              }
              showSection.set(false);
            });

            currentLetter = l;

            section = self.start('span')
              .show(showSection)
              .addClass(self.myClass('section'))
              .start('span')
                .addClass(self.myClass('header'))
                .add(l)
              .end();

            lSection = section;
          }

          var localI    = i.valueOf();
          var localShow = foam.core.SimpleSlot.create({value: true});

          section
            .start('span')
              .show(localShow)
              .addClass(self.myClass('dao'))
              .add(label)
              .attrs({title: spec.description})
              .on('click', function() { self.memento = spec.id; });

              self.search$.sub(function() {
                var contains = false;
                if ( ! self.search ) {
                  contains = true;
                } else if ( label.toLowerCase().includes(self.search.toLowerCase()) ) {
                  contains =  true;
                } else if ( ! contains && spec.keywords && spec.keywords.length > 0 ) {
                  for ( var k in spec.keywords ) {
                    if ( k.toLowerCase().includes(self.search.toLowerCase()) ) {
                      contains  = true;
                      break;
                    }
                  }
                }

                localShow.set(contains);
                updateSections[localI].set(! updateSections[localI].get());
              });
        });
      });

      this.onDetach(this.memento$.sub(this.mementoChange));
      this.mementoChange();
    }
  ],

  listeners: [
    function mementoChange() {
      var m = this.memento;
      console.log('****************************** mementoChange',m);

      return;

      /*if ( this.stack.depth > 1 )*/ this.stack.back();

      if ( ! m ) return;

      var x = this.__subContext__.createSubContext();
      x.register(this.DAOUpdateControllerView, 'foam.comics.DAOUpdateControllerView');

      this.stack.push({
        class: this.BackBorder,
        title: m,
        inner: {
          class: this.BrowserView,
          data: this.__context__[m],
          stack: this.stack
        }
      }, x);
    }
  ]
});
<commit_msg>Permanent fix for DataManagement menu.<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.boot',
  name: 'DAOConfigSummaryView',
  extends: 'foam.u2.Controller',

  documentation: 'Data Management UI for browsing all DAOs.',

  classes: [
    {
      name: 'DAOUpdateControllerView',
      extends: 'foam.comics.DAOUpdateControllerView',

      documentation: 'Same as regular UpdateController except it starts in EDIT mode',

      properties: [
        {
          name: 'controllerMode',
          factory: function() {
            return this.ControllerMode.EDIT;
          }
        }
      ]
    },

    {
      name: 'BackBorder',
      extends: 'foam.u2.Element',

      imports: [ 'memento', 'stack' ],

      css: `
        ^title {
          padding: 25px;
          font-size: 26px;
        }
        ^title a {
          color: blue;
          text-decoration: underline;
        }
      `,

      properties: [
        'title',
        {
          class: 'foam.u2.ViewSpec',
          name: 'inner'
        }
      ],

      methods: [
        function initE() {
          this.SUPER();

          this.
            start().
              addClass(this.myClass('title')).
              start('a').
                add('Data Management').on('click', () => { this.memento = ''; }).
              end().
              add(' / ', this.title).
            end().
            tag(this.inner);
        }
      ]
    }
  ],

  css: `
    ^ {
      padding: 6px;
    }
    ^dao, ^header {
      display: inline-block;
      font-size: smaller;
      margin: 2px;
      padding: 2px;
      width: 220px;
    }
    ^dao {
      color: #555;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    ^dao:hover {
      background: lightgray;
    }
    ^section {
      display: inline-grid;
    }
    ^header {
      background: /*%BLACK%*/ #1e1f21;
      color: white;
      font-weight: 800;
    }
  `,

  requires: [ 'foam.comics.BrowserView', 'foam.nanos.boot.NSpec' ],

  implements: [ 'foam.mlang.Expressions' ],

  imports: [ 'memento', 'nSpecDAO', 'stack' ],

  properties: [
    {
      name: 'data',
      factory: function() {
       return this.nSpecDAO;
      }
    },
    {
      name: 'filteredDAO',
      factory: function() {
       return this.data.where(
         this.AND(
           this.ENDS_WITH(foam.nanos.boot.NSpec.ID, 'DAO'),
           this.EQ(foam.nanos.boot.NSpec.SERVE,     true)
         ));
      }
    },
    {
      class: 'String',
      name: 'search',
      view: {
       class: 'foam.u2.TextField',
       type: 'search',
       onKey: true
      }
    },
    'currentMemento_'
  ],

  methods: [
    function initE() {
      this.SUPER();

      var self          = this;
      var currentLetter = '';
      var section;

      this.addClass(this.myClass()).
      start().
        style({ 'height': '56px'}).
        start().
          style({ 'font-size': '26px', 'width': 'fit-content', 'float': 'left', 'padding-top': '10px' }).
          add('Data Management').
        end()
        .start()
        .style({ 'width': 'fit-content', 'float': 'right', 'margin-right': '4%', 'margin-top': '6px' })
            .start(this.SEARCH).focus().end()
            .addClass('foam-u2-search-TextSearchView')
            .addClass(this.myClass('foam-u2-search-TextSearchView'))
          .end()
        .end()
      .end();

      var updateSections = [];
      var i = 0;

      this.filteredDAO.select().then(function(specs) {
        specs.array.sort(function(o1, o2) { return foam.String.compare(o1.id.toUpperCase(), o2.id.toUpperCase())}).forEach(function(spec) {
          var label = foam.String.capitalize(spec.id.substring(0, spec.id.length-3));
          var l     = label.charAt(0);

          if ( l != currentLetter ) {
            let lSection;
            let showSection = foam.core.SimpleSlot.create({value: true});
            i = updateSections.length;

            var updateSlot = foam.core.SimpleSlot.create({value: false});
            updateSections.push(updateSlot);

            updateSections[i].sub(function() {
              // first child is a header
              for ( var j = 1 ; j < lSection.childNodes.length ; j++ ) {
                if ( lSection.childNodes[j].shown ) {
                  showSection.set(true);
                  return;
                }
              }
              showSection.set(false);
            });

            currentLetter = l;

            section = self.start('span')
              .show(showSection)
              .addClass(self.myClass('section'))
              .start('span')
                .addClass(self.myClass('header'))
                .add(l)
              .end();

            lSection = section;
          }

          var localI    = i.valueOf();
          var localShow = foam.core.SimpleSlot.create({value: true});

          section
            .start('span')
              .show(localShow)
              .addClass(self.myClass('dao'))
              .add(label)
              .attrs({title: spec.description})
              .on('click', function() { self.memento = spec.id; });

              self.search$.sub(function() {
                var contains = false;
                if ( ! self.search ) {
                  contains = true;
                } else if ( label.toLowerCase().includes(self.search.toLowerCase()) ) {
                  contains =  true;
                } else if ( ! contains && spec.keywords && spec.keywords.length > 0 ) {
                  for ( var k in spec.keywords ) {
                    if ( k.toLowerCase().includes(self.search.toLowerCase()) ) {
                      contains  = true;
                      break;
                    }
                  }
                }

                localShow.set(contains);
                updateSections[localI].set(! updateSections[localI].get());
              });
        });
      });

//      this.onDetach(this.memento$.sub(this.mementoChange));
      this.memento$.sub(this.mementoChange);
      this.mementoChange();
    }
  ],

  listeners: [
    function mementoChange() {
      var m = this.memento;

      if ( ! m ) {
        if ( this.currentMemento_ ) this.stack.back();
        this.currentMemento_ = '';
        return;
      }

      this.currentMemento_ = m;

      var x = this.__subContext__.createSubContext();
      x.register(this.DAOUpdateControllerView, 'foam.comics.DAOUpdateControllerView');

      this.stack.push({
        class: this.BackBorder,
        title: m,
        inner: {
          class: this.BrowserView,
          data: this.__context__[m],
          stack: this.stack
        }
      }, x);
    }
  ]
});
<|endoftext|>


<commit_before>// Most Simple - No error Handling app. Just for demo
var express = require('express');
var jsonParser = require('./json-parser.js')

var app = express();
app.use(jsonParser())

app.get('*', function (req, resp) {
	resp.status(403).send('Not Implemented')
})

app.post('/Plugin.Activate', function(req, resp) {
	resp.json( {Implements: ["VolumeDriver"] }).end()
})

app.post('/VolumeDriver.Create', function(req, resp) {
	resp.json( {Err: null }).end()
})

app.post('/VolumeDriver.Remove', function(req, resp) {
	resp.json( {Err: null }).end()
})

app.post('/VolumeDriver.Mount', function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

app.post('/VolumeDriver.Path', jsonParser, function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

app.post('/VolumeDriver.Unmount', jsonParser, function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

var server = app.listen(7010, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("Docker Volume Plugin helloworld listening at http://%s:%s", host, port)
})
<commit_msg>removed unneeded json parser<commit_after>// Most Simple - No error Handling app. Just for demo
var express = require('express');
var jsonParser = require('./json-parser.js')

var app = express();
app.use(jsonParser())

app.get('*', function (req, resp) {
	resp.status(403).send('Not Implemented')
})

app.post('/Plugin.Activate', function(req, resp) {
	resp.json( {Implements: ["VolumeDriver"] }).end()
})

app.post('/VolumeDriver.Create', function(req, resp) {
	resp.json( {Err: null }).end()
})

app.post('/VolumeDriver.Remove', function(req, resp) {
	resp.json( {Err: null }).end()
})

app.post('/VolumeDriver.Mount', function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

app.post('/VolumeDriver.Path', function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

app.post('/VolumeDriver.Unmount', function(req, resp) {
	resp.json( {MountPoint: "/tmp/mnt/" + req.body.Name, Err: null}).end()
})

var server = app.listen(7010, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("Docker Volume Plugin helloworld listening at http://%s:%s", host, port)
})
<|endoftext|>


<commit_before>import '6to5-core/polyfill'
import _ from 'lodash'
import SISData from './sis-degreeaudit.json'
import 'whatwg-fetch'
import Promise from 'bluebird'
import db from '../app/helpers/db'
import loadData from '../app/helpers/loadData'
import comb from '../app/helpers/combos'
import queryCourses from 'sto-helpers/lib/queryCourses'
import {checkScheduleTimeConflicts} from 'sto-sis-time-parser'

loadData()

function tableToJson(table) {
	// from http://johndyer.name/html-table-to-json/
	var data = []; // first row needs to be headers
	var headers = [];
	for (var i=0; i < table.rows[0].cells.length; i++) {
		headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi,'');
	}
	// go through cells
	for (var i=1; i<table.rows.length; i++) {
		var tableRow = table.rows[i];
		var rowData = {};
		for (var j=0; j < tableRow.cells.length; j++) {
			rowData[ headers[j] ] = tableRow.cells[j].innerHTML;
		}
		data.push(rowData);
	}
	return data;
}

let unescapeAllValues = (obj) => _[_.isArray(obj) ? 'map' : 'mapValues'](obj, (value) => {
	if (_.isString(value))
		return _.unescape(value)
	else if (_.isArray(value))
		return unescapeAllValues(value)
	return value
})

function prettyifyCourses(jsonCourses) {
	return _.map(jsonCourses, function(course) {
		delete course[' ']
		delete course.inst

		course.credit = course.credit.replace(/\((.*)\)/, '$1')
		course.credit = parseFloat(course.credit)
		course.credits = course.credit
		delete course.credit

	 	if (course['g.e.'])
			course['g.e.'] = course['g.e.'].trim()
		delete course.gereqs

		course.num = parseInt(course.no, 10)
		delete course.no

		course.sem = parseInt(course.term, 10)
		course.year = parseInt(course.year, 10)
		course.term = parseInt(String(course.year) + String(course.sem), 10)

		course.deptnum = course.dept + ' ' + course.num
		course.level = course.level * 100
		course.depts = course.dept.split('/')

		if (course['g.e.'] && course['g.e.'].length)
			course.gereqs = course['g.e.'].split(' ')
		delete course['g.e.']

		course.name = course.title.trim()
		delete course.title

		delete course.sts // status is In-Progress or blank

		// console.log(JSON.stringify(course))
		course = unescapeAllValues(course)
		// console.log(JSON.stringify(course))

		return course
	})
}

function prepareCourseForQuery(course) {
	course = _.cloneDeep(course)

	delete course.type
	delete course.grade

	course = _.mapValues(course, (value, key) => {
		// console.log(key, value)
		return _.isArray(value) ? value : [value]
	})

	return course
}

function parseHTMLfromJSON() {
	var parser = new DOMParser();
	var html = parser.parseFromString(SISData.sis, 'text/html');
	return parseSIS(html)
}

function parseHTMLfromPage() {
	var html = document.querySelector('#sis')
	return parseSIS(html)
}

function cleanUpPage(html) {
	// Remove the topnav
	var topNav = html.querySelector('.topnav');
	topNav.parentNode.removeChild(topNav);

	var sidebar = html.querySelector('table td:nth-child(1)')
	sidebar.parentNode.removeChild(sidebar);

	// Delve deep within the table structure
	var main = html.querySelector('table td')

	// Get all tables.
	var tonsOfTables = main.querySelectorAll('table');
	// Remove the breadcrumb bar
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);
	// Refresh the lsit of tables
	tonsOfTables = main.querySelectorAll('table');
	// Remove the "Degree Audit(s)" header
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);
	// Refresh the list of tables
	tonsOfTables = main.querySelectorAll('table');
	// Remove the "B.{A,M}." header
	let degreeType = tonsOfTables[0].textContent.trim().split(' ')[0]
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);

	// Now to remove the parent tables
	var tables = Array.prototype.slice.call(main.querySelectorAll('table'));

	// Remove the first parent table
	tables.splice(0, 1)
	// Remove the General Graduation Requirements parent table
	tables.splice(2, 1)
	// Remove the info table and count of s/u courses
	tables.splice(3, 2)

	return [tables, degreeType]
}

function nameTheTables(tables) {
	return {
		info: tables[0],
		areas: tables[1],
		grad: tables[2],
		gened: tables[3],
		courses: tables[4],
	}
}

function processInfoTable(infoTable) {
	var info = {}
	_.each(infoTable.rows, (row) => {
		var rowName = row.cells[0].textContent.replace(/:$/, '').toLowerCase();
		info[rowName] = row.cells[1].textContent;
	})

	info.graduation = parseInt(info['class year'], 10)
	delete info['class year']
	info.matriculation = parseInt(info['curriculum year'], 10)
	delete info['curriculum year']
	info.standing = info['academic standing']
	delete info['academic standing']

	return info
}

function processCoursesTable(coursesTable) {
	var jsonRepresentation = tableToJson(coursesTable)
	jsonRepresentation.pop() // remove the extra row at the end

	// loop to query each of the ugly
	var coursesArr = prettyifyCourses(jsonRepresentation)

	return coursesArr
}

function processGradTable(gradTable) {
	var jsonRepresentation = tableToJson(gradTable)
	let result = {}
	_.each(jsonRepresentation, (value, key) => {
		if (key === '') {
			key = key.replace(' ', ' ')
			result[key] = {}
		}
	})
	console.log('grad table', jsonRepresentation)
	// return courses
}

function processAreaTable(areaTable) {
	var jsonRepresentation = tableToJson(areaTable)
	let majors = _.chain(jsonRepresentation).pluck('majors').compact().value()
	let concentrations = _.chain(jsonRepresentation).pluck('concentrations').compact().value()
	let emphases = _.chain(jsonRepresentation).pluck('emphases').compact().value()
	return {majors, concentrations, emphases}
}

function lookupAreaAbbrFromTitle(title) {
	let lookup = {
		"AFRICA AND THE AMERICAS": 'AFAM',
		"ALTERNATE LANGUAGE STUDY OPTION": 'ALSO',
		"AMERICAN CONVERSATION": 'AMCON',
		"AMERICAN STUDIES": 'AMST',
		"AMERICAN RACIAL AND MULTICULTURAL STUDIES": 'ARMS',
		"ART AND ART HISTORY": 'ART',
		"ASIAN STUDIES": 'ASIAN',
		"BIOLOGY": 'BIO',
		"BIOMOLECULAR SCIENCE": 'BMOLS',
		"CHEMISTRY": 'CHEM',
		"CHINESE": 'CHIN',
		"CLASSICS": 'CLASS',
		"COMPUTER SCIENCE": 'CSCI',
		"DANCE": 'DANCE',
		"ECONOMICS": 'ECON',
		"EDUCATION": 'EDUC',
		"ENGLISH": 'ENGL',
		"ENVIRONMENTAL STUDIES": 'ENVST',
		"EXERCISE SCIENCE ACTIVITY": 'ESAC',
		"EXERCISE SCIENCE THEORY": 'ESTH',
		"FAMILY STUDIES": 'FAMST',
		"FILM STUDIES": 'FILM',
		"FRENCH": 'FREN',
		"GREAT CONVERSATION": 'GCON',
		"GERMAN": 'GERM',
		"GREEK": 'GREEK',
		"HISTORY": 'HIST',
		"HISPANIC STUDIES": 'HSPST',
		"UNKNOWN (IDFA)": 'IDFA',
		"INTERDEPARTMENTAL": 'INTD',
		"INTERDISCIPLINARY": 'INTER',
		"INTEGRATIVE STUDIES": 'IS',
		"JAPANESE": 'JAPAN',
		"LATIN": 'LATIN',
		"MATHEMATICS": 'MATH',
		"MEDIA STUDIES": 'MEDIA',
		"MEDIEVAL STUDIES": 'MEDVL',
		"MANAGEMENT STUDIES": 'MGMT',
		"MUSIC": 'MUSIC',
		"MUSIC PERFORMANCE": 'MUSPF',
		"NEUROSCIENCE": 'NEURO',
		"NORWEGIAN": 'NORW',
		"NURSING": 'NURS',
		"PHILOSPHY": 'PHIL',
		"PHYSICS": 'PHYS',
		"POLITICAL SCIENCE": 'PSCI',
		"PSYCHOLOGY": 'PSYCH',
		"RELIGION": 'REL',
		"RUSSIAN STUDIES": 'RUSST',
		"RUSSIAN": 'RUSSN',
		"SCIENCE CONVERSATION": 'SCICN',
		"SOCIOLOGY AND ANTHROPOLOGY": 'SOAN',
		"SPANISH": 'SPAN',
		"STATISTICS": 'STAT',
		"SOCIAL WORK": 'SWRK',
		"THEATER": 'THEAT',
		"WOMEN'S STUDIES": 'WMGST',
		"WOMEN'S & GENDER STUDIES": 'WMGST',
		"WRITING": 'WRIT',
	}
	var result = lookup[title.toUpperCase()]
	if (!result)
		return "UNKWN";
	return result
}

function findAreasOfStudy(areas, degreeType) {
	let areasOfStudy = []

	let degree = {type: 'degree'}
	if (degreeType === 'B.A.') {
		degree.id = 'd-ba'
		degree.abbr = 'B.A.'
		degree.title = 'Bachelor of Arts'
	} else if (degreeType === 'B.M.') {
		degree.id = 'd-bm'
		degree.abbr = 'B.M.'
		degree.title = 'Bachelor of Music'
	}
	areasOfStudy.push(degree)

	_.each(areas.majors, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'm-' + abbr.toLowerCase(),
			type: 'major', abbr: abbr, title: area,
		})
	})

	_.each(areas.concentrations, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'c-' + abbr.toLowerCase(),
			type: 'concentration', abbr: abbr, title: area,
		})
	})

	_.each(areas.emphases, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'e-' + abbr.toLowerCase(),
			type: 'emphasis', abbr: abbr, title: area,
		})
	})

	return areasOfStudy
}

function createSchedules(courses) {
	let terms = _.chain(courses).pluck('term').uniq().value()
	let grouped = _.groupBy(courses, 'term')
	let id = 1;
	let schedules = _.map(terms, (t) => {
		let sched = {
			id: id++,
			year: Math.floor(t / 10),
			sem: t % 10,
			title: 'Schedule 1',
			index: 1,
			active: true,
			clbids: [],
			raw_courses: grouped[t]
		}
		_.each(sched.courses, (course, i) => {
			// attempt 1
			let attemptToFindCourse = queryCourses({name: course.name, term: course.term, deptnum: course.deptnum})
			if (attemptToFindCourse) {
				clbids.push(attemptToFindCourse.clbid)
				delete
			}
		})
		return sched
	})
	console.log(schedules)
}

function makeStudent(tables, degreeType) {
	let student = {}

	student.name = tables.info.name
	student.advisor = tables.info.advisor
	student.matriculation = tables.info.matriculation
	student.graduation = tables.info.graduation

	student.studies = findAreasOfStudy(tables.areas, degreeType)
	student.schedules = createSchedules(tables.courses)

	return student
}

function parseSIS(html) {
	let [rawTables, degreeType] = cleanUpPage(html)
	var tables = nameTheTables(rawTables)
	console.log(tables)

	var cleanedTables = {
		courses: processCoursesTable(tables.courses),
		info: processInfoTable(tables.info),
		areas: processAreaTable(tables.areas),
	}

	console.log('cleaned', cleanedTables)
	let student = makeStudent(cleanedTables, degreeType)
	console.log('student', student)

	return student
}

parseHTMLfromJSON()

document.querySelector('button').addEventListener('click', function(ev) {
	parseHTMLfromPage()
})
<commit_msg>Hide a log<commit_after>import '6to5-core/polyfill'
import _ from 'lodash'
import SISData from './sis-degreeaudit.json'
import 'whatwg-fetch'
import Promise from 'bluebird'
import db from '../app/helpers/db'
import loadData from '../app/helpers/loadData'
import comb from '../app/helpers/combos'
import queryCourses from 'sto-helpers/lib/queryCourses'
import {checkScheduleTimeConflicts} from 'sto-sis-time-parser'

loadData()

function tableToJson(table) {
	// from http://johndyer.name/html-table-to-json/
	var data = []; // first row needs to be headers
	var headers = [];
	for (var i=0; i < table.rows[0].cells.length; i++) {
		headers[i] = table.rows[0].cells[i].innerHTML.toLowerCase().replace(/ /gi,'');
	}
	// go through cells
	for (var i=1; i<table.rows.length; i++) {
		var tableRow = table.rows[i];
		var rowData = {};
		for (var j=0; j < tableRow.cells.length; j++) {
			rowData[ headers[j] ] = tableRow.cells[j].innerHTML;
		}
		data.push(rowData);
	}
	return data;
}

let unescapeAllValues = (obj) => _[_.isArray(obj) ? 'map' : 'mapValues'](obj, (value) => {
	if (_.isString(value))
		return _.unescape(value)
	else if (_.isArray(value))
		return unescapeAllValues(value)
	return value
})

function prettyifyCourses(jsonCourses) {
	return _.map(jsonCourses, function(course) {
		delete course[' ']
		delete course.inst

		course.credit = course.credit.replace(/\((.*)\)/, '$1')
		course.credit = parseFloat(course.credit)
		course.credits = course.credit
		delete course.credit

	 	if (course['g.e.'])
			course['g.e.'] = course['g.e.'].trim()
		delete course.gereqs

		course.num = parseInt(course.no, 10)
		delete course.no

		course.sem = parseInt(course.term, 10)
		course.year = parseInt(course.year, 10)
		course.term = parseInt(String(course.year) + String(course.sem), 10)

		course.deptnum = course.dept + ' ' + course.num
		course.level = course.level * 100
		course.depts = course.dept.split('/')

		if (course['g.e.'] && course['g.e.'].length)
			course.gereqs = course['g.e.'].split(' ')
		delete course['g.e.']

		course.name = course.title.trim()
		delete course.title

		delete course.sts // status is In-Progress or blank

		// console.log(JSON.stringify(course))
		course = unescapeAllValues(course)
		// console.log(JSON.stringify(course))

		return course
	})
}

function prepareCourseForQuery(course) {
	course = _.cloneDeep(course)

	delete course.type
	delete course.grade

	course = _.mapValues(course, (value, key) => {
		// console.log(key, value)
		return _.isArray(value) ? value : [value]
	})

	return course
}

function parseHTMLfromJSON() {
	var parser = new DOMParser();
	var html = parser.parseFromString(SISData.sis, 'text/html');
	return parseSIS(html)
}

function parseHTMLfromPage() {
	var html = document.querySelector('#sis')
	return parseSIS(html)
}

function cleanUpPage(html) {
	// Remove the topnav
	var topNav = html.querySelector('.topnav');
	topNav.parentNode.removeChild(topNav);

	var sidebar = html.querySelector('table td:nth-child(1)')
	sidebar.parentNode.removeChild(sidebar);

	// Delve deep within the table structure
	var main = html.querySelector('table td')

	// Get all tables.
	var tonsOfTables = main.querySelectorAll('table');
	// Remove the breadcrumb bar
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);
	// Refresh the lsit of tables
	tonsOfTables = main.querySelectorAll('table');
	// Remove the "Degree Audit(s)" header
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);
	// Refresh the list of tables
	tonsOfTables = main.querySelectorAll('table');
	// Remove the "B.{A,M}." header
	let degreeType = tonsOfTables[0].textContent.trim().split(' ')[0]
	tonsOfTables[0].parentNode.removeChild(tonsOfTables[0]);

	// Now to remove the parent tables
	var tables = Array.prototype.slice.call(main.querySelectorAll('table'));

	// Remove the first parent table
	tables.splice(0, 1)
	// Remove the General Graduation Requirements parent table
	tables.splice(2, 1)
	// Remove the info table and count of s/u courses
	tables.splice(3, 2)

	return [tables, degreeType]
}

function nameTheTables(tables) {
	return {
		info: tables[0],
		areas: tables[1],
		grad: tables[2],
		gened: tables[3],
		courses: tables[4],
	}
}

function processInfoTable(infoTable) {
	var info = {}
	_.each(infoTable.rows, (row) => {
		var rowName = row.cells[0].textContent.replace(/:$/, '').toLowerCase();
		info[rowName] = row.cells[1].textContent;
	})

	info.graduation = parseInt(info['class year'], 10)
	delete info['class year']
	info.matriculation = parseInt(info['curriculum year'], 10)
	delete info['curriculum year']
	info.standing = info['academic standing']
	delete info['academic standing']

	return info
}

function processCoursesTable(coursesTable) {
	var jsonRepresentation = tableToJson(coursesTable)
	jsonRepresentation.pop() // remove the extra row at the end

	// loop to query each of the ugly
	var coursesArr = prettyifyCourses(jsonRepresentation)

	return coursesArr
}

function processGradTable(gradTable) {
	var jsonRepresentation = tableToJson(gradTable)
	let result = {}
	_.each(jsonRepresentation, (value, key) => {
		if (key === '') {
			key = key.replace(' ', ' ')
			result[key] = {}
		}
	})
	//console.log('grad table', jsonRepresentation)
	// return courses
}

function processAreaTable(areaTable) {
	var jsonRepresentation = tableToJson(areaTable)
	let majors = _.chain(jsonRepresentation).pluck('majors').compact().value()
	let concentrations = _.chain(jsonRepresentation).pluck('concentrations').compact().value()
	let emphases = _.chain(jsonRepresentation).pluck('emphases').compact().value()
	return {majors, concentrations, emphases}
}

function lookupAreaAbbrFromTitle(title) {
	let lookup = {
		"AFRICA AND THE AMERICAS": 'AFAM',
		"ALTERNATE LANGUAGE STUDY OPTION": 'ALSO',
		"AMERICAN CONVERSATION": 'AMCON',
		"AMERICAN STUDIES": 'AMST',
		"AMERICAN RACIAL AND MULTICULTURAL STUDIES": 'ARMS',
		"ART AND ART HISTORY": 'ART',
		"ASIAN STUDIES": 'ASIAN',
		"BIOLOGY": 'BIO',
		"BIOMOLECULAR SCIENCE": 'BMOLS',
		"CHEMISTRY": 'CHEM',
		"CHINESE": 'CHIN',
		"CLASSICS": 'CLASS',
		"COMPUTER SCIENCE": 'CSCI',
		"DANCE": 'DANCE',
		"ECONOMICS": 'ECON',
		"EDUCATION": 'EDUC',
		"ENGLISH": 'ENGL',
		"ENVIRONMENTAL STUDIES": 'ENVST',
		"EXERCISE SCIENCE ACTIVITY": 'ESAC',
		"EXERCISE SCIENCE THEORY": 'ESTH',
		"FAMILY STUDIES": 'FAMST',
		"FILM STUDIES": 'FILM',
		"FRENCH": 'FREN',
		"GREAT CONVERSATION": 'GCON',
		"GERMAN": 'GERM',
		"GREEK": 'GREEK',
		"HISTORY": 'HIST',
		"HISPANIC STUDIES": 'HSPST',
		"UNKNOWN (IDFA)": 'IDFA',
		"INTERDEPARTMENTAL": 'INTD',
		"INTERDISCIPLINARY": 'INTER',
		"INTEGRATIVE STUDIES": 'IS',
		"JAPANESE": 'JAPAN',
		"LATIN": 'LATIN',
		"MATHEMATICS": 'MATH',
		"MEDIA STUDIES": 'MEDIA',
		"MEDIEVAL STUDIES": 'MEDVL',
		"MANAGEMENT STUDIES": 'MGMT',
		"MUSIC": 'MUSIC',
		"MUSIC PERFORMANCE": 'MUSPF',
		"NEUROSCIENCE": 'NEURO',
		"NORWEGIAN": 'NORW',
		"NURSING": 'NURS',
		"PHILOSPHY": 'PHIL',
		"PHYSICS": 'PHYS',
		"POLITICAL SCIENCE": 'PSCI',
		"PSYCHOLOGY": 'PSYCH',
		"RELIGION": 'REL',
		"RUSSIAN STUDIES": 'RUSST',
		"RUSSIAN": 'RUSSN',
		"SCIENCE CONVERSATION": 'SCICN',
		"SOCIOLOGY AND ANTHROPOLOGY": 'SOAN',
		"SPANISH": 'SPAN',
		"STATISTICS": 'STAT',
		"SOCIAL WORK": 'SWRK',
		"THEATER": 'THEAT',
		"WOMEN'S STUDIES": 'WMGST',
		"WOMEN'S & GENDER STUDIES": 'WMGST',
		"WRITING": 'WRIT',
	}
	var result = lookup[title.toUpperCase()]
	if (!result)
		return "UNKWN";
	return result
}

function findAreasOfStudy(areas, degreeType) {
	let areasOfStudy = []

	let degree = {type: 'degree'}
	if (degreeType === 'B.A.') {
		degree.id = 'd-ba'
		degree.abbr = 'B.A.'
		degree.title = 'Bachelor of Arts'
	} else if (degreeType === 'B.M.') {
		degree.id = 'd-bm'
		degree.abbr = 'B.M.'
		degree.title = 'Bachelor of Music'
	}
	areasOfStudy.push(degree)

	_.each(areas.majors, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'm-' + abbr.toLowerCase(),
			type: 'major', abbr: abbr, title: area,
		})
	})

	_.each(areas.concentrations, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'c-' + abbr.toLowerCase(),
			type: 'concentration', abbr: abbr, title: area,
		})
	})

	_.each(areas.emphases, (area) => {
		let abbr = lookupAreaAbbrFromTitle(area)
		areasOfStudy.push({
			id: 'e-' + abbr.toLowerCase(),
			type: 'emphasis', abbr: abbr, title: area,
		})
	})

	return areasOfStudy
}

function createSchedules(courses) {
	let terms = _.chain(courses).pluck('term').uniq().value()
	let grouped = _.groupBy(courses, 'term')
	let id = 1;
	let schedules = _.map(terms, (t) => {
		let sched = {
			id: id++,
			year: Math.floor(t / 10),
			sem: t % 10,
			title: 'Schedule 1',
			index: 1,
			active: true,
			clbids: [],
			raw_courses: grouped[t]
		}
		_.each(sched.courses, (course, i) => {
			// attempt 1
			let attemptToFindCourse = queryCourses({name: course.name, term: course.term, deptnum: course.deptnum})
			if (attemptToFindCourse) {
				clbids.push(attemptToFindCourse.clbid)
				delete
			}
		})
		return sched
	})
	console.log(schedules)
}

function makeStudent(tables, degreeType) {
	let student = {}

	student.name = tables.info.name
	student.advisor = tables.info.advisor
	student.matriculation = tables.info.matriculation
	student.graduation = tables.info.graduation

	student.studies = findAreasOfStudy(tables.areas, degreeType)
	student.schedules = createSchedules(tables.courses)

	return student
}

function parseSIS(html) {
	let [rawTables, degreeType] = cleanUpPage(html)
	var tables = nameTheTables(rawTables)
	console.log(tables)

	var cleanedTables = {
		courses: processCoursesTable(tables.courses),
		info: processInfoTable(tables.info),
		areas: processAreaTable(tables.areas),
	}

	console.log('cleaned', cleanedTables)
	let student = makeStudent(cleanedTables, degreeType)
	console.log('student', student)

	return student
}

parseHTMLfromJSON()

document.querySelector('button').addEventListener('click', function(ev) {
	parseHTMLfromPage()
})
<|endoftext|>


<commit_before>/**
 * jQuery JSON plugin 2.4-alpha
 *
 * @author Brantley Harris, 2009-2011
 * @author Timo Tijhof, 2011-2012
 * @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
 *         copyrighted 2005 by Bob Ippolito.
 * @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
 *         website's http://www.json.org/json2.js, which proclaims:
 *         "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 *         I uphold.
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
 */
/*global jQuery */
/*jslint sloppy: true, continue: true, plusplus: true */
(function ($) {
	var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
		meta = {
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'"' : '\\"',
			'\\': '\\\\'
		},
		hasOwn = Object.prototype.hasOwnProperty;

	/**
	 * jQuery.toJSON
	 * Converts the given argument into a JSON respresentation.
	 *
	 * @param o {Mixed} The json-serializble *thing* to be converted
	 *
	 * If an object has a toJSON prototype, that will be used to get the representation.
	 * Non-integer/string keys are skipped in the object, as are keys that point to a
	 * function.
	 *
	 */
	$.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
		if (o === null) {
			return 'null';
		}

		var pairs, k, name, val,
			type = $.type(o);

		if (type === 'undefined') {
			return undefined;
		}

		// Also covers instantiated Number and Boolean objects,
		// which are typeof 'object' but thanks to $.type, we
		// catch them here. I don't know whether it is right
		// or wrong that instantiated primitives are not
		// exported to JSON as an {"object":..}.
		// We choose this path because that's what the browsers did.
		if (type === 'number' || type === 'boolean') {
			return String(o);
		}
		if (type === 'string') {
			return $.quoteString(o);
		}
		if (typeof o.toJSON === 'function') {
			return $.toJSON(o.toJSON());
		}
		if (type === 'date') {
			var month = o.getUTCMonth() + 1,
				day = o.getUTCDate(),
				year = o.getUTCFullYear(),
				hours = o.getUTCHours(),
				minutes = o.getUTCMinutes(),
				seconds = o.getUTCSeconds(),
				milli = o.getUTCMilliseconds();

			if (month < 10) {
				month = '0' + month;
			}
			if (day < 10) {
				day = '0' + day;
			}
			if (hours < 10) {
				hours = '0' + hours;
			}
			if (minutes < 10) {
				minutes = '0' + minutes;
			}
			if (seconds < 10) {
				seconds = '0' + seconds;
			}
			if (milli < 100) {
				milli = '0' + milli;
			}
			if (milli < 10) {
				milli = '0' + milli;
			}
			return '"' + year + '-' + month + '-' + day + 'T' +
				hours + ':' + minutes + ':' + seconds +
				'.' + milli + 'Z"';
		}

		pairs = [];

		if ($.isArray(o)) {
			for (k = 0; k < o.length; k++) {
				pairs.push($.toJSON(o[k]) || 'null');
			}
			return '[' + pairs.join(',') + ']';
		}

		// Any other object (plain object, RegExp, ..)
		// Need to do typeof instead of $.type, because we also
		// want to catch non-plain objects.
		if (typeof o === 'object') {
			for (k in o) {
				// Only include own properties,
				// Filter out inherited prototypes
				if (hasOwn.call(o, k)) {
					// Keys must be numerical or string. Skip others
					type = typeof k;
					if (type === 'number') {
						name = '"' + k + '"';
					} else if (type === 'string') {
						name = $.quoteString(k);
					} else {
						continue;
					}
					type = typeof o[k];

					// Invalid values like these return undefined
					// from toJSON, however those object members
					// shouldn't be included in the JSON string at all.
					if (type !== 'function' && type !== 'undefined') {
						val = $.toJSON(o[k]);
						pairs.push(name + ':' + val);
					}
				}
			}
			return '{' + pairs.join(',') + '}';
		}
	};

	/**
	 * jQuery.evalJSON
	 * Evaluates a given json string.
	 *
	 * @param str {String}
	 */
	$.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
		return eval('(' + str + ')');
	};

	/**
	 * jQuery.secureEvalJSON
	 * Evals JSON in a way that is *more* secure.
	 *
	 * @param str {String}
	 */
	$.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
		var filtered =
			str
			.replace(/\\["\\\/bfnrtu]/g, '@')
			.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
			.replace(/(?:^|:|,)(?:\s*\[)+/g, '');

		if (/^[\],:{}\s]*$/.test(filtered)) {
			return eval('(' + str + ')');
		}
		throw new SyntaxError('Error parsing JSON, source is not valid.');
	};

	/**
	 * jQuery.quoteString
	 * Returns a string-repr of a string, escaping quotes intelligently.
	 * Mostly a support function for toJSON.
	 * Examples:
	 * >>> jQuery.quoteString('apple')
	 * "apple"
	 *
	 * >>> jQuery.quoteString('"Where are we going?", she asked.')
	 * "\"Where are we going?\", she asked."
	 */
	$.quoteString = function (str) {
		if (str.match(escape)) {
			return '"' + str.replace(escape, function (a) {
				var c = meta[a];
				if (typeof c === 'string') {
					return c;
				}
				c = a.charCodeAt();
				return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
			}) + '"';
		}
		return '"' + str + '"';
	};

}(jQuery));
<commit_msg>Fix issue 56: Spelling errors in the comments.<commit_after>/**
 * jQuery JSON plugin 2.4-alpha
 *
 * @author Brantley Harris, 2009-2011
 * @author Timo Tijhof, 2011-2012
 * @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
 *         copyrighted 2005 by Bob Ippolito.
 * @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
 *         website's http://www.json.org/json2.js, which proclaims:
 *         "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
 *         I uphold.
 * @license MIT License <http://www.opensource.org/licenses/mit-license.php>
 */
/*global jQuery */
/*jslint sloppy: true, continue: true, plusplus: true */
(function ($) {
	var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
		meta = {
			'\b': '\\b',
			'\t': '\\t',
			'\n': '\\n',
			'\f': '\\f',
			'\r': '\\r',
			'"' : '\\"',
			'\\': '\\\\'
		},
		hasOwn = Object.prototype.hasOwnProperty;

	/**
	 * jQuery.toJSON
	 * Converts the given argument into a JSON representation.
	 *
	 * @param o {Mixed} The json-serializable *thing* to be converted
	 *
	 * If an object has a toJSON prototype, that will be used to get the representation.
	 * Non-integer/string keys are skipped in the object, as are keys that point to a
	 * function.
	 *
	 */
	$.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
		if (o === null) {
			return 'null';
		}

		var pairs, k, name, val,
			type = $.type(o);

		if (type === 'undefined') {
			return undefined;
		}

		// Also covers instantiated Number and Boolean objects,
		// which are typeof 'object' but thanks to $.type, we
		// catch them here. I don't know whether it is right
		// or wrong that instantiated primitives are not
		// exported to JSON as an {"object":..}.
		// We choose this path because that's what the browsers did.
		if (type === 'number' || type === 'boolean') {
			return String(o);
		}
		if (type === 'string') {
			return $.quoteString(o);
		}
		if (typeof o.toJSON === 'function') {
			return $.toJSON(o.toJSON());
		}
		if (type === 'date') {
			var month = o.getUTCMonth() + 1,
				day = o.getUTCDate(),
				year = o.getUTCFullYear(),
				hours = o.getUTCHours(),
				minutes = o.getUTCMinutes(),
				seconds = o.getUTCSeconds(),
				milli = o.getUTCMilliseconds();

			if (month < 10) {
				month = '0' + month;
			}
			if (day < 10) {
				day = '0' + day;
			}
			if (hours < 10) {
				hours = '0' + hours;
			}
			if (minutes < 10) {
				minutes = '0' + minutes;
			}
			if (seconds < 10) {
				seconds = '0' + seconds;
			}
			if (milli < 100) {
				milli = '0' + milli;
			}
			if (milli < 10) {
				milli = '0' + milli;
			}
			return '"' + year + '-' + month + '-' + day + 'T' +
				hours + ':' + minutes + ':' + seconds +
				'.' + milli + 'Z"';
		}

		pairs = [];

		if ($.isArray(o)) {
			for (k = 0; k < o.length; k++) {
				pairs.push($.toJSON(o[k]) || 'null');
			}
			return '[' + pairs.join(',') + ']';
		}

		// Any other object (plain object, RegExp, ..)
		// Need to do typeof instead of $.type, because we also
		// want to catch non-plain objects.
		if (typeof o === 'object') {
			for (k in o) {
				// Only include own properties,
				// Filter out inherited prototypes
				if (hasOwn.call(o, k)) {
					// Keys must be numerical or string. Skip others
					type = typeof k;
					if (type === 'number') {
						name = '"' + k + '"';
					} else if (type === 'string') {
						name = $.quoteString(k);
					} else {
						continue;
					}
					type = typeof o[k];

					// Invalid values like these return undefined
					// from toJSON, however those object members
					// shouldn't be included in the JSON string at all.
					if (type !== 'function' && type !== 'undefined') {
						val = $.toJSON(o[k]);
						pairs.push(name + ':' + val);
					}
				}
			}
			return '{' + pairs.join(',') + '}';
		}
	};

	/**
	 * jQuery.evalJSON
	 * Evaluates a given json string.
	 *
	 * @param str {String}
	 */
	$.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
		return eval('(' + str + ')');
	};

	/**
	 * jQuery.secureEvalJSON
	 * Evals JSON in a way that is *more* secure.
	 *
	 * @param str {String}
	 */
	$.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
		var filtered =
			str
			.replace(/\\["\\\/bfnrtu]/g, '@')
			.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
			.replace(/(?:^|:|,)(?:\s*\[)+/g, '');

		if (/^[\],:{}\s]*$/.test(filtered)) {
			return eval('(' + str + ')');
		}
		throw new SyntaxError('Error parsing JSON, source is not valid.');
	};

	/**
	 * jQuery.quoteString
	 * Returns a string-repr of a string, escaping quotes intelligently.
	 * Mostly a support function for toJSON.
	 * Examples:
	 * >>> jQuery.quoteString('apple')
	 * "apple"
	 *
	 * >>> jQuery.quoteString('"Where are we going?", she asked.')
	 * "\"Where are we going?\", she asked."
	 */
	$.quoteString = function (str) {
		if (str.match(escape)) {
			return '"' + str.replace(escape, function (a) {
				var c = meta[a];
				if (typeof c === 'string') {
					return c;
				}
				c = a.charCodeAt();
				return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
			}) + '"';
		}
		return '"' + str + '"';
	};

}(jQuery));
<|endoftext|>


<commit_before>/*
	forces Constraint Validation API

	helpers for the HTML5 constraint validation API

*/

if ( jQuery !== 'undefined' ) {
(function( $ ){
	'use strict';

	// http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
	// 1*( atext / "." ) "@" ldh-str 1*( "." ldh-str )
	var REXP_EMAIL = /^[A-Za-z0-9!#$%&'*+\-\/=\?\^_`\{\|\}~\.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)*$/,

		// fields that validate
		candidateForValidation = 'input, select, textarea',

		// for feature detection
		input = $( '<input>' ),

		// polyfill test
		polyfill = typeof input[ 0 ].validity !== 'object',

		// new 'invalid' event
		invalidEvent = function() {
			var invalid = $.Event( 'invalid' );
			// invalid events do not bubble
			invalid.stopImmediatePropagation();
			return invalid;
		},

		// manage validity state object
		validityState = function( typeMismatch, valueMissing, customError, message ) {

			if ( typeof message === 'string' ) {
				customError = !! message;
			}
			return {
				customError: customError,
				typeMismatch: !! typeMismatch,
				valueMissing: !! valueMissing,
				valid: ! typeMismatch && ! valueMissing && ! customError
			};
		},

		validateField = function( message ) {

			var $this = $( this ),
				valueMissing = !! $this.attr( 'required' ),
				invalidEmail = this.getAttribute( 'type' ) === 'email' && !! this.value && ! REXP_EMAIL.test( this.value )
			;

			// if required, check for missing value
			if ( valueMissing ) {

				if ( /^select$/i.test( this.nodeName )) {
					valueMissing = this.selectedIndex === 0 && this.options[ 0 ].value === '';

				// radio buttons
				} else if ( this.type === 'radio' ) {
					valueMissing = $( this.form.elements[ this.name ] ).filter( ':checked' ).length === 0;
				} else {
					valueMissing = ! this.value;
				}
			}

			// set .validityState
			this.validity = validityState( invalidEmail, valueMissing, this.validity.customError || false, message );

			// set .validationMessage
			if ( this.validity.valid ) {
				this.validationMessage = '';

			} else if ( this.validity.customError && typeof message === 'string' ) {
				this.validationMessage = message;

			} else if ( this.validity.valueMissing ) {
				this.validationMessage = 'Please answer this question';

			} else if ( this.validity.typeMismatch ) {
				this.validationMessage = 'Please type an email address';
			}

			return this.disabled || this.validity.valid;
		},

		changeHandler = function( event ) {
			var target = event.target;

			validateField.call( target );

			if ( target.type === 'radio' ) {
				$( target.form.elements[ this.name ] ).each(function() {
					this.validity = target.validity;
					this.validationMessage = target.validationMessage;
				});
			}
		},

		submitHandler = function( event ) {

			var form = $( this ),
				novalidate = !! form.attr( 'novalidate' ),
				invalid = false
			;

			// polyfill validation?
			if ( polyfill ) {
				// check fields
				form.find( candidateForValidation ).each(function() {

					invalid = ! validateField.call( this );

					// unless @novalidate
					if ( ! novalidate ) {
						// if invalid
						if ( invalid ) {
							// trigger invalid
							$( this ).trigger( invalidEvent() );
						}
					}
				});
			}

			// NOTE the code below runs in all browsers to polyfill implementation bugs
			// e.g. Opera 11 on OSX fires submit event even when fields are invalid
			// correct implementations will not invoke this submit handler until all fields are valid

			// unless @novalidate
			// if there are invalid fields
			if ( ! novalidate && form.find( candidateForValidation ).filter(function() {
				return ! ( this.disabled || this.validity.valid );
			}).length > 0 ) {
				// abort submit
				event.stopImmediatePropagation();
				event.preventDefault();
				return false;
			}
		},

		initConstraintValidationAPI = function() {
			var candidates = $( candidateForValidation );

			// INPUT validityState
			if ( typeof input[ 0 ].validity !== 'object' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.validity !== 'object';
				}).each(function() {

					this.validity = validityState( false, false, false, '' );
					this.validationMessage = '';

				});

				// check validity on change
				candidates
					.unbind( 'change.constraintValidationAPI' )
					.bind( 'change.constraintValidationAPI', changeHandler )
				;
			}

			// INPUT validitationMessage
			if ( typeof input[ 0 ].validationMessage !== 'string' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.validationMessage !== 'string';
				}).each(function() {
					this.validationMessage = '';
				});
			}

			// INPUT checkValidity
			if ( typeof input[ 0 ].checkValidity !== 'function' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.checkValidity !== 'function';
				}).each(function() {
					var domElement = this,
						$this = $( this );

					this.checkValidity = function() {
						// TODO is this breaking .setCustomValidity() by passing NULL as 'message'
						var valid = validateField.call( domElement );

						// if invalid, and unless novalidate
						if ( ! valid && ! $this.closest( 'form' ).attr( 'novalidate' )) {
							// fire invalid event
							$this.trigger( invalidEvent() );
						}

						return valid;
					};
				});
			}

			// INPUT setCustomValidity
			if ( typeof input[ 0 ].setCustomValidity !== 'function' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.setCustomValidity !== 'function';
				}).each(function() {
					var that = this;

					this.setCustomValidity = function( message ) {
						validateField.call( that, message );
					};
				});
			}

			// check validity on submit
			// this should be bound before all other submit handlers bound to the same form
			// otherwise they will execute before this handler can cancel submit (oninvalid)
			$( 'form' )
				.unbind( 'submit.constraintValidationAPI' )
				.bind( 'submit.constraintValidationAPI', submitHandler )
			;
		}
	;

	// run immediately and ondocumentready
	initConstraintValidationAPI();
	$( initConstraintValidationAPI );

	// expose init function
	window.initConstraintValidationAPI = initConstraintValidationAPI;

}( jQuery ));
}
<commit_msg>add .patternMismatch to validityState object<commit_after>/*
	forces Constraint Validation API

	helpers for the HTML5 constraint validation API

*/

if ( jQuery !== 'undefined' ) {
(function( $ ){
	'use strict';

	// http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
	// 1*( atext / "." ) "@" ldh-str 1*( "." ldh-str )
	var REXP_EMAIL = /^[A-Za-z0-9!#$%&'*+\-\/=\?\^_`\{\|\}~\.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)*$/,

		// fields that validate
		candidateForValidation = 'input, select, textarea',

		// for feature detection
		input = $( '<input>' ),

		// polyfill test
		polyfill = typeof input[ 0 ].validity !== 'object',

		// new 'invalid' event
		invalidEvent = function() {
			var invalid = $.Event( 'invalid' );
			// invalid events do not bubble
			invalid.stopImmediatePropagation();
			return invalid;
		},

		// manage validity state object
		validityState = function( typeMismatch, valueMissing, customError, message, patternMismatch ) {

			if ( typeof message === 'string' ) {
				customError = !! message;
			}
			return {
				customError: customError,
				typeMismatch: !! typeMismatch,
				patternMismatch: !! patternMismatch,
				valueMissing: !! valueMissing,
				valid: ! valueMissing && ! customError && ! typeMismatch && ! patternMismatch
			};
		},

		validateField = function( message ) {

			var $this = $( this ),
				valueMissing = !! $this.attr( 'required' ),
				invalidEmail = this.getAttribute( 'type' ) === 'email' && !! this.value && ! REXP_EMAIL.test( this.value ),
				patternMismatch = false
			;

			// if required, check for missing value
			if ( valueMissing ) {

				if ( /^select$/i.test( this.nodeName )) {
					valueMissing = this.selectedIndex === 0 && this.options[ 0 ].value === '';

				// radio buttons
				} else if ( this.type === 'radio' ) {
					valueMissing = $( this.form.elements[ this.name ] ).filter( ':checked' ).length === 0;
				} else {
					valueMissing = ! this.value;
				}
			}

			// set .validityState
			this.validity = validityState( invalidEmail, valueMissing, this.validity.customError || false, message, patternMismatch );

			// set .validationMessage
			if ( this.validity.valid ) {
				this.validationMessage = '';

			} else if ( this.validity.customError && typeof message === 'string' ) {
				this.validationMessage = message;

			} else if ( this.validity.valueMissing ) {
				this.validationMessage = 'Please answer this question';

			} else if ( this.validity.typeMismatch ) {
				this.validationMessage = 'Please type an email address';
			}

			return this.disabled || this.validity.valid;
		},

		changeHandler = function( event ) {
			var target = event.target;

			validateField.call( target );

			if ( target.type === 'radio' ) {
				$( target.form.elements[ this.name ] ).each(function() {
					this.validity = target.validity;
					this.validationMessage = target.validationMessage;
				});
			}
		},

		submitHandler = function( event ) {

			var form = $( this ),
				novalidate = !! form.attr( 'novalidate' ),
				invalid = false
			;

			// polyfill validation?
			if ( polyfill ) {
				// check fields
				form.find( candidateForValidation ).each(function() {

					invalid = ! validateField.call( this );

					// unless @novalidate
					if ( ! novalidate ) {
						// if invalid
						if ( invalid ) {
							// trigger invalid
							$( this ).trigger( invalidEvent() );
						}
					}
				});
			}

			// NOTE the code below runs in all browsers to polyfill implementation bugs
			// e.g. Opera 11 on OSX fires submit event even when fields are invalid
			// correct implementations will not invoke this submit handler until all fields are valid

			// unless @novalidate
			// if there are invalid fields
			if ( ! novalidate && form.find( candidateForValidation ).filter(function() {
				return ! ( this.disabled || this.validity.valid );
			}).length > 0 ) {
				// abort submit
				event.stopImmediatePropagation();
				event.preventDefault();
				return false;
			}
		},

		initConstraintValidationAPI = function() {
			var candidates = $( candidateForValidation );

			// INPUT validityState
			if ( typeof input[ 0 ].validity !== 'object' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.validity !== 'object';
				}).each(function() {

					this.validity = validityState( false, false, false, '', false );
					this.validationMessage = '';

				});

				// check validity on change
				candidates
					.unbind( 'change.constraintValidationAPI' )
					.bind( 'change.constraintValidationAPI', changeHandler )
				;
			}

			// INPUT validitationMessage
			if ( typeof input[ 0 ].validationMessage !== 'string' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.validationMessage !== 'string';
				}).each(function() {
					this.validationMessage = '';
				});
			}

			// INPUT checkValidity
			if ( typeof input[ 0 ].checkValidity !== 'function' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.checkValidity !== 'function';
				}).each(function() {
					var domElement = this,
						$this = $( this );

					this.checkValidity = function() {
						// TODO is this breaking .setCustomValidity() by passing NULL as 'message'
						var valid = validateField.call( domElement );

						// if invalid, and unless novalidate
						if ( ! valid && ! $this.closest( 'form' ).attr( 'novalidate' )) {
							// fire invalid event
							$this.trigger( invalidEvent() );
						}

						return valid;
					};
				});
			}

			// INPUT setCustomValidity
			if ( typeof input[ 0 ].setCustomValidity !== 'function' ) {
				// set us up the API
				candidates.filter(function() {
					return typeof this.setCustomValidity !== 'function';
				}).each(function() {
					var that = this;

					this.setCustomValidity = function( message ) {
						validateField.call( that, message );
					};
				});
			}

			// check validity on submit
			// this should be bound before all other submit handlers bound to the same form
			// otherwise they will execute before this handler can cancel submit (oninvalid)
			$( 'form' )
				.unbind( 'submit.constraintValidationAPI' )
				.bind( 'submit.constraintValidationAPI', submitHandler )
			;
		}
	;

	// run immediately and ondocumentready
	initConstraintValidationAPI();
	$( initConstraintValidationAPI );

	// expose init function
	window.initConstraintValidationAPI = initConstraintValidationAPI;

}( jQuery ));
}
<|endoftext|>


<commit_before>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
	// Beep Boop sets the SLACK_VERIFY_TOKEN env var
	verify_token: process.env.SLACK_VERIFY_TOKEN,
	convo_store: ConvoStore(),
	context: Context()
})

var HELP_TEXT = `
I will respond to the following messages:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
slapp.message('help', ['mention', 'direct_message'], (msg) => {
	msg.say(HELP_TEXT)
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
.message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
	msg
	.say(`${text}, how are you?`)
		// sends next event from user to this route, passing along state
		.route('how-are-you', { greeting: text })
	})
.route('how-are-you', (msg, state) => {
	var text = (msg.body.event && msg.body.event.text) || ''

	// user may not have typed text as their next action, ask again and re-route
	if (!text) {
		return msg
		.say("Whoops, I'm still waiting to hear how you're doing.")
		.say('How are you?')
		.route('how-are-you', state)
	}

	// add their response to state
	state.status = text

	msg
	.say("Ok then. What's your favorite color?")
	.route('color', state)
})
.route('color', (msg, state) => {
	var text = (msg.body.event && msg.body.event.text) || ''

	// user may not have typed text as their next action, ask again and re-route
	if (!text) {
		return msg
		.say("I'm eagerly awaiting to hear your favorite color.")
		.route('color', state)
	}

	// add their response to state
	state.color = text

	msg
	.say('Thanks for sharing.')
	.say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
	// At this point, since we don't route anywhere, the "conversation" is over
})

	// Can use a regex as well
	slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
		// You can provide a list of responses, and a random one will be chosen
		// You can also include slack emoji in your responses
		msg.say([
			"You're welcome :smile:",
			'You bet',
			':+1: Of course',
			'Anytime :sun_with_face: :full_moon_with_face:'
			])
	})

	// demonstrate returning an attachment...
	slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
		msg.say({
			text: 'Check out this amazing attachment! :confetti_ball: ',
			attachments: [{
				text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
				title: 'Slapp Library - Open Source',
				image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
				title_link: 'https://beepboophq.com/',
				color: '#7CD197'
			}]
		})
	})

	// demonstrate returning an attachment...
	slapp.message('doggos', ['mention', 'direct_message'], (msg) => {
		getImage()
		msg.say({
			text: 'Check out this amazing attachment! :confetti_ball: ',
			attachments: [{
				text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
				title: 'Slapp Library - Open Source',
				image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
				title_link: 'https://beepboophq.com/',
				color: '#7CD197'
			}]
		})
	})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_mention', 'direct_message'], (msg) => {
	// respond only 40% of the time
	if (Math.random() < 0.4) {
		msg.say([':wave:', ':pray:', ':raised_hands:'])
	}
})

function getImage () {
	var http = require('http');
	var url = 'https://api.tumblr.com/v2/tagged?tag=husky&api_key=' + process.env.TUMBLR_ACCESS_KEY;

	var doggos = ["samoyed", "shibe", "shiba", "doge", "shibainu", "shetland-sheepdog", "sheltie", "shelties"]
	var woofers = ["Saint-Bernard", "mastiff", "greyhound", "german-shepard", "german shepard", "husky", "Siberian Husky", "Golden Retriever"]
	var puppers = ["beagle", "beagles", "dachshund", "papillon", "pomeranian", "schipperke", "yorkie"]

	var request = require('request');
	request(url, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			console.log(body);
			var parsed = JSON.parse(body);

			var urls = []
			var photos;

			for (var post in parsed) {
				if (post.hasOwnProperty('photos')) {
					photos = post['photos'];
					var size = photos[0]['original_size'];
					var url = size['url'];
					urls.append(url);
				}
			}
			console.log(urls);
		}
	});
}

// attach Slapp to express server
var server = slapp.attachToExpress(express())

// start http server
server.listen(port, (err) => {
	if (err) {
		return console.error(err)
	}

	console.log(`Listening on port ${port}`)
})
<commit_msg>more print<commit_after>'use strict'

const express = require('express')
const Slapp = require('slapp')
const ConvoStore = require('slapp-convo-beepboop')
const Context = require('slapp-context-beepboop')

// use `PORT` env var on Beep Boop - default to 3000 locally
var port = process.env.PORT || 3000

var slapp = Slapp({
	// Beep Boop sets the SLACK_VERIFY_TOKEN env var
	verify_token: process.env.SLACK_VERIFY_TOKEN,
	convo_store: ConvoStore(),
	context: Context()
})

var HELP_TEXT = `
I will respond to the following messages:
\`help\` - to see this message.
\`hi\` - to demonstrate a conversation that tracks state.
\`thanks\` - to demonstrate a simple response.
\`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
\`attachment\` - to see a Slack attachment message.
`

//*********************************************
// Setup different handlers for messages
//*********************************************

// response to the user typing "help"
slapp.message('help', ['mention', 'direct_message'], (msg) => {
	msg.say(HELP_TEXT)
})

// "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
slapp
.message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
	msg
	.say(`${text}, how are you?`)
		// sends next event from user to this route, passing along state
		.route('how-are-you', { greeting: text })
	})
.route('how-are-you', (msg, state) => {
	var text = (msg.body.event && msg.body.event.text) || ''

	// user may not have typed text as their next action, ask again and re-route
	if (!text) {
		return msg
		.say("Whoops, I'm still waiting to hear how you're doing.")
		.say('How are you?')
		.route('how-are-you', state)
	}

	// add their response to state
	state.status = text

	msg
	.say("Ok then. What's your favorite color?")
	.route('color', state)
})
.route('color', (msg, state) => {
	var text = (msg.body.event && msg.body.event.text) || ''

	// user may not have typed text as their next action, ask again and re-route
	if (!text) {
		return msg
		.say("I'm eagerly awaiting to hear your favorite color.")
		.route('color', state)
	}

	// add their response to state
	state.color = text

	msg
	.say('Thanks for sharing.')
	.say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
	// At this point, since we don't route anywhere, the "conversation" is over
})

	// Can use a regex as well
	slapp.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
		// You can provide a list of responses, and a random one will be chosen
		// You can also include slack emoji in your responses
		msg.say([
			"You're welcome :smile:",
			'You bet',
			':+1: Of course',
			'Anytime :sun_with_face: :full_moon_with_face:'
			])
	})

	// demonstrate returning an attachment...
	slapp.message('attachment', ['mention', 'direct_message'], (msg) => {
		msg.say({
			text: 'Check out this amazing attachment! :confetti_ball: ',
			attachments: [{
				text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
				title: 'Slapp Library - Open Source',
				image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
				title_link: 'https://beepboophq.com/',
				color: '#7CD197'
			}]
		})
	})

	// demonstrate returning an attachment...
	slapp.message('doggos', ['mention', 'direct_message'], (msg) => {
		getImage()
		msg.say({
			text: 'Check out this amazing attachment! :confetti_ball: ',
			attachments: [{
				text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
				title: 'Slapp Library - Open Source',
				image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
				title_link: 'https://beepboophq.com/',
				color: '#7CD197'
			}]
		})
	})

// Catch-all for any other responses not handled above
slapp.message('.*', ['direct_mention', 'direct_message'], (msg) => {
	// respond only 40% of the time
	if (Math.random() < 0.4) {
		msg.say([':wave:', ':pray:', ':raised_hands:'])
	}
})

function getImage () {
	var http = require('http');
	var url = 'https://api.tumblr.com/v2/tagged?tag=husky&api_key=' + process.env.TUMBLR_ACCESS_KEY;

	var doggos = ["samoyed", "shibe", "shiba", "doge", "shibainu", "shetland-sheepdog", "sheltie", "shelties"]
	var woofers = ["Saint-Bernard", "mastiff", "greyhound", "german-shepard", "german shepard", "husky", "Siberian Husky", "Golden Retriever"]
	var puppers = ["beagle", "beagles", "dachshund", "papillon", "pomeranian", "schipperke", "yorkie"]

	var request = require('request');
	request(url, function (error, response, body) {
		if (!error && response.statusCode == 200) {
			console.log(body);
			var parsed = JSON.parse(body);

			var urls = []
			var photos;

			for (var post in parsed) {
				if (post.hasOwnProperty('photos')) {
					console.log("post:::");
					console.log(post);
					photos = post['photos'];
					console.log("photos:::");
					console.log(photos);
					var size = photos[0]['original_size'];
					console.log("size:::");
					console.log(size);
					var url = size['url'];
					console.log("url:::");
					console.log(url);
					urls.append(url);
				}
			}
			console.log(urls);
		}
	});
}

// attach Slapp to express server
var server = slapp.attachToExpress(express())

// start http server
server.listen(port, (err) => {
	if (err) {
		return console.error(err)
	}

	console.log(`Listening on port ${port}`)
})
<|endoftext|>


<commit_before>const {React} = Serverboards
const random_color = Serverboards.utils.random_color

function find_service(service_id, services){
  return services.find(s => s.uuid == service_id) || {name: service_id}
}

function Action(props){
  const a = props.action
  return (

       props.onRunAction(a)} style={{textAlign:"center"}}>

            <i className={`ui huge blue icon ${a.icon || "hand pointer"}`}/>

            {a.name}

            {a.service ? (
              {find_service(a.service, props.services).name}
            ) : null}

         {ev.preventDefault(); props.onConfigureAction(a)}}>

  )
}

function View(props){
  const actions = (props.actions || []).sort( (a,b) => a.name.localeCompare(b.name) )
  return (

        {(actions || []).map( (a) => (
          <Action key={a.id} action={a} {...props}/>
        ))}

  )
}

export default View
<commit_msg>Style added more padding to quick actions<commit_after>const {React} = Serverboards
const random_color = Serverboards.utils.random_color

function find_service(service_id, services){
  return services.find(s => s.uuid == service_id) || {name: service_id}
}

function Action(props){
  const a = props.action
  return (

       props.onRunAction(a)} style={{textAlign:"center"}}>

            <i className={`ui huge blue icon ${a.icon || "hand pointer"}`}/>

            {a.name}

            {a.service ? (
              {find_service(a.service, props.services).name}
            ) : null}

         {ev.preventDefault(); props.onConfigureAction(a)}}>

  )
}

function View(props){
  const actions = (props.actions || []).sort( (a,b) => a.name.localeCompare(b.name) )
  return (

        {(actions || []).map( (a) => (
          <Action key={a.id} action={a} {...props}/>
        ))}

  )
}

export default View
<|endoftext|>


<commit_before>/**
 * authenticated
 *
 * @module      :: Policy
 * @description :: Simple policy to allow any authenticated user
 *                 Assumes that your login action in one of your controllers sets `req.session.authenticated = true;`
 * @docs        :: http://sailsjs.org/#!documentation/policies
 *
 */
module.exports = function(req, res, next) {

  var token;

  var fail = function() {
    return res.forbidden('You are not permitted to perform this action.');
  };

  if (req.isSocket) {
    token = req.socket.authToken;
    console.log('token-socket', token);

    if (!req.socket || !req.socket.redis) {
      redis.socketOnConnection(req.session, req.socket);
    }

  } else {
    token = req.headers['auth-token'];
    console.log('token-regular', token);
  }

  User.findByToken(token, function(err, user) {
    if (err) return res.serverError(err);

    if (!user) return fail();

    req.user = user;

    next();
  });

};
<commit_msg>dont look up user for every request over websocket. only the first.<commit_after>/**
 * authenticated
 *
 * @module      :: Policy
 * @description :: Simple policy to allow any authenticated user
 *                 Assumes that your login action in one of your controllers sets `req.session.authenticated = true;`
 * @docs        :: http://sailsjs.org/#!documentation/policies
 *
 */
module.exports = function(req, res, next) {

  var token;

  var fail = function() {
    return res.forbidden('You are not permitted to perform this action.');
  };

  if (req.isSocket) {
    req.user = req.socket.user;

    if (req.user) return next();

    token = req.socket.authToken;
    console.log('token-socket', token);

    if (!req.socket || !req.socket.redis) {
      redis.socketOnConnection(req.session, req.socket);
    }

  } else {
    token = req.headers['auth-token'];
    console.log('token-regular', token);
  }

  User.findByToken(token, function(err, user) {
    if (err) return res.serverError(err);

    if (!user) return fail();

    req.user        = user;
    req.socket.user = user;

    next();
  });

};
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    for (var counter = 0; counter < length; counter++)
    {
      var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
      var args = ['commit', outFile, '-m', message]
      fs.writeSync(outFD, data.slice(counter, counter+1), 0, 1)
      sleep(Math.random() * max_sleep)
      child_process.execFileSync('/usr/bin/git', args)
    }
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [time <commit_msg>Someone poisoned the waterhole!<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writing from %s to %s, with up to %s seconds between commits", inFile, outFile, max_sleep)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    console.info("Bytes: %s", length)
    try {
      child_process.execFileSync('/usr/bin/git', ['add', outFile])
    } catch (e) {
      console.error("Couldn't add %s to git: %s", outFile, e)
    }
    var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
    var args = ['commit', outFile, '-m', message]
    for (var counter = 0; counter < length; counter++)
    {
      var message = commit_messages[randomIntFromInterval(0, commit_messages.length - 1)]
      var args = ['commit', outFile, '-m', message]
      fs.writeSync(outFD, data.slice(counter, counter+1), 0, 1)
      sleep(Math.random() * max_sleep)
      child_process.execFileSync('/usr/bin/git', args)
    }
  })
} else {
  console.info("Usage:")
  console.info("node commit.js infile outfile [time i<|endoftext|>


<commit_before>//@TODO - Stop using globals
module.exports = function (options) {

    _ = require('underscore');
    CircularJSON = require('circular-json');
    Cleverbot = require('cleverbot-node');
    fs = require('fs');
    moment = require('moment');
    dpath = require('path');
    Promise = require('bluebird');
    reload = require('require-reload')(require);
    request = require('request');
    S = require('string');
    Sequelize = require('sequelize');
    YouTube = require('youtube-api');

    config = options.config;
    bot = options.bot;

    console.log('Here....');

    models = require(dpath.resolve(__dirname, 'models/index'));

console.log('Here2....');

    mentions = {
        lastRunAll: 0,
        lastRunUsers: []
    };
    botUser = bot.getSelf();

    models.sequelize.sync({ force: config.db.forceSequelizeSync }).then(function () {
        console.log('Connected to ' + config.db.dialect + ' database: ' + config.db.database);
    });

    if (config.apiKeys.youtube !== undefined) {
        console.log('[YOUTUBE]', 'Authenticating with youtube...');
        var oauth = YouTube.authenticate({
            type: "key",
            key: config.apiKeys.youtube.api_key
        });
        console.log('[YOUTUBE]', 'Authenticated! ' + JSON.stringify(oauth, null, 2));
    }

    cleverbot = new Cleverbot;
    cleverbot.configure({ botapi: config.apiKeys.cleverbot })

    PERMISSIONS = {
        NONE: 0,
        RDJ: 1,
        RDJ_PLUS: 1.5,
        BOUNCER: 2,
        BOUNCER_PLUS: 2.5,
        MANAGER: 3,
        COHOST: 4,
        HOST: 5
    };

    uptime = new Date();
    lastRpcMessage = new Date();

    settings = {
        autoskip: false,
        timeguard: false,
        maxdctime: 15 * 60,
        maxsonglength: config.queue.maxSongLengthSecs,
        maxdjidletime: config.queue.djIdleAfterMins * 60,
        djidle: false,
        djidleminqueue: config.queue.djIdleMinQueueLengthToEnforce,
        djcyclemaxqueue: config.queue.djCycleMaxQueueLength,
        lockdown: false,
        cleverbot: false,
        rdjplus: false,
        bouncerplus: false
    };
    setting_names = {
        'autoskip': 'Autoskip',
        'timeguard': 'Timeguard',
        'maxdctime': 'DC Protection Time',
        'maxsonglength': 'Max Song Length',
        'maxdjidletime': 'Max DJ Idle Time',
        'djidleminqueue': 'DJ Idle Min Queue',
        'djcyclemaxqueue': 'DJ Cycle Max Queue',
        'lockdown': 'Lockdown Mode',
        'cleverbot': 'Cleverbot',
        'rdjplus': 'RDJ+ Mode',
        'bouncerplus': 'Bouncer+ Mode',
    };

    iso_languages = {
        'af': 'Afrikkans',
        'ar': 'Arabic',
        'be': 'Belarusian',
        'bg': 'Bulgarian',
        'ca': 'Catalan',
        'cs': 'Czech',
        'da': 'Danish',
        'de': 'German',
        'el': 'Greek',
        'en': 'English',
        'es': 'Spanish',
        'et': 'Estonian',
        'eu': 'Basque',
        'fa': 'Farsi',
        'fi': 'Finnish',
        'fo': 'Faeroese',
        'fr': 'French',
        'ga': 'Irish',
        'gd': 'Gaelic',
        'hi': 'Hindi',
        'hr': 'Croatian',
        'hu': 'Hungarian',
        'id': 'Indonesian',
        'is': 'Icelandic',
        'it': 'Italian',
        'ja': 'Japanese',
        'ji': 'Yiddish',
        'ko': 'Korean',
        'ku': 'Kurdish',
        'lt': 'Lithuanian',
        'lv': 'Latvian',
        'mk': 'Macedonian',
        'ml': 'Malayalam',
        'ms': 'Malasian',
        'mt': 'Maltese',
        'nl': 'Dutch',
        'nb': 'Norwegian',
        'no': 'Norwegian',
        'pa': 'Punjabi',
        'pl': 'Polish',
        'pt': 'Portuguese',
        'rm': 'Rhaeto-Romanic',
        'ro': 'Romanian',
        'ru': 'Russian',
        'sb': 'Sorbian',
        'sk': 'Slovak',
        'sl': 'Slovenian',
        'sq': 'Albanian',
        'sr': 'Serbian',
        'sv': 'Swedish',
        'th': 'Thai',
        'tn': 'Tswana',
        'tr': 'Turkish',
        'ts': 'Tsonga',
        'uk': 'Ukranian',
        'ur': 'Urdu',
        've': 'Venda',
        'vi': 'Vietnamese',
        'xh': 'Xhosa',
        'zh': 'Chinese',
        'zu': 'Zulu'
    };

    /**
     * Set default time thresholds for moment
     * (round up a little less aggressively)
     */
    moment.relativeTimeThreshold('s', 55);
    moment.relativeTimeThreshold('m', 90);
    moment.relativeTimeThreshold('h', 24);
    moment.relativeTimeThreshold('d', 30);
    moment.relativeTimeThreshold('M', 12);

    startupTimestamp = moment.utc().toDate();

    /**
     * Custom functions accessible to commands
     */

    getActiveDJs = function (maxIdleMins, startPosition, callback) {
        var activeUsers = [];
        if (startPosition === undefined) {
            startPosition = 0;
        }

        Promise.map(_.rest(bot.getDJs(), startPosition), function (dj) {
            return models.User.find({ where: { site_id: dj.id, site: config.site } }).then(function (dbUser) {
                if (dbUser !== null && dbUser.site_id !== bot.getSelf().id) {
                    if (secondsSince(dbUser.last_active) <= (maxIdleMins * 60)) {
                        activeUsers.push(dbUser);
                    }
                }
            });
        }).then(function () {
            callback(activeUsers);
        });
    };

};
<commit_msg>Remove debugging statements<commit_after>//@TODO - Stop using globals
module.exports = function (options) {

    _ = require('underscore');
    CircularJSON = require('circular-json');
    Cleverbot = require('cleverbot-node');
    fs = require('fs');
    moment = require('moment');
    dpath = require('path');
    Promise = require('bluebird');
    reload = require('require-reload')(require);
    request = require('request');
    S = require('string');
    Sequelize = require('sequelize');
    YouTube = require('youtube-api');

    config = options.config;
    bot = options.bot;

    models = require(dpath.resolve(__dirname, 'models/index'));

    mentions = {
        lastRunAll: 0,
        lastRunUsers: []
    };
    botUser = bot.getSelf();

    models.sequelize.sync({ force: config.db.forceSequelizeSync }).then(function () {
        console.log('Connected to ' + config.db.dialect + ' database: ' + config.db.database);
    });

    if (config.apiKeys.youtube !== undefined) {
        console.log('[YOUTUBE]', 'Authenticating with youtube...');
        var oauth = YouTube.authenticate({
            type: "key",
            key: config.apiKeys.youtube.api_key
        });
        console.log('[YOUTUBE]', 'Authenticated! ' + JSON.stringify(oauth, null, 2));
    }

    cleverbot = new Cleverbot;
    cleverbot.configure({ botapi: config.apiKeys.cleverbot })

    PERMISSIONS = {
        NONE: 0,
        RDJ: 1,
        RDJ_PLUS: 1.5,
        BOUNCER: 2,
        BOUNCER_PLUS: 2.5,
        MANAGER: 3,
        COHOST: 4,
        HOST: 5
    };

    uptime = new Date();
    lastRpcMessage = new Date();

    settings = {
        autoskip: false,
        timeguard: false,
        maxdctime: 15 * 60,
        maxsonglength: config.queue.maxSongLengthSecs,
        maxdjidletime: config.queue.djIdleAfterMins * 60,
        djidle: false,
        djidleminqueue: config.queue.djIdleMinQueueLengthToEnforce,
        djcyclemaxqueue: config.queue.djCycleMaxQueueLength,
        lockdown: false,
        cleverbot: false,
        rdjplus: false,
        bouncerplus: false
    };
    setting_names = {
        'autoskip': 'Autoskip',
        'timeguard': 'Timeguard',
        'maxdctime': 'DC Protection Time',
        'maxsonglength': 'Max Song Length',
        'maxdjidletime': 'Max DJ Idle Time',
        'djidleminqueue': 'DJ Idle Min Queue',
        'djcyclemaxqueue': 'DJ Cycle Max Queue',
        'lockdown': 'Lockdown Mode',
        'cleverbot': 'Cleverbot',
        'rdjplus': 'RDJ+ Mode',
        'bouncerplus': 'Bouncer+ Mode',
    };

    iso_languages = {
        'af': 'Afrikkans',
        'ar': 'Arabic',
        'be': 'Belarusian',
        'bg': 'Bulgarian',
        'ca': 'Catalan',
        'cs': 'Czech',
        'da': 'Danish',
        'de': 'German',
        'el': 'Greek',
        'en': 'English',
        'es': 'Spanish',
        'et': 'Estonian',
        'eu': 'Basque',
        'fa': 'Farsi',
        'fi': 'Finnish',
        'fo': 'Faeroese',
        'fr': 'French',
        'ga': 'Irish',
        'gd': 'Gaelic',
        'hi': 'Hindi',
        'hr': 'Croatian',
        'hu': 'Hungarian',
        'id': 'Indonesian',
        'is': 'Icelandic',
        'it': 'Italian',
        'ja': 'Japanese',
        'ji': 'Yiddish',
        'ko': 'Korean',
        'ku': 'Kurdish',
        'lt': 'Lithuanian',
        'lv': 'Latvian',
        'mk': 'Macedonian',
        'ml': 'Malayalam',
        'ms': 'Malasian',
        'mt': 'Maltese',
        'nl': 'Dutch',
        'nb': 'Norwegian',
        'no': 'Norwegian',
        'pa': 'Punjabi',
        'pl': 'Polish',
        'pt': 'Portuguese',
        'rm': 'Rhaeto-Romanic',
        'ro': 'Romanian',
        'ru': 'Russian',
        'sb': 'Sorbian',
        'sk': 'Slovak',
        'sl': 'Slovenian',
        'sq': 'Albanian',
        'sr': 'Serbian',
        'sv': 'Swedish',
        'th': 'Thai',
        'tn': 'Tswana',
        'tr': 'Turkish',
        'ts': 'Tsonga',
        'uk': 'Ukranian',
        'ur': 'Urdu',
        've': 'Venda',
        'vi': 'Vietnamese',
        'xh': 'Xhosa',
        'zh': 'Chinese',
        'zu': 'Zulu'
    };

    /**
     * Set default time thresholds for moment
     * (round up a little less aggressively)
     */
    moment.relativeTimeThreshold('s', 55);
    moment.relativeTimeThreshold('m', 90);
    moment.relativeTimeThreshold('h', 24);
    moment.relativeTimeThreshold('d', 30);
    moment.relativeTimeThreshold('M', 12);

    startupTimestamp = moment.utc().toDate();

    /**
     * Custom functions accessible to commands
     */

    getActiveDJs = function (maxIdleMins, startPosition, callback) {
        var activeUsers = [];
        if (startPosition === undefined) {
            startPosition = 0;
        }

        Promise.map(_.rest(bot.getDJs(), startPosition), function (dj) {
            return models.User.find({ where: { site_id: dj.id, site: config.site } }).then(function (dbUser) {
                if (dbUser !== null && dbUser.site_id !== bot.getSelf().id) {
                    if (secondsSince(dbUser.last_active) <= (maxIdleMins * 60)) {
                        activeUsers.push(dbUser);
                    }
                }
            });
        }).then(function () {
            callback(activeUsers);
        });
    };

};
<|endoftext|>