<commit_before>// ==UserScript==
// @name         UTS Timetable Generator
// @author       szdc
// @namespace    http://github.com/szdc
// @version      0.1
// @description  Timetable generator for UTS students
// @match        https://mysubjects.uts.edu.au/aplus2015/aptimetable*
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @copyright    2015+, szdc
// ==/UserScript==

$(document).ready(function () {
  'use strict';

  var subjects   = [],
      activities = [];

  init();

  function init() {
    // Find the table rows that hold the timetable data
    var table = $('table[cellspacing="1"] tr');

    // Process each row
    table.each(processRow);
    console.log('Subjects found: ' + subjects.length);

    // Get all activity groups.
    var activityGroups = getAllActivityGroups();
    console.log('Total activity groups: ' + activityGroups.length);

    // Get all timetable combinations
    var timetableCombinations = cartesianProductOf(activityGroups);
    timetableCombinations = timetableCombinations.map(function (timetable) {
      return new Timetable(timetable);
    });
    console.log('Timetable combinations: ' + timetableCombinations.length);

    var timetableList = new TimetableList(timetableCombinations);
  }

  /**
   * Processes a timetable row.
   */
  function processRow() {
    var isSubjectHeader = $(this).find('td > a').length > 0,
        isActivityRow   = $(this).attr('bgcolor') === '#eeeeee';

    if (isSubjectHeader) {
      // New subject
      subjects.push(Subject.fromTableRow($(this)));
    } else if (isActivityRow) {
      // New activity under the latest subject
      var subject = subjects[subjects.length - 1];
      subject.addActivity(Activity.fromTableRow($(this), subject));
    }
  }

  /**
   * Gets an array of all activity groups.
   */
  function getAllActivityGroups() {
    return subjects.reduce(function (activityGroups, subject) {
      var groups = subject.getActivityGroups(),
          keys = Object.keys(groups);

      keys.forEach(function (key) {
        activityGroups.push(groups[key]);
      });

      return activityGroups;
    }, []);
  }

  /**
   * Returns the cartesian product of N arrays.
   */
  function cartesianProductOf(arr) {
    return arr.reduce(function(a, b) {
      var ret = [];
      a.forEach(function(a) {
        b.forEach(function(b) {
          ret.push(a.concat([b]));
        });
      });
      return ret;
    }, [[]]);
  }
});

/**
 * Represents a UTS subject.
 */
function Subject(name, code, semester) {
  if (arguments.length < 3) {
    throw new Error('Too few arguments supplied to create the Subject object.');
  }

  var activityGroups = {};

  /**
   * Adds an activity.
   */
  function addActivity(activity) {
    var type = activity.getType(),
        activities = activityGroups[type];
    if (typeof activityGroups[type] === 'undefined') {
      activityGroups[type] = [];
    }

    var existingActivity = getExistingActivity(activityGroups[type], activity);
    if (existingActivity) {
      existingActivity.addNumbers(activity.getNumbers());
    } else {
      activityGroups[type].push(activity);
    }
  }

  /**
   * Retrieves an activity from the array if it matches the
   * activity given.
   */
  function getExistingActivity(existingActivities, activity) {
    for (var i = 0; i < existingActivities.length; i++) {
      var existingActivity = existingActivities[i];
      if (existingActivity.matches(activity)) {
        return existingActivity;
      }
    }
    return null;
  }

  return {
    addActivity: addActivity,
    getActivityGroups: function () { return activityGroups; },
    getCode: function () { return code; }
  };
}

/**
 * Creates a new Subject object from a table row.
 */
Subject.fromTableRow = function (row) {
  var content = row.find('b:first');

  var name     = content.text().substr(content.text().indexOf(':') + 1).trim(),
      code     = content.children('a').attr('href').match(/\d+/)[0],
      semester = /AUT/.test(content.text()) ? 'autumn' : 'spring';

  return new Subject(name, code, semester);
};

/**
 * Represents a UTS subject activity.
 */
function Activity(type, numbers, day, startTime, duration, finishTime, subject) {
  if (arguments.length < 5) {
    throw new Error('Too few arguments supplied to create the Activity object.');
  }

  /**
   * Determines if two activities clash.
   */
  function hasTimeClashWith(activity) {
    var timeClashExists = startTime < activity.getFinishTime() && finishTime > activity.getStartTime();
    return timeClashExists;
  }

  /**
   * Determines if two activities are essentially identical.
   */
  function matches(activity) {
    return type === activity.getType() && day === activity.getDay() &&
      startTime === activity.getStartTime() && finishTime === activity.getFinishTime() &&
      subject === activity.getSubject();
  }

  /**
   * Appends activity numbers to the numbers array.
   */
  function addNumbers(numbersArray) {
    numbers = numbers.concat(numbersArray);
  }

  function toString() {
    return day + ' ' + startTime + '-' + finishTime + ': ' + subject.getCode() + ' ' + type + ' ' + numbers.join(',') + ' (' + duration + ')';
  }

  return {
    hasTimeClashWith: hasTimeClashWith,
    getStartTime: function () { return startTime; },
    getFinishTime: function () { return finishTime; },
    getType: function () { return type; },
    getNumbers: function () { return numbers; },
    getDay: function () { return day; },
    getSubject: function () { return subject; },
    matches: matches,
    addNumbers: addNumbers,
    toString: toString
  };
}

/**
 * Creates a new Activity object from a table row.
 */
Activity.fromTableRow = function (row, subject) {
  var cells = row.children().map(function() {return this.innerHTML;});

  var type       = cells[0],
      numbers    = [cells[1]],
      day        = cells[2],
      startTime  = cells[3],
      duration   = cells[4],
      finishTime = new Date('1/1/2015 ' + startTime).addMinutes(duration).get24hrTime(),
      startTime = parseInt(startTime.replace(':', ''));

  return new Activity(type, numbers, day, startTime, duration, finishTime, subject);
};

/**
 * Compares two activities.
 */
Activity.compare = function (a, b) {
  var days = { "Mon": 0, "Tue": 1, "Wed": 2, "Thu": 3, "Fri": 4, "Sat": 5, "Sun": 6 };

  if (a.getDay() === b.getDay()) {
    return a.getStartTime() - b.getStartTime();
  } else {
    return days[a.getDay()] - days[b.getDay()];
  }
};

/**
 * Represents a list of Timetable objects.
 */
function TimetableList(timetables) {
  if (arguments.length < 1) {
    throw new Error('Too few arguments supplied to create the TimetableList object.');
  }

  // Filter the list to only valid timetable combinations
  timetables = timetables.filter(function (timetable) {
    return timetable.isValid();
  });
  console.log('Valid timetables: ' + timetables.length);

  /**
   * Returns a filtered array of timetables.
   */
  function filter(filterMethod, args) {
    return timetables.filter(filterMethod, args);
  }

  /**
   * Returns an array of timetables that match multiple filters.
   *
   * @param filters {Array<FilterInfo>}
   * An array of FilterInfo objects
   */
  function filterMany(filters) {
    return timetables.filter(function (timetable) {
      for (var i = 0; i < filters.length; i++) {
        var filter = filters[i];
        var result = filter.method.call(filter.options, timetable);
        if (!filter.method.call(filter.options, timetable)) {
          return false;
        }
      }
      return true;
    });
  }

  /**
   * Sorts the timetables.
   */
  function sort(compareMethod) {
    timetables.sort(compareMethod);
  }

  return {
    getTimetables: function () { return timetables; },
    filter: filter,
    filterMany: filterMany,
    sort: sort
  };
}

/**
 * A static object of common filtering methods.
 */
TimetableList.FilterBy = {
  /**
   * Filters by the number of days the timetable spans.
   *
   * Required: The 2nd parameter of the filter method must
   * contain an object with the following properties:
   *  days  {Number}  The maximum allowable number of days
   *  exact {Boolean} Whether or not to also include timetables
   *                  with less days in the results.
   */
  NumberOfDays: function (timetable) {
    if (typeof this.days === 'undefined') {
      throw new Error('Days property missing for filter.');
    }

    var numberOfDays = Object.keys(timetable.getDays()).length;
    return this.exact ? numberOfDays === this.days : numberOfDays <= this.days;
  },

  /**
   * Filters to only timetables that always start later or finish
   * earlier than the specified time.
   *
   * Required: The 2nd parameter of the filter method must
   * contain an object with the following properties:
   *  time  {Number}  The earliest time an activity can start
   *  constraint {String} 'start' for later than; 'finish' for earlier than
   */
  TimeConstraint: function (timetable) {
    if (typeof this.time === 'undefined' || typeof this.constraint === 'undefined') {
      throw new Error('Time or Constraint property missing for filter.');
    }

    return timetable.getActivities().every(function (activity) {
      if (this.constraint === 'start') {
        return activity.getStartTime() >= this.time;
      } else {
        return activity.getFinishTime() <= this.time;
      }
    }, this);
  }
};

/**
 * A static object of common sorting methods.
 */
TimetableList.SortBy = {
  /**
   * Sorts by the total number of hours spent on campus.
   * Hours spent each day = end of last class - start of first class
   */
  HoursOnCampus: function (a, b) {
    return a.getHours() - b.getHours();
  }
};

/**
 * Contains the method and options for a filter.
 */
function FilterInfo(method, options) {
  this.method = method;
  this.options = options;
}

function Timetable(activities) {
  /**
   * Returns an object of days and times that the timetable spans.
   */
  function getDays() {
    var dayInfo = activities.reduce(function (days, activity) {
      var day = activity.getDay(),
          start  = activity.getStartTime(),
          finish = activity.getFinishTime();

      if (typeof days[day] === 'undefined') {
        days[day] = { start: start, finish: finish };
      } else {
        if (start < days[day].start) days[day].start = start;
        if (finish > days[day].finish) days[day].finish = finish;
      }

      return days;
    }, {});

    return dayInfo;
  }

  /**
   * Determines if a timetable is valid.
   * There must be no activity clashes to return true.
   */
  function isValid() {
    var invalidTimes = {
      "Mon": [], "Tue": [], "Wed": [], "Thu": [], "Fri": [], "Sat": [], "Sun": []
    };

    var valid = activities.every(function (activity) {
      var conflicts = invalidTimes[activity.getDay()];

      if (conflicts.length === 0) {
        conflicts.push(activity);
        return true;
      }

      for (var i = 0; i < conflicts.length; i++) {
        var existingActivity = conflicts[i];
        if (activity.hasTimeClashWith(existingActivity)) {
          return false;
        }
      }
      conflicts.push(activity);
      return true;
    });

    return valid;
  }

  /**
   * Calculates the total number of hours spent at uni.
   */
  function getHours() {
    var dayInfo = getDays();

    var hours = Object.keys(dayInfo).reduce(function (totalHours, day) {
      totalHours += getTimeDiffInHours(dayInfo[day].finish, dayInfo[day].start);
      return totalHours;
    }, 0);

    return hours;
  }

  /**
   * Gets the difference between two times in hours.
   */
  function getTimeDiffInHours(startTime, finishTime) {
    startTime = getHoursSinceMidnight(startTime);
    finishTime = getHoursSinceMidnight(finishTime);
    return startTime - finishTime;
  }

  /**
   * Gets the number of hours passed since midnight.
   */
  function getHoursSinceMidnight(time) {
    var time    = time / 100,
        hours   = Math.floor(time),
        minutes = (time - hours) / 0.6;

    return (hours + minutes).toFixed(1);
  }

  /**
   * Sorts the timetable.
   */
  function sort(compareFn) {
    activities.sort(compareFn);
  }

  return {
    getActivities: function () { return activities; },
    getDays: getDays,
    getHours: getHours,
    isValid: isValid,
    sort: sort
  };
}

/**
 * Adds minutes to the date object.
 */
Date.prototype.addMinutes = function (minutes) {
  return new Date(this.getTime() + parseInt(minutes) * 60000);
};

/**
 * Returns the 24hr-formatted time.
 */
Date.prototype.get24hrTime = function () {
  return this.getHours() * 100 + this.getMinutes();
};

<commit_msg>Changed the name of an option in the NumberOfDays filter to better reflect the value it holds.<commit_after>// ==UserScript==
// @name         UTS Timetable Generator
// @author       szdc
// @namespace    http://github.com/szdc
// @version      0.1
// @description  Timetable generator for UTS students
// @match        https://mysubjects.uts.edu.au/aplus2015/aptimetable*
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @copyright    2015+, szdc
// ==/UserScript==

$(document).ready(function () {
  'use strict';

  var subjects   = [],
      activities = [];

  init();

  function init() {
    // Find the table rows that hold the timetable data
    var table = $('table[cellspacing="1"] tr');

    // Process each row
    table.each(processRow);
    console.log('Subjects found: ' + subjects.length);

    // Get all activity groups.
    var activityGroups = getAllActivityGroups();
    console.log('Total activity groups: ' + activityGroups.length);

    // Get all timetable combinations
    var timetableCombinations = cartesianProductOf(activityGroups);
    timetableCombinations = timetableCombinations.map(function (timetable) {
      return new Timetable(timetable);
    });
    console.log('Timetable combinations: ' + timetableCombinations.length);

    var timetableList = new TimetableList(timetableCombinations);
  }

  /**
   * Processes a timetable row.
   */
  function processRow() {
    var isSubjectHeader = $(this).find('td > a').length > 0,
        isActivityRow   = $(this).attr('bgcolor') === '#eeeeee';

    if (isSubjectHeader) {
      // New subject
      subjects.push(Subject.fromTableRow($(this)));
    } else if (isActivityRow) {
      // New activity under the latest subject
      var subject = subjects[subjects.length - 1];
      subject.addActivity(Activity.fromTableRow($(this), subject));
    }
  }

  /**
   * Gets an array of all activity groups.
   */
  function getAllActivityGroups() {
    return subjects.reduce(function (activityGroups, subject) {
      var groups = subject.getActivityGroups(),
          keys = Object.keys(groups);

      keys.forEach(function (key) {
        activityGroups.push(groups[key]);
      });

      return activityGroups;
    }, []);
  }

  /**
   * Returns the cartesian product of N arrays.
   */
  function cartesianProductOf(arr) {
    return arr.reduce(function(a, b) {
      var ret = [];
      a.forEach(function(a) {
        b.forEach(function(b) {
          ret.push(a.concat([b]));
        });
      });
      return ret;
    }, [[]]);
  }
});

/**
 * Represents a UTS subject.
 */
function Subject(name, code, semester) {
  if (arguments.length < 3) {
    throw new Error('Too few arguments supplied to create the Subject object.');
  }

  var activityGroups = {};

  /**
   * Adds an activity.
   */
  function addActivity(activity) {
    var type = activity.getType(),
        activities = activityGroups[type];
    if (typeof activityGroups[type] === 'undefined') {
      activityGroups[type] = [];
    }

    var existingActivity = getExistingActivity(activityGroups[type], activity);
    if (existingActivity) {
      existingActivity.addNumbers(activity.getNumbers());
    } else {
      activityGroups[type].push(activity);
    }
  }

  /**
   * Retrieves an activity from the array if it matches the
   * activity given.
   */
  function getExistingActivity(existingActivities, activity) {
    for (var i = 0; i < existingActivities.length; i++) {
      var existingActivity = existingActivities[i];
      if (existingActivity.matches(activity)) {
        return existingActivity;
      }
    }
    return null;
  }

  return {
    addActivity: addActivity,
    getActivityGroups: function () { return activityGroups; },
    getCode: function () { return code; }
  };
}

/**
 * Creates a new Subject object from a table row.
 */
Subject.fromTableRow = function (row) {
  var content = row.find('b:first');

  var name     = content.text().substr(content.text().indexOf(':') + 1).trim(),
      code     = content.children('a').attr('href').match(/\d+/)[0],
      semester = /AUT/.test(content.text()) ? 'autumn' : 'spring';

  return new Subject(name, code, semester);
};

/**
 * Represents a UTS subject activity.
 */
function Activity(type, numbers, day, startTime, duration, finishTime, subject) {
  if (arguments.length < 5) {
    throw new Error('Too few arguments supplied to create the Activity object.');
  }

  /**
   * Determines if two activities clash.
   */
  function hasTimeClashWith(activity) {
    var timeClashExists = startTime < activity.getFinishTime() && finishTime > activity.getStartTime();
    return timeClashExists;
  }

  /**
   * Determines if two activities are essentially identical.
   */
  function matches(activity) {
    return type === activity.getType() && day === activity.getDay() &&
      startTime === activity.getStartTime() && finishTime === activity.getFinishTime() &&
      subject === activity.getSubject();
  }

  /**
   * Appends activity numbers to the numbers array.
   */
  function addNumbers(numbersArray) {
    numbers = numbers.concat(numbersArray);
  }

  function toString() {
    return day + ' ' + startTime + '-' + finishTime + ': ' + subject.getCode() + ' ' + type + ' ' + numbers.join(',') + ' (' + duration + ')';
  }

  return {
    hasTimeClashWith: hasTimeClashWith,
    getStartTime: function () { return startTime; },
    getFinishTime: function () { return finishTime; },
    getType: function () { return type; },
    getNumbers: function () { return numbers; },
    getDay: function () { return day; },
    getSubject: function () { return subject; },
    matches: matches,
    addNumbers: addNumbers,
    toString: toString
  };
}

/**
 * Creates a new Activity object from a table row.
 */
Activity.fromTableRow = function (row, subject) {
  var cells = row.children().map(function() {return this.innerHTML;});

  var type       = cells[0],
      numbers    = [cells[1]],
      day        = cells[2],
      startTime  = cells[3],
      duration   = cells[4],
      finishTime = new Date('1/1/2015 ' + startTime).addMinutes(duration).get24hrTime(),
      startTime = parseInt(startTime.replace(':', ''));

  return new Activity(type, numbers, day, startTime, duration, finishTime, subject);
};

/**
 * Compares two activities.
 */
Activity.compare = function (a, b) {
  var days = { "Mon": 0, "Tue": 1, "Wed": 2, "Thu": 3, "Fri": 4, "Sat": 5, "Sun": 6 };

  if (a.getDay() === b.getDay()) {
    return a.getStartTime() - b.getStartTime();
  } else {
    return days[a.getDay()] - days[b.getDay()];
  }
};

/**
 * Represents a list of Timetable objects.
 */
function TimetableList(timetables) {
  if (arguments.length < 1) {
    throw new Error('Too few arguments supplied to create the TimetableList object.');
  }

  // Filter the list to only valid timetable combinations
  timetables = timetables.filter(function (timetable) {
    return timetable.isValid();
  });
  console.log('Valid timetables: ' + timetables.length);

  /**
   * Returns a filtered array of timetables.
   */
  function filter(filterMethod, args) {
    return timetables.filter(filterMethod, args);
  }

  /**
   * Returns an array of timetables that match multiple filters.
   *
   * @param filters {Array<FilterInfo>}
   * An array of FilterInfo objects
   */
  function filterMany(filters) {
    return timetables.filter(function (timetable) {
      for (var i = 0; i < filters.length; i++) {
        var filter = filters[i];
        var result = filter.method.call(filter.options, timetable);
        if (!filter.method.call(filter.options, timetable)) {
          return false;
        }
      }
      return true;
    });
  }

  /**
   * Sorts the timetables.
   */
  function sort(compareMethod) {
    timetables.sort(compareMethod);
  }

  return {
    getTimetables: function () { return timetables; },
    filter: filter,
    filterMany: filterMany,
    sort: sort
  };
}

/**
 * A static object of common filtering methods.
 */
TimetableList.FilterBy = {
  /**
   * Filters by the number of days the timetable spans.
   *
   * Required: The 2nd parameter of the filter method must
   * contain an object with the following properties:
   *  count {Number}  The maximum allowable number of days
   *  exact {Boolean} Whether or not to also include timetables
   *                  with less days in the results.
   */
  NumberOfDays: function (timetable) {
    if (typeof this.count === 'undefined') {
      throw new Error('Days property missing for filter.');
    }

    var numberOfDays = Object.keys(timetable.getDays()).length;
    return this.exact ? numberOfDays === this.count : numberOfDays <= this.count;
  },

  /**
   * Filters to only timetables that always start later or finish
   * earlier than the specified time.
   *
   * Required: The 2nd parameter of the filter method must
   * contain an object with the following properties:
   *  time  {Number}  The earliest time an activity can start
   *  constraint {String} 'start' for later than; 'finish' for earlier than
   */
  TimeConstraint: function (timetable) {
    if (typeof this.time === 'undefined' || typeof this.constraint === 'undefined') {
      throw new Error('Time or Constraint property missing for filter.');
    }

    return timetable.getActivities().every(function (activity) {
      if (this.constraint === 'start') {
        return activity.getStartTime() >= this.time;
      } else {
        return activity.getFinishTime() <= this.time;
      }
    }, this);
  }
};

/**
 * A static object of common sorting methods.
 */
TimetableList.SortBy = {
  /**
   * Sorts by the total number of hours spent on campus.
   * Hours spent each day = end of last class - start of first class
   */
  HoursOnCampus: function (a, b) {
    return a.getHours() - b.getHours();
  }
};

/**
 * Contains the method and options for a filter.
 */
function FilterInfo(method, options) {
  this.method = method;
  this.options = options;
}

function Timetable(activities) {
  /**
   * Returns an object of days and times that the timetable spans.
   */
  function getDays() {
    var dayInfo = activities.reduce(function (days, activity) {
      var day = activity.getDay(),
          start  = activity.getStartTime(),
          finish = activity.getFinishTime();

      if (typeof days[day] === 'undefined') {
        days[day] = { start: start, finish: finish };
      } else {
        if (start < days[day].start) days[day].start = start;
        if (finish > days[day].finish) days[day].finish = finish;
      }

      return days;
    }, {});

    return dayInfo;
  }

  /**
   * Determines if a timetable is valid.
   * There must be no activity clashes to return true.
   */
  function isValid() {
    var invalidTimes = {
      "Mon": [], "Tue": [], "Wed": [], "Thu": [], "Fri": [], "Sat": [], "Sun": []
    };

    var valid = activities.every(function (activity) {
      var conflicts = invalidTimes[activity.getDay()];

      if (conflicts.length === 0) {
        conflicts.push(activity);
        return true;
      }

      for (var i = 0; i < conflicts.length; i++) {
        var existingActivity = conflicts[i];
        if (activity.hasTimeClashWith(existingActivity)) {
          return false;
        }
      }
      conflicts.push(activity);
      return true;
    });

    return valid;
  }

  /**
   * Calculates the total number of hours spent at uni.
   */
  function getHours() {
    var dayInfo = getDays();

    var hours = Object.keys(dayInfo).reduce(function (totalHours, day) {
      totalHours += getTimeDiffInHours(dayInfo[day].finish, dayInfo[day].start);
      return totalHours;
    }, 0);

    return hours;
  }

  /**
   * Gets the difference between two times in hours.
   */
  function getTimeDiffInHours(startTime, finishTime) {
    startTime = getHoursSinceMidnight(startTime);
    finishTime = getHoursSinceMidnight(finishTime);
    return startTime - finishTime;
  }

  /**
   * Gets the number of hours passed since midnight.
   */
  function getHoursSinceMidnight(time) {
    var time    = time / 100,
        hours   = Math.floor(time),
        minutes = (time - hours) / 0.6;

    return (hours + minutes).toFixed(1);
  }

  /**
   * Sorts the timetable.
   */
  function sort(compareFn) {
    activities.sort(compareFn);
  }

  return {
    getActivities: function () { return activities; },
    getDays: getDays,
    getHours: getHours,
    isValid: isValid,
    sort: sort
  };
}

/**
 * Adds minutes to the date object.
 */
Date.prototype.addMinutes = function (minutes) {
  return new Date(this.getTime() + parseInt(minutes) * 60000);
};

/**
 * Returns the 24hr-formatted time.
 */
Date.prototype.get24hrTime = function () {
  return this.getHours() * 100 + this.getMinutes();
};

<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const PORT = process.env.PORT || 3000;
const proxyRequest = require('express-request-proxy');
//bodyParser help returning json objects
const bodyParser = require('body-parser');
// windows and Linux `postgres://postgres:${process.env.PG_PASSWORD}@localhost:5432/kilovolt`
const DATABASE_URL = process.env.DATABASE_URL || `postgres://localhost:5432/kilovolt`;
//requiring pg: your postgres
const pg = require('pg');

//create a new a dabase clien with our connection string: linking it togather
const dbClient = new pg.Client(DATABASE_URL);
//connect it immediatly
dbClient.connect();
//check db for errors
dbClient.on('error', function(err){
  console.error(err);
})

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(express.static('.'));

app.get(`/doStuff`, function(req, res){
  loadUsers(res)
})

app.get('/github/*', function(req, res) {
  let headers = process.env.GITHUB_TOKEN ? {
    Authorization: `token ${process.env.GITHUB_TOKEN}`
  } : {};
  let url = `${req.params[0]}`;
  (proxyRequest({
    url: url,
    headers: headers
  }))(req, res);
})

function loadMoviesFromJSON(){
  // THis is going to load the moves from a JSON file into the DB.
}

/// TODO Load the users from the github repo and insert them in the DB and update if it doesnt exist.

function loadUsers(response){
  console.log('This is running now')
  let request = require('superagent');
  request.get('https://api.github.com/users/masters3d').end( function(err, res){
    console.log(res.body)
    console.log(new Date())
    response.send(res.body);
  })
}

function loadUsersFromRepo(){
  console.log('This is running now')
  let request = require('superagent');
  request.get('https://api.github.com/users/masters3d').end( function(err, res){
    console.log(res.body)
    console.log(new Date())
  })
}

function loadDb(){
  // USER Creation
  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       users (
         userid SERIAL PRIMARY KEY,
         urlphoto VARCHAR(255) NOT NULL,
         name VARCHAR (255) NOT NULL,
         course VARCHAR (255) NOT NULL
       );
    `
  ) .then(loadUsersFromRepo)
  .catch(console.error);

  // Get the hard coded Movies JSON file and put it in the database
  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       movies (
         movieid SERIAL PRIMARY KEY,
         category VARCHAR(255) NULL,
         name VARCHAR(255) NULL,
         releasedate DATE,
         urlphoto VARCHAR(255) NULL
       );
    `
  ) .then(loadMoviesFromJSON)
  .catch(console.error);

  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       usermovies (
         id SERIAL PRIMARY KEY,
         movieid INTEGER NOT NULL REFERENCES movies(movieid),
         userid INTEGER NOT NULL REFERENCES users(userid)
       );
    `
  ).catch(console.error);
}

loadDb();
app.get('*', function(req, res) {
  res.sendFile('index.html', {root: './'});
})

app.listen(PORT, function() {
  console.info(`App is runnig on port: ${PORT}`);
})
<commit_msg>renamed table, added date column<commit_after>'use strict';

const express = require('express');
const PORT = process.env.PORT || 3000;
const proxyRequest = require('express-request-proxy');
//bodyParser help returning json objects
const bodyParser = require('body-parser');
// windows and Linux `postgres://postgres:${process.env.PG_PASSWORD}@localhost:5432/kilovolt`
const DATABASE_URL = process.env.DATABASE_URL || `postgres://postgres:${process.env.PG_PASSWORD}@localhost:5432/kilovolt`
// `postgres://localhost:5432/kilovolt`;
//requiring pg: your postgres
const pg = require('pg');

//create a new a dabase clien with our connection string: linking it togather
const dbClient = new pg.Client(DATABASE_URL);
//connect it immediatly
dbClient.connect();
//check db for errors
dbClient.on('error', function(err){
  console.error(err);
})

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use(express.static('.'));

app.get('/github/*', function(req, res) {
  let headers = process.env.GITHUB_TOKEN ? {
    Authorization: `token ${process.env.GITHUB_TOKEN}`
  } : {};
  let url = `${req.params[0]}`;
  (proxyRequest({
    url: url,
    headers: headers
  }))(req, res);
})

function loadMoviesFromJSON(){
  // THis is going to load the moves from a JSON file into the DB.
}

/// TODO Load the users from the github repo and insert them in the DB and update if it doesnt exist.

function loadUsersFromRepo(){
  console.log('This is running now')
  let request = require('superagent');
  request.get('https://api.github.com/users/masters3d').end( function(err, res){
    console.log(res.body)
    console.log(new Date())
  })
}

function loadDb(){
  // USER Creation
  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       users (
         userid SERIAL PRIMARY KEY,
         urlphoto VARCHAR(255) NOT NULL,
         name VARCHAR (255) NOT NULL,
         course VARCHAR (255) NOT NULL
       );
    `
  ) .then(loadUsersFromRepo)
  .catch(console.error);

  // Get the hard coded Movies JSON file and put it in the database
  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       movies (
         movieid SERIAL PRIMARY KEY,
         category VARCHAR(255) NULL,
         name VARCHAR(255) NULL,
         releasedate DATE,
         urlphoto VARCHAR(255) NULL
       );
    `
  ) .then(loadMoviesFromJSON)
  .catch(console.error);

  dbClient.query(
    `CREATE TABLE IF NOT EXISTS
       favorites (
         id SERIAL PRIMARY KEY,
         userid INTEGER NOT NULL REFERENCES users(userid),
         movieid INTEGER NOT NULL REFERENCES movies(movieid),
         dateliked DATE
       );
    `
  ).catch(console.error);
}

loadDb();
app.get('*', function(req, res) {
  res.sendFile('index.html', {root: './'});
})

app.listen(PORT, function() {
  console.info(`App is runnig on port: ${PORT}`);
})
<|endoftext|>


<commit_before>import Ember from 'ember';

const { run, aliasMethod, guidFor, assign, inject: { service }, set } = Ember;

export default Ember.Service.extend({
  windowMessengerEvents: service(),

  callbacks: null,
  targets: null,

  init() {
    this._super(...arguments);
    this.setProperties({
      targets: {},
      callbacks: {}
    });
    this.get('windowMessengerEvents').on('from:ember-window-messenger-server', this, this._onMessage);
  },

  /**
   * Add new contentWindow target
   *
   * @param {String} name         String name of the target
   * @param {contentWindow} targetWindow DOM contentWindow
   * @public
   */
  addTarget(name, targetWindow) {
    set(this.targets, name, targetWindow);
  },

  /**
   * Remove contentWindow target
   *
   * @param  {String} name
   * @public
   */
  removeTarget(name) {
    delete this.get('targets')[name];
  },

  /**
   * Tests whether a target is currently registered and open.
   *
   * @param  {String} name
   * @public
   *
   * returns: boolean
   */
  hasTarget(name) {
    if (!(name in this.targets)) {
      return false;
    }
    return (this.targets[name].opener && !this.targets[name].opener.closed);
  },

  /*
   * @private
   * @return {Window}
   */
  _getWindow() {
    return window;
  },

  /**
   * Parse <target>:<request> uri
   *
   * @param  {String} uri
   * @return {Object}
   */
  _parseURI(uri) {
    let split = uri.split(':');
    let resource = split[1] || split[0];
    return {
      target: split[1] ? split[0] : 'parent',
      resource: Ember.String.dasherize(resource)
    };
  },

  /**
   * Determine if resource target is parent or not
   *
   * @param  {String}  target
   * @private
   * @return {Boolean}
   */
  _isTargetParent(target) {
    let win = this._getWindow();
    let isEmbedded = win.self !== win.top || win.opener;
    return isEmbedded || target === 'parent';
  },

  /**
   * @private
   * @return {Window}
   */
  _getWindowParent() {
    let win = this._getWindow();
    return win.opener || win.parent;
  },

  /**
   * @param {String} target
   * @private
   * @return {Object}
   */
  _targetFor(target) {
    return this._isTargetParent(target) ? this._getWindowParent() : this.targets[target];
  },

  /**
   * @param {String} target
   * @private
   * @return {Object}
   */
  _targetOriginFor(target) {
    return this.get(`targetOriginMap.${target}`);
  },

  /**
   * Fetch data from server side
   *
   * @param  {String} path
   * @param  {Object} queryParams
   * @return {Promise}
   */
  fetch(path, queryParams) {
    let uri = this._parseURI(path);
    let targetName = uri.target;
    let queryObject = queryParams ? assign({}, queryParams) : {};

    let targetOrigin = this._targetOriginFor(targetName);
    Ember.assert(`Target origin for target: ${targetName} does not exist`, targetOrigin);

    let target = this._targetFor(targetName);
    Ember.assert(`Target window is not registered for: ${targetName}`, target);

    return new Ember.RSVP.Promise((resolve, reject) => {
      let uuid = guidFor(queryObject);
      let query = {
        id: uuid,
        type: 'ember-window-messenger-client',
        name: uri.resource,
        query: queryObject
      };

      this.callbacks[uuid] = {
        success: (json) => {
          run(null, resolve, json);
        },
        error: (json) => {
          run(null, reject, json);
        }
      };
      target.postMessage(JSON.stringify(query), targetOrigin);
    }, `ember-window-messenger: ${path}`);
  },

  /**
   * Alias to fetch method, for providing semantic sugar
   *
   * @public
   */
  rpc: aliasMethod('fetch'),

  /**
   * Handle message event from Messenger Events
   *
   * @private
   * @param  {Object} message
   */
  _onMessage(message) {
    let { response, id, error } = message;
    let inQueue = this.callbacks[id];

    if (Ember.typeOf(inQueue) === 'object') {
      if (error) {
        inQueue.error(response);
      } else {
        inQueue.success(response);
      }
    }
    delete this.callbacks[id];
  },

  willDestroy() {
    this._super(...arguments);
    this.get('windowMessengerEvents').off('from:ember-window-messenger-server', this, this._onMessage);
  }
});
<commit_msg>Fix @return for hasTarget method<commit_after>import Ember from 'ember';

const { run, aliasMethod, guidFor, assign, inject: { service }, set } = Ember;

export default Ember.Service.extend({
  windowMessengerEvents: service(),

  callbacks: null,
  targets: null,

  init() {
    this._super(...arguments);
    this.setProperties({
      targets: {},
      callbacks: {}
    });
    this.get('windowMessengerEvents').on('from:ember-window-messenger-server', this, this._onMessage);
  },

  /**
   * Add new contentWindow target
   *
   * @param {String} name         String name of the target
   * @param {contentWindow} targetWindow DOM contentWindow
   * @public
   */
  addTarget(name, targetWindow) {
    set(this.targets, name, targetWindow);
  },

  /**
   * Remove contentWindow target
   *
   * @param  {String} name
   * @public
   */
  removeTarget(name) {
    delete this.get('targets')[name];
  },

  /**
   * Tests whether a target is currently registered and open.
   *
   * @param  {String} name
   * @public
   *
   * @return {Boolean}
   */
  hasTarget(name) {
    if (!(name in this.targets)) {
      return false;
    }
    return (this.targets[name].opener && !this.targets[name].opener.closed);
  },

  /*
   * @private
   * @return {Window}
   */
  _getWindow() {
    return window;
  },

  /**
   * Parse <target>:<request> uri
   *
   * @param  {String} uri
   * @return {Object}
   */
  _parseURI(uri) {
    let split = uri.split(':');
    let resource = split[1] || split[0];
    return {
      target: split[1] ? split[0] : 'parent',
      resource: Ember.String.dasherize(resource)
    };
  },

  /**
   * Determine if resource target is parent or not
   *
   * @param  {String}  target
   * @private
   * @return {Boolean}
   */
  _isTargetParent(target) {
    let win = this._getWindow();
    let isEmbedded = win.self !== win.top || win.opener;
    return isEmbedded || target === 'parent';
  },

  /**
   * @private
   * @return {Window}
   */
  _getWindowParent() {
    let win = this._getWindow();
    return win.opener || win.parent;
  },

  /**
   * @param {String} target
   * @private
   * @return {Object}
   */
  _targetFor(target) {
    return this._isTargetParent(target) ? this._getWindowParent() : this.targets[target];
  },

  /**
   * @param {String} target
   * @private
   * @return {Object}
   */
  _targetOriginFor(target) {
    return this.get(`targetOriginMap.${target}`);
  },

  /**
   * Fetch data from server side
   *
   * @param  {String} path
   * @param  {Object} queryParams
   * @return {Promise}
   */
  fetch(path, queryParams) {
    let uri = this._parseURI(path);
    let targetName = uri.target;
    let queryObject = queryParams ? assign({}, queryParams) : {};

    let targetOrigin = this._targetOriginFor(targetName);
    Ember.assert(`Target origin for target: ${targetName} does not exist`, targetOrigin);

    let target = this._targetFor(targetName);
    Ember.assert(`Target window is not registered for: ${targetName}`, target);

    return new Ember.RSVP.Promise((resolve, reject) => {
      let uuid = guidFor(queryObject);
      let query = {
        id: uuid,
        type: 'ember-window-messenger-client',
        name: uri.resource,
        query: queryObject
      };

      this.callbacks[uuid] = {
        success: (json) => {
          run(null, resolve, json);
        },
        error: (json) => {
          run(null, reject, json);
        }
      };
      target.postMessage(JSON.stringify(query), targetOrigin);
    }, `ember-window-messenger: ${path}`);
  },

  /**
   * Alias to fetch method, for providing semantic sugar
   *
   * @public
   */
  rpc: aliasMethod('fetch'),

  /**
   * Handle message event from Messenger Events
   *
   * @private
   * @param  {Object} message
   */
  _onMessage(message) {
    let { response, id, error } = message;
    let inQueue = this.callbacks[id];

    if (Ember.typeOf(inQueue) === 'object') {
      if (error) {
        inQueue.error(response);
      } else {
        inQueue.success(response);
      }
    }
    delete this.callbacks[id];
  },

  willDestroy() {
    this._super(...arguments);
    this.get('windowMessengerEvents').off('from:ember-window-messenger-server', this, this._onMessage);
  }
});
<|endoftext|>


<commit_before>/*
 * Node Express app server with API endpoints
 * --------------------------------------------
 * Creates a SQLite3 schema and users table for inserting, updating, and deleting
 * user records.  Passwords are salted and a combined salt+hash is stored in the db.
 * Also signs and unsigns cookies which it uses to persist the client's authentication.
 *
*/
/*added this because the original wouldn't connect*/
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname)).listen(8080);
//
var express = require('express'),
    http = require('http'),

    config = require("./config"),
   // bcrypt = require("bcrypt"),
    sqlite = require("sqlite3"),
    _ = require("underscore"),

    app = express(),
    port = process.env.PORT || config.port,
    server = http.createServer(app).listen(port);

// Initialize sqlite and create our db if it doesnt exist
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database(__dirname+'/db/bb-login.db');

// Create our users table if it doesn't exist
db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, username TEXT UNIQUE, password TEXT, auth_token TEXT UNIQUE)");

// Allow node to be run with proxy passing
app.enable('trust proxy');

// Logging config
app.configure('local', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
    app.use(express.errorHandler());
});

// Compression (gzip)
app.use( express.compress() );
app.use( express.methodOverride() );
app.use( express.urlencoded() );            // Needed to parse POST data sent as JSON payload
app.use( express.json() );

// Cookie config
app.use( express.cookieParser( config.cookieSecret ) );           // populates req.signedCookies
app.use( express.cookieSession( config.sessionSecret ) );         // populates req.session, needed for CSRF

// We need serverside view templating to initially set the CSRF token in the <head> metadata
// Otherwise, the html could just be served statically from the public directory
app.set('view engine', 'html');
app.set('views', __dirname + '/views' );
app.engine('html', require('hbs').__express);

app.use(express.static(__dirname+'/public'));
app.use(express.csrf());

app.use( app.router );

app.get("/", function(req, res){
    res.render('index', { csrfToken: req.csrfToken() });
});

// GET /api/auth
// @desc: checks a user's auth status based on cookie
app.get("/api/auth", function(req, res){
    db.get("SELECT * FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, user){
        if(user){
            res.json({ user: _.omit(user, ['password', 'auth_token']) });
        } else {
            res.json({ error: "Client has no valid login cookies."  });
        }
    });
});

// POST /api/auth/login
// @desc: logs in a user
app.post("/api/auth/login", function(req, res){
    db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
        if(user){

            // Compare the POSTed password with the encrypted db password
            if( bcrypt.compareSync( req.body.password, user.password)){
                res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge  });
                res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge  });

                // Correct credentials, return the user object
                res.json({ user: _.omit(user, ['password', 'auth_token']) });

            } else {
                // Username did not match password given
                res.json({ error: "Invalid username or password."  });
            }
        } else {
            // Could not find the username
            res.json({ error: "Username does not exist."  });
        }
    });
});

// POST /api/auth/signup
// @desc: creates a user
app.post("/api/auth/signup", function(req, res){
    db.serialize(function(){
        db.run("INSERT INTO users(username, name, password, auth_token) VALUES (?, ?, ?, ?)",
                [ req.body.username, req.body.name, bcrypt.hashSync(req.body.password, 8), bcrypt.genSaltSync(8) ], function(err, rows){
            if(err){
                res.json({ error: "Username has been taken.", field: "username" });
            } else {

                // Retrieve the inserted user data
                db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
                    if(!user) {
                        console.log(err, rows);
                        res.json({ error: "Error while trying to register user." });
                    } else {

                        // Set the user cookies and return the cleansed user data
                        res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge  });
                        res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge  });
                        res.json({ user: _.omit(user, ['password', 'auth_token']) });
                    }
                });
            }
        });
    });
});

// POST /api/auth/logout
// @desc: logs out a user, clearing the signed cookies
app.post("/api/auth/logout", function(req, res){
    res.clearCookie('user_id');
    res.clearCookie('auth_token');
    res.json({ success: "User successfully logged out." });
});

// POST /api/auth/remove_account
// @desc: deletes a user
app.post("/api/auth/remove_account", function(req, res){
    db.run("DELETE FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, rows){
        if(err){
            res.json({ error: "Error while trying to delete user." });
        } else {
            res.clearCookie('user_id');
            res.clearCookie('auth_token');
            res.json({ success: "User successfully deleted." });
        }
    });
});

// Close the db connection on process exit
// (should already happen, but to be safe)
process.on("exit", function(){
    db.close();
});

console.log("STARTUP:: Express server listening on port::", port, ", environment:: ", app.settings.env);
<commit_msg>Update server.js<commit_after>/*
 * Node Express app server with API endpoints
 * --------------------------------------------
 * Creates a SQLite3 schema and users table for inserting, updating, and deleting
 * user records.  Passwords are salted and a combined salt+hash is stored in the db.
 * Also signs and unsigns cookies which it uses to persist the client's authentication.
 *
*/
/*added this because the original wouldn't connect*/
var connect = require('connect');
var serveStatic = require('serve-static');
connect().use(serveStatic(__dirname)).listen(8080);
//
var express = require('express'),
    http = require('http'),

    config = require("./config"),
    bcrypt = require("bcrypt"),
    sqlite = require("sqlite3"),
    _ = require("underscore"),

    app = express(),
    port = process.env.PORT || config.port,
    server = http.createServer(app).listen(port);

// Initialize sqlite and create our db if it doesnt exist
var sqlite3 = require("sqlite3").verbose();
var db = new sqlite3.Database(__dirname+'/db/bb-login.db');

// Create our users table if it doesn't exist
db.run("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, username TEXT UNIQUE, password TEXT, auth_token TEXT UNIQUE)");

// Allow node to be run with proxy passing
app.enable('trust proxy');

// Logging config
app.configure('local', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
    app.use(express.errorHandler());
});

// Compression (gzip)
app.use( express.compress() );
app.use( express.methodOverride() );
app.use( express.urlencoded() );            // Needed to parse POST data sent as JSON payload
app.use( express.json() );

// Cookie config
app.use( express.cookieParser( config.cookieSecret ) );           // populates req.signedCookies
app.use( express.cookieSession( config.sessionSecret ) );         // populates req.session, needed for CSRF

// We need serverside view templating to initially set the CSRF token in the <head> metadata
// Otherwise, the html could just be served statically from the public directory
app.set('view engine', 'html');
app.set('views', __dirname + '/views' );
app.engine('html', require('hbs').__express);

app.use(express.static(__dirname+'/public'));
app.use(express.csrf());

app.use( app.router );

app.get("/", function(req, res){
    res.render('index', { csrfToken: req.csrfToken() });
});

// GET /api/auth
// @desc: checks a user's auth status based on cookie
app.get("/api/auth", function(req, res){
    db.get("SELECT * FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, user){
        if(user){
            res.json({ user: _.omit(user, ['password', 'auth_token']) });
        } else {
            res.json({ error: "Client has no valid login cookies."  });
        }
    });
});

// POST /api/auth/login
// @desc: logs in a user
app.post("/api/auth/login", function(req, res){
    db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
        if(user){

            // Compare the POSTed password with the encrypted db password
            if( bcrypt.compareSync( req.body.password, user.password)){
                res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge  });
                res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge  });

                // Correct credentials, return the user object
                res.json({ user: _.omit(user, ['password', 'auth_token']) });

            } else {
                // Username did not match password given
                res.json({ error: "Invalid username or password."  });
            }
        } else {
            // Could not find the username
            res.json({ error: "Username does not exist."  });
        }
    });
});

// POST /api/auth/signup
// @desc: creates a user
app.post("/api/auth/signup", function(req, res){
    db.serialize(function(){
        db.run("INSERT INTO users(username, name, password, auth_token) VALUES (?, ?, ?, ?)",
                [ req.body.username, req.body.name, bcrypt.hashSync(req.body.password, 8), bcrypt.genSaltSync(8) ], function(err, rows){
            if(err){
                res.json({ error: "Username has been taken.", field: "username" });
            } else {

                // Retrieve the inserted user data
                db.get("SELECT * FROM users WHERE username = ?", [ req.body.username ], function(err, user){
                    if(!user) {
                        console.log(err, rows);
                        res.json({ error: "Error while trying to register user." });
                    } else {

                        // Set the user cookies and return the cleansed user data
                        res.cookie('user_id', user.id, { signed: true, maxAge: config.cookieMaxAge  });
                        res.cookie('auth_token', user.auth_token, { signed: true, maxAge: config.cookieMaxAge  });
                        res.json({ user: _.omit(user, ['password', 'auth_token']) });
                    }
                });
            }
        });
    });
});

// POST /api/auth/logout
// @desc: logs out a user, clearing the signed cookies
app.post("/api/auth/logout", function(req, res){
    res.clearCookie('user_id');
    res.clearCookie('auth_token');
    res.json({ success: "User successfully logged out." });
});

// POST /api/auth/remove_account
// @desc: deletes a user
app.post("/api/auth/remove_account", function(req, res){
    db.run("DELETE FROM users WHERE id = ? AND auth_token = ?", [ req.signedCookies.user_id, req.signedCookies.auth_token ], function(err, rows){
        if(err){
            res.json({ error: "Error while trying to delete user." });
        } else {
            res.clearCookie('user_id');
            res.clearCookie('auth_token');
            res.json({ success: "User successfully deleted." });
        }
    });
});

// Close the db connection on process exit
// (should already happen, but to be safe)
process.on("exit", function(){
    db.close();
});

console.log("STARTUP:: Express server listening on port::", port, ", environment:: ", app.settings.env);
<|endoftext|>


<commit_before>angular.module('ngRSS', ['ngRemoteStorage', 'ngSockethubClient']).

/**
 * settings service
 */
value('configHelper', {
  exists: function exists(config, cfg) {
    if (!cfg) {
      cfg = config;
    }

    for (var key in config) {
      if (!cfg[key]) {
        return false;
      }
    }
    return true;
  },
  set: function (config, cfg) {
    for (var key in cfg) {
      config[key] = cfg[key];
    }
    return config;
  }
}).

value('util', {
  isEmptyObject: function isEmptyObject(obj) {
    for(var prop in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, prop)) {
        return false;
      }
    }
    return true;
  }
}).

///////////////////////////////////////////////////////////////////////////
//
// FACTORY
//
///////////////////////////////////////////////////////////////////////////

/**
 * Factory: RSS
 */
factory('RSS', ['$q', 'SH', 'configHelper', 'RS', 'RSutil', '$rootScope',
function ($q, SH, CH, RS, RSutil, $rootScope) {

  var config = {};
  var data = {
    articles: [],
    info: {}
  };
  var func = {};

  /****
   * CONFIG MANAGEMENT
   ********************/
  function exists(cfg) {
    return CH.exists(config, cfg);
  }

  func.setConfig = function set(cfg) {
    var defer = $q.defer();
    if (exists(cfg)) {
      if (cfg) {
        CH.set(config, cfg);
      }
      defer.resolve(config);
    } else {
      defer.reject('config not set correctly');
    }
    return defer.promise;
  };

  /****
   * FEED MANAGEMENT
   ******************/
  // grab whatever feeds exists in remoteStorage right away
  (function getFeedUrls() {
    RS.call('rss', 'getAll', ['']).then(function (urls) {
      for (var key in urls) {
        var url = urls[key].url;
        data.info[url] = urls[key];  // asign existing feed info to data struct
        func.fetchFeed(url);  // fetch articles from sockethub
      }
    }, function (err) {
      console.log('error: unable to get feed list from remoteStorage: ', err);
    });
  })();

  // add a new feed to remoteStorage
  func.addFeed = function addFeed(obj) {
    var defer = $q.defer();
    RS.call('rss', 'add', [obj]).then(function (m) {
      console.log('feed added: ', obj);
      data.info[obj.url] = obj;
      func.fetchFeed(obj.url);
      defer.resolve(m);
    }, function (err) {
      defer.reject(err);
    });
    return defer.promise;
  };

  /****
   * ARTICLE MANAGEMENT
   *********************/
  // update or create an article entry on remoteStorage
  func.updateArticle = function updateArticle(obj) {
    var defer = $q.defer();
    var s_obj = {
      link: obj.object.link,
      title: obj.object.title,
      date: obj.object.data,
      html: obj.object.html,
      text: obj.object.text,
      brief_html: obj.object.brief_html,
      brief_text: obj.object.brief_text,
      read: (obj.object.read) ? true : false,
      source_link: obj.actor.address,
      source_title: obj.actor.name
    };

    RS.call('articles', 'update', [s_obj]).then(function (m) {
      //console.log('article added: ', m);
      //data.info[obj.url] = obj;
      //func.fetchFeed(obj.url);
      defer.resolve(m);
    }, function (err) {
      defer.reject(err);
    });
    return defer.promise;
  };

  /****
   * FEED FETCHING
   ****************/
  // issue orders to fetch feeds from sockethub
  func.fetchFeed = function fetch(url) {
    var defer = $q.defer();
    var msg = {
      actor: {},
      target: [{}]
    };
    msg.target[0].address = url;
    msg.platform = 'rss';
    msg.verb = 'fetch';
    msg.actor.address = "rss";
    console.log("FETCH: ", msg);
    SH.submit(msg, 5000).then(defer.resolve, defer.reject);
    return defer.promise;
  };

  // detect when new articles are received from Sockethub
  SH.on('rss', 'message', function (m) {
    //console.log("RSS received message");
    var key = m.actor.address;
    if (!m.status) {
      $rootScope.$broadcast('message', {
        type: 'error',
        message: m.target[0].address + ' ' + m.object.message
      });
    }

    if (!data.info[key]) {
      console.log("*** RSS: key doesn't match a feed entry [ " + key +" ]: ", m);
      if (!m.status) {
        // actor not found and error detected, probably bad feed
        var t_key = m.target[0].address;
        //console.log('CHECKING: [tkey:' + t_key + '] data.info: ', data.info);
        if (data.info[t_key]) {
          console.log('PROBLEM FEED SETTINGS:', data.info[t_key]);
          data.info[t_key]['loaded'] = true;
          data.info[t_key]['error'] = true;
          data.info[t_key]['errorMsg'] = m.object.message;
        }
      }
    } else if (data.info[key].name !== m.actor.name) {
      data.info[key]['name'] = m.actor.name;
      func.addFeed(data.info[key]);
      data.info[key]['loaded'] = true;
    } else {
      //console.log("*** Names already match: " + m.actor.name);
      data.info[key]['loaded'] = true;
    }

    if (!m.object.read) {
      m.object.read = false;
    }

    if (m.status) {
      console.log('adding: ', m);
      var id = RSutil.encode(m.object.link);
      console.log('ID: ', id);
      RS.call('articles', 'get', [id]).then(function (a) {
        if (a) {
          console.log('ARTICLE FETCH from RS: ', a);
          m.object.read = a.read;
        }

        if (!m.object.read) {
          data.info[key].unread = (typeof data.info[key].unread === "number") ? data.info[key].unread + 1 : 1;
        }
        data.articles.push(m);
      }, function (e) {
        console.log("ARTICL FETCH ERROR: ", e);
        data.articles.push(m);
      });
    }
  });

  return {
    config: config,
    data: data,
    func: func
  };
}]).

///////////////////////////////////////////////////////////////////////////
//
// CONTROLLERS
//
///////////////////////////////////////////////////////////////////////////

/**
 * controller: addFeedCtrl
 */
controller('addFeedCtrl',
['$scope', 'RSS', '$rootScope',
function ($scope, RSS, $rootScope) {
  $scope.url = '';
  $scope.adding = false;

  $scope.add = function (url) {
    $scope.adding = true;

    var obj = {
      url: url,
      name: url,
      cache_articles: 20,
      last_fetched: new Date().getTime()
    };

    RSS.func.addFeed(obj).then(function (m) {
      console.log('rss feed url saved!: ', m);
      $rootScope.$broadcast('closeModalAddFeed');
      $scope.adding = false;
      $rootScope.$broadcast('message', {type: 'success', message: 'RSS feed added: '+obj.url});
    }, function (err) {
      console.log('rss feed url save failed!: ', err);
      $rootScope.$broadcast('message', {type: 'error', message: err.message});
    });
  };

}]).

/**
 * controller: feedCtrl
 */
controller('feedCtrl',
['$scope', 'RSS', 'util', '$rootScope',
function ($scope, RSS, util, $rootScope) {

  $scope.model = {};
  $scope.model.feeds = RSS.data;
  $scope.model.message = '';
  $scope.model.loading = true;
  $scope.model.feeds.current = {
    name: '',
    indexes: []
  };

  $scope.isSelected = function(url, inclusive) {
    if ($scope.model.feeds.current.indexes.length === 0) {
      if ((inclusive) || (!url)) {
        return true;
      } else {
        return false;
      }
    } else {
      for (var i = 0, num = $scope.model.feeds.current.indexes.length; i < num; i = i + 1) {
        if ($scope.model.feeds.current.indexes[i] === url) {
          return true;
        }
      }
    }
    return false;
  };

  $scope.switchFeed = function (url) {
    if (!url) {
      $scope.model.feeds.current.name = '';
      $scope.model.feeds.current.indexes = [];
    } else {
      $scope.model.feeds.current.name = RSS.data.info[url].name;
      $scope.model.feeds.current.indexes = [url];
    }
  };

  $scope.showFeedSettings = function (url) {
    $rootScope.$broadcast('showModalFeedSettings', {locked: false});
  };

  $scope.markRead = function (url) {
    console.log('markRead Called!');
    for (var i = 0, num = $scope.model.feeds.articles.length; i < num; i = i + 1) {
      //console.log('A.link: ' + $scope.model.feeds.articles[i].object.link + ' url: '+url);
      if ($scope.model.feeds.articles[i].object.link === url) {
        if (!$scope.model.feeds.articles[i].object.read) {
          //console.log('subtracting 1 from : '+ $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread);
          $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread =
              $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread - 1;
          $scope.model.feeds.articles[i].object.read = true;
          RSS.func.updateArticle($scope.model.feeds.articles[i]);
        }
        return;
      }
    }
  };

  // display friendly message when no feeds are loaded
  if (util.isEmptyObject($scope.model.feeds.info)) {
    $scope.model.message = "loading feed list...";
  }

  $scope.$watch('$scope.model.feeds.info', function (newVal, oldVal) {
    if (!util.isEmptyObject($scope.model.feeds.info)) {
      $scope.model.message = '';
    } else {
      $scope.model.message = "no feeds yet, add some!";
    }
  });

  $scope.$watch('$scope.model.feeds.articles', function (newVal, oldVal) {
    console.log('article changed! ', newVal, oldVal);
  });

  $rootScope.$on('SockethubConnectFailed', function (event, e) {
    $rootScope.$broadcast('message', {
      message: e.message,
      type: 'error',
      timeout: false
    });
    $scope.model.loading = true;
    $rootScope.$broadcast('showModalSockethubSettings', {locked: false});
  });
}]).

///////////////////////////////////////////////////////////////////////////
//
// DIRECTIVES
//
///////////////////////////////////////////////////////////////////////////

/**
 * directive: feedList
 */
directive('feedList', [
function () {
  return {
    restrict: 'E',
    scope: {
      'feeds': '='
    },
    template: '

' +
              '{{ message }}' +
              '
' +
              '
' +
              '    ' +
              '      All Items' +
              '    ' +
              '
' +
              '
' +
              '    ' +
              '    ' +
              '
' +
              '      ' +
              '        {{ f.name }} {{ f.unread }}' +
              '      ' +
              '
' +
              '
' +
              '
',
    transclude: true
  };
}]).

/**
 * directive: articles
 */
directive('articles', [
function () {
  return {
    restrict: 'E',
    scope: {
      'feeds': '='
    },
    template: '
{{ feeds.current.name }}
' +
              '
' +
              '
{{ a.object.title }}
' +
              '
feed: {{ a.actor.name }}
' +
              '
date: {{ a.object.date | date }}
' +
              '
article link: {{ a.object.link }}
' +
              '

' +
              '
',
    link: function (scope, element, attrs) {
      //console.log('#### LINK FUNCTION: scope: ', scope);
      //console.log('#### LINK FUNCTION: element: ', element);
      //console.log('#### LINK FUNCTION: attrs: ', attrs);

      scope.$watch(attrs.feeds, function (val) {
        console.log('WATCH attrs.feeds', val);
      });

      var divs = document.getElementsByClassName('article');
      for (var i = 0, num = divs.length; i < num; i = i + 1) {
        // grab all of the links inside the div
        var links = divs[i].getElementsByTagName('a');
        // Loop through those links and attach the target attribute
        for (var j = 0, jnum = links.length; j < jnum; j = j + 1) {
          // the _blank will make the link open in new window
          links[j].setAttribute('target', '_blank');
        }
      }
    }
  };
}]);<commit_msg>informational messages<commit_after>angular.module('ngRSS', ['ngRemoteStorage', 'ngSockethubClient']).

/**
 * settings service
 */
value('configHelper', {
  exists: function exists(config, cfg) {
    if (!cfg) {
      cfg = config;
    }

    for (var key in config) {
      if (!cfg[key]) {
        return false;
      }
    }
    return true;
  },
  set: function (config, cfg) {
    for (var key in cfg) {
      config[key] = cfg[key];
    }
    return config;
  }
}).

value('util', {
  isEmptyObject: function isEmptyObject(obj) {
    for(var prop in obj) {
      if (Object.prototype.hasOwnProperty.call(obj, prop)) {
        return false;
      }
    }
    return true;
  }
}).

///////////////////////////////////////////////////////////////////////////
//
// FACTORY
//
///////////////////////////////////////////////////////////////////////////

/**
 * Factory: RSS
 */
factory('RSS', ['$q', 'SH', 'configHelper', 'RS', 'RSutil', '$rootScope',
function ($q, SH, CH, RS, RSutil, $rootScope) {

  var config = {};
  var data = {
    articles: [],
    info: {}
  };
  var func = {};

  /****
   * CONFIG MANAGEMENT
   ********************/
  function exists(cfg) {
    return CH.exists(config, cfg);
  }

  func.setConfig = function set(cfg) {
    var defer = $q.defer();
    if (exists(cfg)) {
      if (cfg) {
        CH.set(config, cfg);
      }
      defer.resolve(config);
    } else {
      defer.reject('config not set correctly');
    }
    return defer.promise;
  };

  /****
   * FEED MANAGEMENT
   ******************/
  // grab whatever feeds exists in remoteStorage right away
  (function getFeedUrls() {
    RS.call('rss', 'getAll', ['']).then(function (urls) {
      for (var key in urls) {
        var url = urls[key].url;
        data.info[url] = urls[key];  // asign existing feed info to data struct
        func.fetchFeed(url);  // fetch articles from sockethub
      }
    }, function (err) {
      console.log('error: unable to get feed list from remoteStorage: ', err);
      $rootScope.$broadcast('message', {
        message: 'unable to get feed list from remotestorage',
        type: 'error',
        timeout: false
      });
    });
  })();

  // add a new feed to remoteStorage
  func.addFeed = function addFeed(obj) {
    var defer = $q.defer();
    RS.call('rss', 'add', [obj]).then(function (m) {
      console.log('feed added: ', obj);
      data.info[obj.url] = obj;
      func.fetchFeed(obj.url);
      defer.resolve(m);
    }, function (err) {
      defer.reject(err);
    });
    return defer.promise;
  };

  /****
   * ARTICLE MANAGEMENT
   *********************/
  // update or create an article entry on remoteStorage
  func.updateArticle = function updateArticle(obj) {
    var defer = $q.defer();
    var s_obj = {
      link: obj.object.link,
      title: obj.object.title,
      date: obj.object.data,
      html: obj.object.html,
      text: obj.object.text,
      brief_html: obj.object.brief_html,
      brief_text: obj.object.brief_text,
      read: (obj.object.read) ? true : false,
      source_link: obj.actor.address,
      source_title: obj.actor.name
    };

    RS.call('articles', 'update', [s_obj]).then(function (m) {
      //console.log('article added: ', m);
      //data.info[obj.url] = obj;
      //func.fetchFeed(obj.url);
      defer.resolve(m);
    }, function (err) {
      defer.reject(err);
    });
    return defer.promise;
  };

  /****
   * FEED FETCHING
   ****************/
  // issue orders to fetch feeds from sockethub
  func.fetchFeed = function fetch(url) {
    var defer = $q.defer();
    var msg = {
      actor: {},
      target: [{}]
    };
    msg.target[0].address = url;
    msg.platform = 'rss';
    msg.verb = 'fetch';
    msg.actor.address = "rss";
    console.log("FETCH: ", msg);
    SH.submit(msg, 5000).then(defer.resolve, defer.reject);
    return defer.promise;
  };

  // detect when new articles are received from Sockethub
  SH.on('rss', 'message', function (m) {
    //console.log("RSS received message");
    var key = m.actor.address;
    if (!m.status) {
      $rootScope.$broadcast('message', {
        type: 'error',
        message: m.target[0].address + ' ' + m.object.message
      });
    }

    if (!data.info[key]) {
      console.log("*** RSS: key doesn't match a feed entry [ " + key +" ]: ", m);
      if (!m.status) {
        // actor not found and error detected, probably bad feed
        var t_key = m.target[0].address;
        //console.log('CHECKING: [tkey:' + t_key + '] data.info: ', data.info);
        if (data.info[t_key]) {
          console.log('PROBLEM FEED SETTINGS:', data.info[t_key]);
          data.info[t_key]['loaded'] = true;
          data.info[t_key]['error'] = true;
          data.info[t_key]['errorMsg'] = m.object.message;
        }
      }
    } else if (data.info[key].name !== m.actor.name) {
      data.info[key]['name'] = m.actor.name;
      func.addFeed(data.info[key]);
      data.info[key]['loaded'] = true;
    } else {
      //console.log("*** Names already match: " + m.actor.name);
      data.info[key]['loaded'] = true;
    }

    if (!m.object.read) {
      m.object.read = false;
    }

    if (m.status) {
      console.log('adding: ', m);
      var id = RSutil.encode(m.object.link);
      console.log('ID: ', id);
      RS.call('articles', 'get', [id]).then(function (a) {
        if (a) {
          console.log('ARTICLE FETCH from RS: ', a);
          m.object.read = a.read;
        }

        if (!m.object.read) {
          data.info[key].unread = (typeof data.info[key].unread === "number") ? data.info[key].unread + 1 : 1;
        }
        data.articles.push(m);
      }, function (e) {
        console.log("ARTICL FETCH ERROR: ", e);
        data.articles.push(m);
      });
    }
  });

  return {
    config: config,
    data: data,
    func: func
  };
}]).

///////////////////////////////////////////////////////////////////////////
//
// CONTROLLERS
//
///////////////////////////////////////////////////////////////////////////

/**
 * controller: addFeedCtrl
 */
controller('addFeedCtrl',
['$scope', 'RSS', '$rootScope',
function ($scope, RSS, $rootScope) {
  $scope.url = '';
  $scope.adding = false;

  $scope.add = function (url) {
    $scope.adding = true;

    var obj = {
      url: url,
      name: url,
      cache_articles: 20,
      last_fetched: new Date().getTime()
    };

    RSS.func.addFeed(obj).then(function (m) {
      console.log('rss feed url saved!: ', m);
      $rootScope.$broadcast('closeModalAddFeed');
      $scope.adding = false;
      $rootScope.$broadcast('message', {type: 'success', message: 'RSS feed added: '+obj.url});
    }, function (err) {
      console.log('rss feed url save failed!: ', err);
      $rootScope.$broadcast('message', {type: 'error', message: err.message});
    });
  };

}]).

/**
 * controller: feedCtrl
 */
controller('feedCtrl',
['$scope', 'RSS', 'util', '$rootScope',
function ($scope, RSS, util, $rootScope) {

  $scope.model = {};
  $scope.model.feeds = RSS.data;
  $scope.model.message = '';
  $scope.model.loading = true;
  $scope.model.feeds.current = {
    name: '',
    indexes: []
  };

  $scope.isSelected = function(url, inclusive) {
    if ($scope.model.feeds.current.indexes.length === 0) {
      if ((inclusive) || (!url)) {
        return true;
      } else {
        return false;
      }
    } else {
      for (var i = 0, num = $scope.model.feeds.current.indexes.length; i < num; i = i + 1) {
        if ($scope.model.feeds.current.indexes[i] === url) {
          return true;
        }
      }
    }
    return false;
  };

  $scope.switchFeed = function (url) {
    if (!url) {
      $scope.model.feeds.current.name = '';
      $scope.model.feeds.current.indexes = [];
    } else {
      $scope.model.feeds.current.name = RSS.data.info[url].name;
      $scope.model.feeds.current.indexes = [url];
    }
  };

  $scope.showFeedSettings = function (url) {
    $rootScope.$broadcast('showModalFeedSettings', {locked: false});
  };

  $scope.markRead = function (url) {
    console.log('markRead Called!');
    for (var i = 0, num = $scope.model.feeds.articles.length; i < num; i = i + 1) {
      //console.log('A.link: ' + $scope.model.feeds.articles[i].object.link + ' url: '+url);
      if ($scope.model.feeds.articles[i].object.link === url) {
        if (!$scope.model.feeds.articles[i].object.read) {
          //console.log('subtracting 1 from : '+ $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread);
          $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread =
              $scope.model.feeds.info[$scope.model.feeds.articles[i].actor.address].unread - 1;
          $scope.model.feeds.articles[i].object.read = true;
          RSS.func.updateArticle($scope.model.feeds.articles[i]);
        }
        return;
      }
    }
  };

  // display friendly message when no feeds are loaded
  if (util.isEmptyObject($scope.model.feeds.info)) {
    $scope.model.message = "loading feed list...";
  }

  $scope.$watch('$scope.model.feeds.info', function (newVal, oldVal) {
    if (!util.isEmptyObject($scope.model.feeds.info)) {
      $scope.model.message = '';
    } else {
      $scope.model.message = "no feeds yet, add some!";
    }
  });

  $scope.$watch('$scope.model.feeds.articles', function (newVal, oldVal) {
    console.log('article changed! ', newVal, oldVal);
  });

  $rootScope.$on('SockethubConnectFailed', function (event, e) {
    $rootScope.$broadcast('message', {
      message: e.message,
      type: 'error',
      timeout: false
    });
    $scope.model.loading = true;
    $rootScope.$broadcast('showModalSockethubSettings', {locked: false});
  });
}]).

///////////////////////////////////////////////////////////////////////////
//
// DIRECTIVES
//
///////////////////////////////////////////////////////////////////////////

/**
 * directive: feedList
 */
directive('feedList', [
function () {
  return {
    restrict: 'E',
    scope: {
      'feeds': '='
    },
    template: '

' +
              '{{ message }}' +
              '
' +
              '
' +
              '    ' +
              '      All Items' +
              '    ' +
              '
' +
              '
' +
              '    ' +
              '    ' +
              '
' +
              '      ' +
              '        {{ f.name }} {{ f.unread }}' +
              '      ' +
              '
' +
              '
' +
              '
',
    transclude: true
  };
}]).

/**
 * directive: articles
 */
directive('articles', [
function () {
  return {
    restrict: 'E',
    scope: {
      'feeds': '='
    },
    template: '
{{ feeds.current.name }}
' +
              '
' +
              '
{{ a.object.title }}
' +
              '
feed: {{ a.actor.name }}
' +
              '
date: {{ a.object.date | date }}
' +
              '
article link: {{ a.object.link }}
' +
              '

' +
              '
',
    link: function (scope, element, attrs) {
      //console.log('#### LINK FUNCTION: scope: ', scope);
      //console.log('#### LINK FUNCTION: element: ', element);
      //console.log('#### LINK FUNCTION: attrs: ', attrs);

      scope.$watch(attrs.feeds, function (val) {
        console.log('WATCH attrs.feeds', val);
      });

      var divs = document.getElementsByClassName('article');
      for (var i = 0, num = divs.length; i < num; i = i + 1) {
        // grab all of the links inside the div
        var links = divs[i].getElementsByTagName('a');
        // Loop through those links and attach the target attribute
        for (var j = 0, jnum = links.length; j < jnum; j = j + 1) {
          // the _blank will make the link open in new window
          links[j].setAttribute('target', '_blank');
        }
      }
    }
  };
}]);<|endoftext|>


<commit_before>require('scripts/jquery-longpress');

// Note that we use 'mousedown touchstart' everywhere. It's pretty important,
// as 'click' could interfere with palette auto-hide feature (as it hides on
// 'mousedown'). Also, it simplifies scenarios for touch devices,
// as 'mousedown' occurs in the same moment as 'touchstart'.

function BasicButton(options, ui, drawingTool) {
  this.ui = ui;
  this.dt = drawingTool;

  this.name = options.name;
  this.palette = options.palette;
  // Note that this will be called later by UI manager.
  this.onInit = options.onInit;

  this.$element = $('<div>')
    .addClass('dt-btn')
    .addClass(options.classes)
    .appendTo(ui.getPalette(options.palette).$element);

  this.$label = $('<span>')
    .text(options.label)
    .appendTo(this.$element);

  if (options.onClick) {
    this.$element.on('mousedown touchstart', function (e) {
      options.onClick.call(this, e, ui, drawingTool);
      e.preventDefault();
    }.bind(this));
  }

  if (options.onLongPress) {
    this.$element.longPress(function (e) {
      options.onLongPress.call(this, e, ui, drawingTool);
      e.preventDefault();
    }.bind(this));
  }

  if (options.onStateChange) {
    drawingTool.on('state:changed', function (state) {
      options.onStateChange.call(this, state);
    }.bind(this));
  }

  if (options.onToolChange) {
    drawingTool.on('tool:changed', function (state) {
      options.onToolChange.call(this, state);
    }.bind(this));
  }

  if (options.activatesTool) {
    this.$element.on('mousedown touchstart', function (e) {
      drawingTool.chooseTool(options.activatesTool);
      e.preventDefault();
    }.bind(this));

    drawingTool.on('tool:changed', function (toolName) {
      if (toolName === options.activatesTool) {
        this.$element.addClass('dt-active');
      } else {
        this.$element.removeClass('dt-active');
      }
    }.bind(this));
  }

  if (options.reflectsTools) {
    drawingTool.on('tool:changed', function (toolName) {
      if (options.reflectsTools.indexOf(toolName) !== -1) {
        this.setActive(true);
        this.setLabel(ui.getButton(toolName).getLabel());
      } else {
        this.setActive(false);
        this.$element.removeClass('dt-active');
      }
    }.bind(this));
  }
}

BasicButton.prototype.setLabel = function (label) {
  this.$label.text(label);
};

BasicButton.prototype.getLabel = function () {
  return this.$label.text();
};

BasicButton.prototype.click = function () {
  // #triggerHandler won't create a native event that bubbles (in contrast
  // to #trigger). Use it as otherwise it could interfere with some other
  // handlers listening to 'mousedown' on window (palette auto-hide feature).
  this.$element.triggerHandler('mousedown');
};

BasicButton.prototype.setActive = function (v) {
  if (v) {
    this.$element.addClass('dt-active');
  } else {
    this.$element.removeClass('dt-active');
  }
};

BasicButton.prototype.setLocked = function (v) {
  if (v) {
    this.$element.addClass('dt-locked');
  } else {
    this.$element.removeClass('dt-locked');
  }
};

module.exports = BasicButton;
<commit_msg>Make sure that locked buttons don't call their handlers on click<commit_after>require('scripts/jquery-longpress');

// Note that we use 'mousedown touchstart' everywhere. It's pretty important,
// as 'click' could interfere with palette auto-hide feature (as it hides on
// 'mousedown'). Also, it simplifies scenarios for touch devices,
// as 'mousedown' occurs in the same moment as 'touchstart'.

function BasicButton(options, ui, drawingTool) {
  this.ui = ui;
  this.dt = drawingTool;

  this.name = options.name;
  this.palette = options.palette;
  // Note that this will be called later by UI manager.
  this.onInit = options.onInit;

  this._locked = false;

  this.$element = $('<div>')
    .addClass('dt-btn')
    .addClass(options.classes)
    .appendTo(ui.getPalette(options.palette).$element);

  this.$label = $('<span>')
    .text(options.label)
    .appendTo(this.$element);

  if (options.onClick) {
    this.$element.on('mousedown touchstart', function (e) {
      if (this._locked) return;
      options.onClick.call(this, e, ui, drawingTool);
      e.preventDefault();
    }.bind(this));
  }

  if (options.onLongPress) {
    this.$element.longPress(function (e) {
      if (this._locked) return;
      options.onLongPress.call(this, e, ui, drawingTool);
      e.preventDefault();
    }.bind(this));
  }

  if (options.onStateChange) {
    drawingTool.on('state:changed', function (state) {
      options.onStateChange.call(this, state);
    }.bind(this));
  }

  if (options.onToolChange) {
    drawingTool.on('tool:changed', function (state) {
      options.onToolChange.call(this, state);
    }.bind(this));
  }

  if (options.activatesTool) {
    this.$element.on('mousedown touchstart', function (e) {
      if (this._locked) return;
      drawingTool.chooseTool(options.activatesTool);
      e.preventDefault();
    }.bind(this));

    drawingTool.on('tool:changed', function (toolName) {
      if (toolName === options.activatesTool) {
        this.$element.addClass('dt-active');
      } else {
        this.$element.removeClass('dt-active');
      }
    }.bind(this));
  }

  if (options.reflectsTools) {
    drawingTool.on('tool:changed', function (toolName) {
      if (options.reflectsTools.indexOf(toolName) !== -1) {
        this.setActive(true);
        this.setLabel(ui.getButton(toolName).getLabel());
      } else {
        this.setActive(false);
        this.$element.removeClass('dt-active');
      }
    }.bind(this));
  }
}

BasicButton.prototype.setLabel = function (label) {
  this.$label.text(label);
};

BasicButton.prototype.getLabel = function () {
  return this.$label.text();
};

BasicButton.prototype.click = function () {
  // #triggerHandler won't create a native event that bubbles (in contrast
  // to #trigger). Use it as otherwise it could interfere with some other
  // handlers listening to 'mousedown' on window (palette auto-hide feature).
  this.$element.triggerHandler('mousedown');
};

BasicButton.prototype.setActive = function (v) {
  if (v) {
    this.$element.addClass('dt-active');
  } else {
    this.$element.removeClass('dt-active');
  }
};

BasicButton.prototype.setLocked = function (v) {
  if (v) {
    this.$element.addClass('dt-locked');
  } else {
    this.$element.removeClass('dt-locked');
  }
  this._locked = v;
};

module.exports = BasicButton;
<|endoftext|>


<commit_before>var Config = require('./config.js');
var fs = require('fs');
var express = require('express');
var connect = require('connect');

// Configure the server
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server, { log: false });

// Configure Express
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

// Add Express middleware
app.use(express.static(__dirname + '/public'));

// Set up Express routes
require('./routes.js')(app, io.sockets);

// Set up Socket.IO
io.sockets.on('connection', require('./socket.js')(io.sockets));

// Start the server
server.listen(Config.web.port, function () {
  console.log('Listening on port ' + Config.web.port + '.');
});
<commit_msg>Removed unused references<commit_after>var Config = require('./config.js');
var express = require('express');

// Configure the server
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io').listen(server, { log: false });

// Configure Express
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

// Add Express middleware
app.use(express.static(__dirname + '/public'));

// Set up Express routes
require('./routes.js')(app, io.sockets);

// Set up Socket.IO
io.sockets.on('connection', require('./socket.js')(io.sockets));

// Start the server
server.listen(Config.web.port, function () {
  console.log('Listening on port ' + Config.web.port + '.');
});
<|endoftext|>


<commit_before>import path from 'path';

import paths from '../server/paths.js';
import * as articles from '../server/articles.js';
import * as database from '../server/database.js';

function getDirectoryDate(directoryPath) {
    return directoryPath
        .split(path.sep)
        .filter(dir => dir == Number(dir))
}

function isDirectoryNameCorrect(metadataDate, directoryName) {
    const publicationDate = new Date(metadataDate);
    const publicationYear = publicationDate.getFullYear();
    const publicationMonth = publicationDate.getMonth() + 1;

    const directoryDate = getDirectoryDate(directoryName);
    const directoryYear = directoryDate[0];
    const directoryMonth = directoryDate[1];

    if (publicationYear != directoryYear || publicationMonth != directoryMonth) {
        console.error(`publication_date in article.md yaml header is different from year or month directory ${ directoryName }`);
        return false;
    }

    return true;
}

export default async function uploadArticles() {
    let allArticlesUrls = [];
    const articlesDirectories = articles.getArticlesDirectories(paths.articles, 2);

    for (let articleDirectory of articlesDirectories) {
        const articleUrl = articleDirectory.split(path.sep).reverse()[0];

        const data = articles.parseArticle(path.join(articleDirectory, 'article.md'));
        const metadata = data.metadata;
        const articleContent = data.html;

        if (!isDirectoryNameCorrect(metadata.publication_date, articleDirectory)) {
            return;
        }

        allArticlesUrls.push(articleUrl);

        let directoryNameDb = getDirectoryDate(articleDirectory).join('/');

        let dbData = [
            metadata.title,
            metadata.description,
            articleUrl,
            directoryNameDb,
            metadata.publication_date,
            metadata.last_update,
            metadata.visible
        ];

        const articleId = await database.getIdByArticleUrl(articleUrl);

        if (articleId === null) { // new article which is not in db
            let dbResponse = await database.insertArticleMetadata(dbData);
            console.log('article "' + articleUrl + '" inserted into db [metadata].');

            let insertId = dbResponse.insertId;
            await database.insertArticleContent([insertId, articleContent]);
            console.log('article "' + articleUrl + '" inserted into db [content].');
        } else {
            await database.updateArticleMetadata(dbData.concat(articleId.id));
            console.log('article "' + articleUrl + '" updated in db [metadata].');

            await database.updateArticleContent([articleContent, articleId.id]);
            console.log('article "' + articleUrl + '" updated in db [content].');
        }
    };

    // delete all articles except the ones in article directory
    var deletedArticles = await database.deleteArticles(allArticlesUrls);

    if (deletedArticles.affectedRows > 0) {
        console.log(`${ deleteArticles.affectedRows } articles, which were not in articles directory, deleted from db.`);
    };

    process.exit();
}
<commit_msg>fix getDirectoryDate function, simplify log messages<commit_after>import path from 'path';

import paths from '../server/paths.js';
import * as articles from '../server/articles.js';
import * as database from '../server/database.js';

function getDirectoryDate(directoryPath) {
    return directoryPath
        .split(path.sep)
        .filter(dir => dir && dir == Number(dir))
}

function isDirectoryNameCorrect(metadataDate, directoryName) {
    const publicationDate = new Date(metadataDate);
    const publicationYear = publicationDate.getFullYear();
    const publicationMonth = publicationDate.getMonth() + 1;

    const directoryDate = getDirectoryDate(directoryName);
    const directoryYear = directoryDate[0];
    const directoryMonth = directoryDate[1];

    if (publicationYear != directoryYear || publicationMonth != directoryMonth) {
        console.error(`publication_date in article.md yaml header is different from year or month directory ${ directoryName }`);
        return false;
    }

    return true;
}

export default async function uploadArticles() {
    let allArticlesUrls = [];
    const articlesDirectories = articles.getArticlesDirectories(paths.articles, 2);
    articlesDirectories.reverse();

    for (let articleDirectory of articlesDirectories) {
        const articleUrl = articleDirectory.split(path.sep).reverse()[0];

        const data = articles.parseArticle(path.join(articleDirectory, 'article.md'));
        const metadata = data.metadata;
        const articleContent = data.html;

        if (!isDirectoryNameCorrect(metadata.publication_date, articleDirectory)) {
            return;
        }

        allArticlesUrls.push(articleUrl);

        let directoryNameDb = getDirectoryDate(articleDirectory).join('/');

        let dbData = [
            metadata.title,
            metadata.description,
            articleUrl,
            directoryNameDb,
            metadata.publication_date,
            metadata.last_update,
            metadata.visible
        ];

        const articleId = await database.getIdByArticleUrl(articleUrl);

        if (articleId === null) { // new article which is not in db
            let dbResponse = await database.insertArticleMetadata(dbData);
            await database.insertArticleContent([dbResponse.insertId, articleContent]);
            console.log(`${ metadata.publication_date.toLocaleDateString('cs') } article ${ articleUrl } INSERTED.`);
        } else {
            await database.updateArticleMetadata(dbData.concat(articleId.id));
            await database.updateArticleContent([articleContent, articleId.id]);
            console.log(`${ metadata.publication_date.toLocaleDateString('cs') } article ${ articleUrl } updated.`);
        }
    };

    // delete all articles except the ones in article directory
    var deletedArticles = await database.deleteArticles(allArticlesUrls);

    if (deletedArticles.affectedRows > 0) {
        console.log(`${ deleteArticles.affectedRows } articles, which were not in articles directory, deleted from db.`);
    };

    process.exit();
}
<|endoftext|>