<commit_before>require('dotenv').config();

if (!process.env.SONGKICK_API_KEY || !process.env.SERVER_IP || !process.env.FCM_API_KEY ||
    !process.env.VAPID_EMAIL || !process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) {
  return console.error('❗ Failed to load in the environment variables. Are they missing from the `.env` file?');
}

const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const path = require('path');
const webPush = require('web-push');
const getColors = require('get-image-colors');

webPush.setGCMAPIKey(process.env.FCM_API_KEY);
webPush.setVapidDetails(
  `mailto:${process.env.VAPID_EMAIL}`,
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

// store data in memory for now
const users = {};
const colors = {};

const getColor = (buffer) => new Promise(resolve => {
  getColors(buffer, 'image/jpeg')
    .then(colors => colors[0]._rgb)
    .then(color => {
      resolve(`rgb(${color.slice(0, -1).join(',')})`);
    })
    .catch(error => {
      // console.error(error);
      resolve(false);
    });
});

const handleColors = (id, imageUrl) => {
  if (colors[id]) {
    return colors[id];
  }

  // get color for next time
  fetch(imageUrl.replace('large_avatar', 'medium_avatar'))
    .then(response => response.buffer())
    .then(getColor)
    .then(color => {
      colors[id] = color;
    });

  return false;
};

const uriPrefix = 'https://api.songkick.com/api/3.0/users';

const apiKey = process.env.SONGKICK_API_KEY;
const inDevelopment = process.env.NODE_ENV !== 'production';

const app = express();

const jsonParser = bodyParser.json();

if (inDevelopment) {
  const webpack = require('webpack');
  const devMiddleware = require('webpack-dev-middleware');
  const hotMiddleware = require('webpack-hot-middleware');
  const config = require('./webpack.config');

  const compiler = webpack(config);

  app.use(devMiddleware(compiler, {
    publicPath: config.output.publicPath,
    historyApiFallback: true
  }));

  app.use(hotMiddleware(compiler));
}

// Static files
app.use(express.static(inDevelopment ? 'src' : 'build'));

const loadData = (options) => new Promise((resolve, reject) => {
  if (!options.uri) {
    return reject('No uri');
  }

  const uri = options.uri;
  const page = options.page || 1;

  return fetch(`${uri}&page=${page}`)
    .then(response => response.json())
    .then(data => {
      if (!data.resultsPage || data.resultsPage.totalEntries <= 0) {
        return reject('No results');
      }

      const pageAmount = Math.ceil(data.resultsPage.totalEntries / data.resultsPage.perPage);

      if (data.resultsPage.page !== pageAmount) {
        return loadData({ uri, page: page + 1 })
          .then(newData => {
            if (typeof data.resultsPage.results.artist !== 'undefined') {
              data.resultsPage.results.artist = data.resultsPage.results.artist.concat(newData.resultsPage.results.artist);
            }

            if (typeof data.resultsPage.results.calendarEntry !== 'undefined') {
              data.resultsPage.results.calendarEntry = data.resultsPage.results.calendarEntry.concat(newData.resultsPage.results.calendarEntry);
            }

            return resolve(data);
          });
      }

      return resolve(data);
    })
    .catch(reject);
});

const getResults = (data) => data.resultsPage.results;

const getEvents = (data) => new Promise((resolve, reject) => {
  if (data.calendarEntry.length <= 0) {
    return reject('No events');
  }

  const events = data.calendarEntry.map(entry => {
    const event = entry.event;
    event.reason = entry.reason;
    return event;
  });

  resolve(events);
});

const getArtists = (data) => new Promise((resolve, reject) => {
  if (data.artist.length <= 0) {
    return reject('No artists');
  }

  const artists = data.artist.map(artist => {
    const imageSrc = getImage(artist);
    const imageColor = handleColors(artist.id, imageSrc);

    return {
      id: artist.id,
      name: artist.displayName,
      image: {
        color: imageColor,
        src: imageSrc
      },
      onTourUntil: artist.onTourUntil,
      uri: artist.uri
    };
  });

  resolve(artists);
});

const getImage = (data) => {
  const IMAGE_PREFIX = 'https://images.sk-static.com/images/media/profile_images';

  if (typeof data.billing !== 'undefined' ) {
    return `${IMAGE_PREFIX}/artists/${data.artist.id}/large_avatar`;
  }

  if (typeof data.onTourUntil !== 'undefined' ) {
    return `${IMAGE_PREFIX}/artists/${data.id}/large_avatar`;
  }

  if (data.type === 'Festival') {
    return `${IMAGE_PREFIX}/events/${data.id}/large_avatar`;
  }

  if (data.performance && data.performance.length > 0) {
    return `${IMAGE_PREFIX}/artists/${data.performance[0].artist.id}/large_avatar`;
  }

  return '';
};

const processPerformances = (performances) => {
  return performances.map(performance => {
    const imageSrc = getImage(performance);
    const imageColor = handleColors(performance.artist.id, imageSrc);

    return {
      id: performance.artist.id,
      type: performance.billing,
      name: performance.artist.displayName,
      image: {
        color: imageColor,
        src: imageSrc
      }
    };
  });
};

function getOrdinal(n) {
  const s = ['th', 'st', 'nd', 'rd'];
  const v = n % 100;
  return s[(v - 20) % 10] || s[v] || s[0];
}

function formatDate(date, type) {
  date = date.trim(); // trim whitespace

  // if we only have the year add some time
  if (date.length === 10) {
    date += ' 00:00:00';
  }

  // replace - with /
  date = date.replace(/-/g, "/");

  // convert strings to date objects
  date = new Date(date);

  const monthNames = [
    'January', 'February', 'March',
    'April', 'May', 'June', 'July',
    'August', 'September', 'October',
    'November', 'December'
  ];

  let amPm = 'AM';
  let minutes = date.getMinutes();

  if (minutes < 10) {
    minutes = '0' + minutes;
  }

  let hours = date.getHours();

  if (hours >= 12) {
    hours -= 12;
    amPm = 'PM';
  }

  const dateString = date.toDateString();
  const dayName = dateString.split(' ')[0];
  const day = date.getDate();
  const dayWithOrdinal = `${day}${getOrdinal(day)}`;
  const monthIndex = date.getMonth();
  const month = monthNames[monthIndex];
  const year = date.getFullYear();

  if (type === 'long') {
    return `${dayName} ${dayWithOrdinal} ${month} ${year}`;
  } else if ( type === 'short') {
    return `${dayName} ${dayWithOrdinal} ${month.substr(0, 3)} `;
  } else if ( type === 'time') {
    return `${hours}:${minutes} ${amPm}`;
  }

  return dateString;
}

const processEvents = (events) => events.map(event => {
  const date = `${event.start.date} ${event.start.time || ''}`;
  const imageSrc = getImage(event);
  const imageColor = handleColors(event.id, imageSrc);
  const newEvent = {
    id: event.id,
    reason: event.reason,
    type: event.type.toLowerCase(),
    performances: processPerformances(event.performance),
    time: {
      iso: date,
      pretty: {
        short: date ? formatDate(date, 'short') : 'Date TBC',
        full:  date ? formatDate(date, 'long') : 'Date to be confirmed',
        doors: date ? formatDate(date, 'time') : 'Doors to be confirmed'
      }
    },
    place: {
      name: event.venue.displayName,
      address: `${event.venue.displayName}, ${event.location.city}`,
      id: event.venue.id,
      uri: event.venue.uri,
      lat: event.venue.lat,
      lon: event.venue.lng
    },
    image: {
      color: imageColor,
      src: imageSrc
    },
    uri: event.uri
  };

  if (event.status === 'cancelled') {
    newEvent.cancelled = true;
  }

  if (newEvent.type === 'festival') {
    newEvent.title = event.displayName;
  }

  return newEvent;
});

function sortUniqueResults(arr) {
  if (arr.length === 0) {
    return arr;
  }

  // sort by id for removing duplicates
  arr = arr.sort((a, b) => {
    return a.id - b.id;
  });

  const newArr = [arr[0]];

  for (let i = 1; i < arr.length; i++) { // start loop at 1 as element 0 can never be a duplicate
    if (arr[i - 1].id !== arr[i].id) {
      newArr.push(arr[i]);
    }
  }

  arr = newArr;

  // sort by date
  arr = arr.sort((a, b) => {
    return new Date(a.start.date) - new Date(b.start.date);
  });

  return arr;
}

function artists(username) {
  const uri = `${uriPrefix}/${username}/artists/tracked.json?apikey=${apiKey}`;
  return loadData({ uri }).then(getResults).then(getArtists);
}

function events(username) {
  return new Promise((resolve, reject) => {
    let uri = `${uriPrefix}/${username}/calendar.json?apikey=${apiKey}&reason=attendance`;
    const events = loadData({ uri }).then(getResults).then(getEvents);

    uri = `${uriPrefix}/${username}/calendar.json?apikey=${apiKey}&reason=tracked_artist`;
    const upcomingEvents = loadData({ uri }).then(getResults).then(getEvents);

    Promise.all([upcomingEvents, events]).then(results => {
      results = [].concat(...results);
      results = sortUniqueResults(results);

      resolve(processEvents(results));
    }).catch(reject);
  });
}

// allow CORS
// TODO: do i need this if im on the same domain anyway?
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

if (process.env.NODE_ENV === 'production') {
  // only allow from this ip address
  app.use((req, res, next) => {
    if (req.ip !== process.env.SERVER_IP) { // Wrong IP address
      res.status(401);
      return res.send('Permission denied');
    }
    next(); // correct IP address, continue middleware chain
  });
}

app.get('/api', (req, res, next) => {
  res.status(404).json({ error: 'No username' });
});

app.get('/api/events', (req, res, next) => {
  const { username } = req.query;
  if (!username) {
    return res.status(404).json({ error: 'No username sent' });
  }

  events(username)
    .then(events => res.json(events))
    .catch(error => res.status(500).json({ error }));
});

app.get('/api/artists', (req, res) => {
  const { username } = req.query;
  if (!username) {
    return res.status(404).json({ error: 'No username sent' });
  }

  artists(username)
    .then(artists => res.json(artists))
    .catch(error => res.status(500).json({ error }));
});

app.post('/api/pushSubscription', jsonParser, (req, res) => {
  const { subscription, username } = req.body;

  // if no user
  if (!users[username]) {
    // add subscription
    users[username] = [subscription];
    //sendPushNotification(username);
    return res.sendStatus(201);
  }

  // check if already subscribed
  const userSubscription = users[username].find(s => s.endpoint === subscription.endpoint);

  if (!userSubscription) {
    // add new subscription
    users[username].push(subscription);
    //sendPushNotification(username);
  }

  // A real world application would store the subscription info.
  // we'd stick this data into subscriptions
  res.sendStatus(201);
});

app.delete('/api/pushSubscription', jsonParser, (req, res) => {
  const { subscription, username } = req.body;
  // if no user
  if (!users[username]) {
    return res.sendStatus(404);
  }

  const userSubscriptionIndex = users[username].findIndex(s => s.endpoint === subscription.endpoint);

  if (userSubscriptionIndex === -1) {
    return res.sendStatus(404);
  }

  // remove subscription from array
  users[username] = users[username].splice(userSubscriptionIndex, 1);

  // in theory this should fail for the one subscribed at that point
  //sendPushNotification(username);

  // A real world application would store the subscription info.
  // we'd stick this data into subscriptions
  res.sendStatus(201);
});

function sendPushNotificationAll() {
  Object.keys(users).forEach(username => {
    sendPushNotification(username);
  });
}

function sendPushNotification(username) {
  const pushSubscriptions = users[username];

  events(username)
    .then(events => {
      const event = events[0];

      const data = {
        title: `${event.performances[0].name}`,
        body: `${event.place.name} | ${event.time.pretty.short}`,
        icon: event.image.src,
        badge: 'https://songkick.pink/assets/icon/badge.png'
      };

      for (let i = 0; i < pushSubscriptions.length; i++) {
        const pushSubscription = pushSubscriptions[i];
        webPush.sendNotification(pushSubscription, JSON.stringify(data));
      }
    })
    .catch(error => console.error('error', error));
}

// Send everything else to react-router
app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build/index.html'));
});

app.listen(process.env.PORT || 8000, (err) => {
  if (err) {
    return console.error(err);
  }

  console.info(`🌐 Listening at http://localhost:${process.env.PORT || 8000}/`);
  console.info(`${inDevelopment ? '🛠 Development' : '🚀 Production'} mode   `);
});
<commit_msg>Poll for new events and send notifications to subscribed users<commit_after>require('dotenv').config();

if (!process.env.SONGKICK_API_KEY || !process.env.SERVER_IP || !process.env.FCM_API_KEY ||
    !process.env.VAPID_EMAIL || !process.env.VAPID_PUBLIC_KEY || !process.env.VAPID_PRIVATE_KEY) {
  return console.error('❗ Failed to load in the environment variables. Are they missing from the `.env` file?');
}

const express = require('express');
const bodyParser = require('body-parser');
const fetch = require('node-fetch');
const path = require('path');
const webPush = require('web-push');
const getColors = require('get-image-colors');

webPush.setGCMAPIKey(process.env.FCM_API_KEY);
webPush.setVapidDetails(
  `mailto:${process.env.VAPID_EMAIL}`,
  process.env.VAPID_PUBLIC_KEY,
  process.env.VAPID_PRIVATE_KEY
);

// store data in memory for now
const users = {};
const colors = {};

const getColor = (buffer) => new Promise(resolve => {
  getColors(buffer, 'image/jpeg')
    .then(colors => colors[0]._rgb)
    .then(color => {
      resolve(`rgb(${color.slice(0, -1).join(',')})`);
    })
    .catch(error => {
      // console.error(error);
      resolve(false);
    });
});

const handleColors = (id, imageUrl) => {
  if (colors[id]) {
    return colors[id];
  }

  // get color for next time
  fetch(imageUrl.replace('large_avatar', 'medium_avatar'))
    .then(response => response.buffer())
    .then(getColor)
    .then(color => {
      colors[id] = color;
    });

  return false;
};

const uriPrefix = 'https://api.songkick.com/api/3.0/users';

const apiKey = process.env.SONGKICK_API_KEY;
const inDevelopment = process.env.NODE_ENV !== 'production';

const app = express();

const jsonParser = bodyParser.json();

if (inDevelopment) {
  const webpack = require('webpack');
  const devMiddleware = require('webpack-dev-middleware');
  const hotMiddleware = require('webpack-hot-middleware');
  const config = require('./webpack.config');

  const compiler = webpack(config);

  app.use(devMiddleware(compiler, {
    publicPath: config.output.publicPath,
    historyApiFallback: true
  }));

  app.use(hotMiddleware(compiler));
}

// Static files
app.use(express.static(inDevelopment ? 'src' : 'build'));

const loadData = (options) => new Promise((resolve, reject) => {
  if (!options.uri) {
    return reject('No uri');
  }

  const uri = options.uri;
  const page = options.page || 1;

  return fetch(`${uri}&page=${page}`)
    .then(response => response.json())
    .then(data => {
      if (!data.resultsPage || data.resultsPage.totalEntries <= 0) {
        return reject('No results');
      }

      const pageAmount = Math.ceil(data.resultsPage.totalEntries / data.resultsPage.perPage);

      if (data.resultsPage.page !== pageAmount) {
        return loadData({ uri, page: page + 1 })
          .then(newData => {
            if (typeof data.resultsPage.results.artist !== 'undefined') {
              data.resultsPage.results.artist = data.resultsPage.results.artist.concat(newData.resultsPage.results.artist);
            }

            if (typeof data.resultsPage.results.calendarEntry !== 'undefined') {
              data.resultsPage.results.calendarEntry = data.resultsPage.results.calendarEntry.concat(newData.resultsPage.results.calendarEntry);
            }

            return resolve(data);
          });
      }

      return resolve(data);
    })
    .catch(reject);
});

const getResults = (data) => data.resultsPage.results;

const getEvents = (data) => new Promise((resolve, reject) => {
  if (data.calendarEntry.length <= 0) {
    return reject('No events');
  }

  const events = data.calendarEntry.map(entry => {
    const event = entry.event;
    event.reason = entry.reason;
    return event;
  });

  resolve(events);
});

const getArtists = (data) => new Promise((resolve, reject) => {
  if (data.artist.length <= 0) {
    return reject('No artists');
  }

  const artists = data.artist.map(artist => {
    const imageSrc = getImage(artist);
    const imageColor = handleColors(artist.id, imageSrc);

    return {
      id: artist.id,
      name: artist.displayName,
      image: {
        color: imageColor,
        src: imageSrc
      },
      onTourUntil: artist.onTourUntil,
      uri: artist.uri
    };
  });

  resolve(artists);
});

const getImage = (data) => {
  const IMAGE_PREFIX = 'https://images.sk-static.com/images/media/profile_images';

  if (typeof data.billing !== 'undefined' ) {
    return `${IMAGE_PREFIX}/artists/${data.artist.id}/large_avatar`;
  }

  if (typeof data.onTourUntil !== 'undefined' ) {
    return `${IMAGE_PREFIX}/artists/${data.id}/large_avatar`;
  }

  if (data.type === 'Festival') {
    return `${IMAGE_PREFIX}/events/${data.id}/large_avatar`;
  }

  if (data.performance && data.performance.length > 0) {
    return `${IMAGE_PREFIX}/artists/${data.performance[0].artist.id}/large_avatar`;
  }

  return '';
};

const processPerformances = (performances) => {
  return performances.map(performance => {
    const imageSrc = getImage(performance);
    const imageColor = handleColors(performance.artist.id, imageSrc);

    return {
      id: performance.artist.id,
      type: performance.billing,
      name: performance.artist.displayName,
      image: {
        color: imageColor,
        src: imageSrc
      }
    };
  });
};

function getOrdinal(n) {
  const s = ['th', 'st', 'nd', 'rd'];
  const v = n % 100;
  return s[(v - 20) % 10] || s[v] || s[0];
}

function formatDate(date, type) {
  date = date.trim(); // trim whitespace

  // if we only have the year add some time
  if (date.length === 10) {
    date += ' 00:00:00';
  }

  // replace - with /
  date = date.replace(/-/g, "/");

  // convert strings to date objects
  date = new Date(date);

  const monthNames = [
    'January', 'February', 'March',
    'April', 'May', 'June', 'July',
    'August', 'September', 'October',
    'November', 'December'
  ];

  let amPm = 'AM';
  let minutes = date.getMinutes();

  if (minutes < 10) {
    minutes = '0' + minutes;
  }

  let hours = date.getHours();

  if (hours >= 12) {
    hours -= 12;
    amPm = 'PM';
  }

  const dateString = date.toDateString();
  const dayName = dateString.split(' ')[0];
  const day = date.getDate();
  const dayWithOrdinal = `${day}${getOrdinal(day)}`;
  const monthIndex = date.getMonth();
  const month = monthNames[monthIndex];
  const year = date.getFullYear();

  if (type === 'long') {
    return `${dayName} ${dayWithOrdinal} ${month} ${year}`;
  } else if ( type === 'short') {
    return `${dayName} ${dayWithOrdinal} ${month.substr(0, 3)} `;
  } else if ( type === 'time') {
    return `${hours}:${minutes} ${amPm}`;
  }

  return dateString;
}

const processEvents = (events) => events.map(event => {
  const date = `${event.start.date} ${event.start.time || ''}`;
  const imageSrc = getImage(event);
  const imageColor = handleColors(event.id, imageSrc);
  const newEvent = {
    id: event.id,
    reason: event.reason,
    type: event.type.toLowerCase(),
    performances: processPerformances(event.performance),
    time: {
      iso: date,
      pretty: {
        short: date ? formatDate(date, 'short') : 'Date TBC',
        full:  date ? formatDate(date, 'long') : 'Date to be confirmed',
        doors: date ? formatDate(date, 'time') : 'Doors to be confirmed'
      }
    },
    place: {
      name: event.venue.displayName,
      address: `${event.venue.displayName}, ${event.location.city}`,
      id: event.venue.id,
      uri: event.venue.uri,
      lat: event.venue.lat,
      lon: event.venue.lng
    },
    image: {
      color: imageColor,
      src: imageSrc
    },
    uri: event.uri
  };

  if (event.status === 'cancelled') {
    newEvent.cancelled = true;
  }

  if (newEvent.type === 'festival') {
    newEvent.title = event.displayName;
  }

  return newEvent;
});

function sortUniqueResults(arr) {
  if (arr.length === 0) {
    return arr;
  }

  // sort by id for removing duplicates
  arr = arr.sort((a, b) => {
    return a.id - b.id;
  });

  const newArr = [arr[0]];

  for (let i = 1; i < arr.length; i++) { // start loop at 1 as element 0 can never be a duplicate
    if (arr[i - 1].id !== arr[i].id) {
      newArr.push(arr[i]);
    }
  }

  arr = newArr;

  // sort by date
  arr = arr.sort((a, b) => {
    return new Date(a.start.date) - new Date(b.start.date);
  });

  return arr;
}

function artists(username) {
  const uri = `${uriPrefix}/${username}/artists/tracked.json?apikey=${apiKey}`;
  return loadData({ uri }).then(getResults).then(getArtists);
}

function events(username) {
  return new Promise((resolve, reject) => {
    let uri = `${uriPrefix}/${username}/calendar.json?apikey=${apiKey}&reason=attendance`;
    const events = loadData({ uri }).then(getResults).then(getEvents);

    uri = `${uriPrefix}/${username}/calendar.json?apikey=${apiKey}&reason=tracked_artist`;
    const upcomingEvents = loadData({ uri }).then(getResults).then(getEvents);

    Promise.all([upcomingEvents, events]).then(results => {
      results = [].concat(...results);
      results = sortUniqueResults(results);

      resolve(processEvents(results));
    }).catch(reject);
  });
}

// allow CORS
// TODO: do i need this if im on the same domain anyway?
app.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

if (process.env.NODE_ENV === 'production') {
  // only allow from this ip address
  app.use((req, res, next) => {
    if (req.ip !== process.env.SERVER_IP) { // Wrong IP address
      res.status(401);
      return res.send('Permission denied');
    }
    next(); // correct IP address, continue middleware chain
  });
}

app.get('/api', (req, res, next) => {
  res.status(404).json({ error: 'No username' });
});

app.get('/api/events', (req, res, next) => {
  const { username } = req.query;
  if (!username) {
    return res.status(404).json({ error: 'No username sent' });
  }

  events(username)
    .then(events => res.json(events))
    .catch(error => res.status(500).json({ error }));
});

app.get('/api/artists', (req, res) => {
  const { username } = req.query;
  if (!username) {
    return res.status(404).json({ error: 'No username sent' });
  }

  artists(username)
    .then(artists => res.json(artists))
    .catch(error => res.status(500).json({ error }));
});

function pollForNewEvents(username) {
  if (!username) {
    return console.error('No username');
  }

  const ONE_HOUR = 60 * 60 * 1000;

  // every hour check for different events
  users[username].poller = setInterval(() => {
    const cachedEventIds = users[username].eventIds;

    events(username)
      .then(events => {
        // find any events that aren't already cached
        // also discount anything thats been tracked as you don't need to be notified
        const newEvents = events.filter(event => !cachedEventIds.includes(event.id))
                                .filter(event => !event.reason.attendance);

        // send push notifs for each new event
        // pretty spammy, we should join these
        for (let i = 0; i < newEvents.length; i++) {
          const newEvent = newEvents[i];
          sendPushNotification(username, newEvent);
        }

        // update event ids
        users[username].eventIds = events.map(event => event.id);
      })
      .catch(console.error);
  }, ONE_HOUR);
}

function sendPushNotification(username, event) {
  if (!username || !event) {
    return console.error('No username or no event');
  }

  const pushSubscriptions = users[username].subscriptions;

  const data = {
    title: `${event.performances[0].name}`,
    body: `${event.place.name} | ${event.time.pretty.short}`,
    icon: event.image.src,
    badge: 'https://songkick.pink/assets/icon/badge.png'
  };

  for (let i = 0; i < pushSubscriptions.length; i++) {
    const pushSubscription = pushSubscriptions[i];
    webPush.sendNotification(pushSubscription, JSON.stringify(data));
  }
}

app.post('/api/pushSubscription', jsonParser, (req, res) => {
  const { subscription, username } = req.body;

  // if no user
  if (!users[username]) {
    // add subscription with latest event ids
    events(username)
      .then(events => {
        const eventIds = events.map(event => event.id);
        users[username] = {
          eventIds,
          subscriptions: [subscription]
        };

        // start polling for events
        pollForNewEvents(username);
      })
      .catch(console.error);

    return res.sendStatus(201);
  }

  // check if already subscribed
  const userSubscription = users[username].subscriptions.find(s => s.endpoint === subscription.endpoint);

  if (!userSubscription) {
    // add new subscription
    users[username].subscriptions.push(subscription);

    // if this isnt a new user but we've delete and created a sub
    if (users[username].subscriptions.length === 1) {
      // start polling for events again
      pollForNewEvents(username);
    }
  }

  res.sendStatus(201);
});

app.delete('/api/pushSubscription', jsonParser, (req, res) => {
  const { subscription, username } = req.body;
  // if no user
  if (!users[username]) {
    return res.sendStatus(404);
  }

  const userSubscriptionIndex = users[username].subscriptions.findIndex(s => s.endpoint === subscription.endpoint);

  if (userSubscriptionIndex === -1) {
    return res.sendStatus(404);
  }

  // remove subscription from array
  users[username].subscriptions = users[username].subscriptions.splice(userSubscriptionIndex, 1);

  // iff there are no more subscriptions
  if (users[username].subscriptions.length === 0) {
    // stop polling for events
    clearInterval(users[username].poller);
  }

  // A real world application would store the subscription info.
  // we'd stick this data into subscriptions
  res.sendStatus(201);
});

// Send everything else to react-router
app.get('/*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build/index.html'));
});

app.listen(process.env.PORT || 8000, (err) => {
  if (err) {
    return console.error(err);
  }

  console.info(`🌐 Listening at http://localhost:${process.env.PORT || 8000}/`);
  console.info(`${inDevelopment ? '🛠 Development' : '🚀 Production'} mode   `);
});
<|endoftext|>


<commit_before>function uniqueId() {
  return Date.now() + 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

var listener = null;
var setupMessageListener = function(siteName, buildEnv) {
  var ref = window.ENV.firebase.root().child('management/sites/' + siteName + '/messages');
  if(listener) {
    ref.off('child_added', listener);
    listener = null;
  }

  var initialIds = {};
  ref.once('value', function(totalData) {
    var val = totalData.val();

    for(var key in val) {
      initialIds[key] = true;
    }

    listener = ref.on('child_added', function(snap) {
      var now = Date.now();
      var message = snap.val();
      var id = snap.name();

      if(!initialIds[id]) {
        if(message.code === 'BUILD') {
          if(message.status === 0) {
            window.ENV.notify('success', 'Site build complete', { icon: 'refresh' });
          } else {
            window.ENV.notify('danger', 'Site build failed', { icon: 'remove' });
          }
          buildEnv.set('building', false);
        }
      }
    });
  });
};

export default Ember.Route.extend({
  notifications: [],

  getBuildEnvironment: function () {

    var route = this;

    if (Ember.isEmpty(route.get('buildEnvironment.isInitialized'))) {
      Ember.Logger.log('ApplicationRoute::getBuildEnvironment');
    } else {
      Ember.Logger.log('ApplicationRoute::getBuildEnvironment::skip');
      return Ember.RSVP.Promise.resolve();
    }

    var buildEnv = route.get('buildEnvironment');

    buildEnv.set('isInitialized', true);
    buildEnv.set('keepReload', Ember.$('meta[name="keepReload"]').attr('content'));

    var siteName = Ember.$('meta[name="siteName"]').attr('content');

    buildEnv.set('siteDisplayName', window.ENV.selfHosted ? siteName.replace('/,1/g', '.') : siteName);
    buildEnv.set('selfHosted', window.ENV.selfHosted);

    if (!window.ENV.selfHosted) {
      window.ENV.siteDNS = siteName + '.webhook.org';
    }
    window.ENV.firebaseRoot.child('/management/sites/' + siteName + '/dns').on('value', function (snap) {
      if (snap.val()) {
        window.ENV.siteDNS = snap.val();
      }
    });

    if (window.ENV.uploadUrl.indexOf('http://') !== 0) {
      window.ENV.uploadUrl = 'http://' + window.ENV.uploadUrl;
    }
    if (window.ENV.uploadUrl.substr(-1) !== '/') {
      window.ENV.uploadUrl = window.ENV.uploadUrl + '/';
    }

    var req = new XMLHttpRequest();
    req.open('GET', document.location, false);
    req.send(null);
    var headers = req.getAllResponseHeaders().toLowerCase();

    return new Ember.RSVP.Promise(function (resolve, reject) {

      if (headers.indexOf('x-webhook-local') !== -1) {
        buildEnv.set('local', true);
        var localSocket = Ember.Object.create({
          socket: new window.WebSocket('ws://' + document.location.hostname + ':6557')
        });

        buildEnv.set('localSocket', localSocket);

        localSocket.reopen({
          connected: function () {
            return this.get('socket.readyState') === 1;
          }.property('socket.readyState')
        });

        localSocket.socket.onmessage = function (event) {
          var storedCallback;
          if (event.data === 'done') {
            storedCallback = localSocket.get('doneCallback');
            localSocket.set('doneCallback', null);

            if (storedCallback) {
              storedCallback();
            }
          } else if (event.data.indexOf('done:') === 0) {
            var data = JSON.parse(event.data.replace('done:', ''));

            storedCallback = localSocket.get('doneCallback');
            localSocket.set('doneCallback', null);

            if (storedCallback) {
              storedCallback(data);
            }
          } else if (event.data.indexOf('message:') === 0) {
            var message = JSON.parse(event.data.replace('message:', ''));
            localSocket.set('message', message);
          }
        };

        localSocket.socket.onopen = function () {
          localSocket.set('connected', true);
          resolve();
        };

        localSocket.socket.onerror = function () {
          resolve();
        };

        if (!$('meta[name=suppressAlert]').attr('content')) {
          localSocket.socket.onclose = function () {
            localSocket.set('lostConnection', true);
          };
        }

        // Shut down LiveReload
        if (window.LiveReload && !buildEnv.get('keepReload')) {
          var shutDown = new CustomEvent('LiveReloadShutDown');
          document.addEventListener("LiveReloadConnect", function () {
            document.dispatchEvent(shutDown);
          }, false);
        }

      } else {
        resolve();
      }
    });
  },

  getMessageSuport: function () {

    var route = this;

    if (Ember.isEmpty(route.get('session.supportedMessages'))) {
      Ember.Logger.log('ApplicationRoute::getMessageSupport');
    } else {
      Ember.Logger.log('ApplicationRoute::getMessageSupport::skip');
      return Ember.RSVP.Promise.resolve();
    }

    route.set('session.supportedMessages', Ember.Object.create());

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var localSocket = route.get('buildEnvironment.localSocket');
      if (!localSocket || !localSocket.connected) {

        Ember.$.ajax({
          dataType: 'jsonp',
          jsonpCallback: 'supportedJSONPCallback',
          url: '/.wh/_supported/',
          success: resolve,
          error: reject
        });

      } else {

        route.gruntCommand('supported_messages', resolve);

      }

    }).then(function (messages) {
      messages.forEach(function (message) {
        route.get('session.supportedMessages').set(message, true);
      });
      Ember.Logger.info('Server Messages Supported:', messages.join(', '));
    }, function (error) {
      Ember.Logger.warn('Failed to retrieve supported grunt messages.');
    });
  },

  // Confirm that user has completed validation steps
  validateUser: function (user) {

    Ember.Logger.log('ApplicationRoute::validateUser');

    var session = this.get('session');
    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var getToken = function (snapshot) {
        var token = snapshot.val();
        session.set('site.token', token);

        window.ENV.firebase = window.ENV.firebaseRoot.child('buckets/' + siteName + '/' + token + '/dev');

        // if you just logged in, we have to set the firebase property
        DS.FirebaseAdapter.reopen({
          firebase: window.ENV.firebase
        });

        Ember.Logger.log('ApplicationRoute::validateUser::✓');

        resolve(user);
      };

      managementSiteRef.child('key').once('value', getToken, function (error) {

        if (error.code === 'PERMISSION_DENIED') {
          var escapedEmail = user.email.replace(/\./g, ',1');
          // Try to add to user list, if this is allowed they were a potential user
          managementSiteRef.child('users').child(escapedEmail).set(user.email, function () {
            managementSiteRef.root().child('management/users').child(escapedEmail).child('sites/user').child(siteName).set(true, function (error) {
              // Try to delete self from potential user list
              managementSiteRef.child('potential_users').child(escapedEmail).remove(function () {
                // Redo original authorization call
                managementSiteRef.child('key').once('value', getToken, reject);
              }, reject);
            });
          }, reject);
        } else {
          reject(error);
        }

      });

    });

  },

  initializeUser: function (user) {

    Ember.Logger.log('ApplicationRoute::initializeUser');

    var route = this;
    var session = route.get('session');
    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    if (!route.get('buildEnvironment.local')) {
      setupMessageListener(siteName, route.get('buildEnvironment'));
    }

    Ember.Logger.info('Logged in as ' + user.email);

    var escapedEmail = user.email.replace(/\./g, ',1');

    var ownerCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      session.set('isOwner', false);
      managementSiteRef.once('value', function (snapshot) {
        var siteData = snapshot.val();

        if (siteData.owners[escapedEmail]) {
          Ember.Logger.info('Logged in user is a site owner.');
          session.set('isOwner', true);
        } else if (siteData.users[escapedEmail]) {
          Ember.Logger.info('Logged in user is a site user.');
        } else {
          Ember.Logger.error('Logged in user is neither a site owner or site user??');
        }

        Ember.run(null, resolve);

      });
    });

    // Default billing values
    var billing = Ember.Object.create({
      active: true,
      status: 'paid',
      url: 'http://billing.webhook.com/site/' + siteName + '/',
    });
    billing.reopen({
      isPaid: function () {
        return this.get('status') === 'paid';
      }.property('status'),
      isTrial: function () {
        return this.get('status') === 'trialing';
      }.property('status')
    });
    session.set('billing', billing);

    // Grab actual billing values
    var billingRef = window.ENV.firebaseRoot.child('billing/sites/' + siteName);

    var activeCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('active').once('value', function (snapshot) {
        session.set('billing.active', snapshot.val());
        Ember.run(null, resolve);
      });
    });

    var statusCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('status').once('value', function (snapshot) {
        session.set('billing.status', snapshot.val());
        Ember.run(null, resolve);
      });
    });

    var endTrialCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('endTrial').once('value', function (snapshot) {
        var endTrial = snapshot.val();
        if (endTrial) {
          var endTrialDays = Math.ceil(moment(snapshot.val()).diff(moment(), 'days', true));
          session.set('billing.endTrial', endTrial);
          session.set('billing.endTrialDays', endTrialDays);
          session.set('billing.endTrialIsLastDay', endTrialDays === 1);
        }
        Ember.run(null, resolve);
      });
    });

    return Ember.RSVP.Promise.all([ownerCheck, activeCheck, statusCheck, endTrialCheck]).then(function () {
      session.set('user', user);
    });

  },

  getSession: function () {

    var route = this;
    var session = route.get('session');

    if (Ember.isEmpty(session.get('auth'))) {
      Ember.Logger.log('ApplicationRoute::getSession');
    } else {
      Ember.Logger.log('ApplicationRoute::getSession::skip');
      return Ember.RSVP.Promise.resolve();
    }

    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var firebaseAuth = new FirebaseSimpleLogin(window.ENV.firebaseRoot, function (error, user) {

        if (route.get('isDestroyed')) {
          return;
        }

        if (user) {
          // Logged in
          route.validateUser(user).then(route.initializeUser.bind(route)).then(resolve, function (error) {
            session.set('user', null);
            session.set('site.token', null);
            session.set('error', error);
          });
        } else if (error) {
          // an error occurred while attempting login
          session.set('user', null);
          session.set('site.token', null);
          session.set('error', error);
          reject(error);
        } else {
          // user is logged out
          session.set('user', null);
          session.set('site.token', null);
          resolve();
        }

      });

      session.set('auth', firebaseAuth);

    });
  },

  beforeModel: function () {
    Ember.Logger.log('ApplicationRoute::beforeModel');

    var route = this;

    return this.getBuildEnvironment().then(function () {
      return Ember.RSVP.Promise.all([
        route.getSession(),
        route.getMessageSuport()
      ]);
    });
  },

  setupController: function (controller) {
    controller.set('notifications', this.get('notifications'));
    this._super.apply(this, arguments);
  },

  gruntCommand: function (command, callback) {
    Ember.Logger.log('%cgruntCommand -> ' + command, 'color: purple; font-weight: bold');

    var localSocket = this.get('buildEnvironment.localSocket');
    if (localSocket && localSocket.connected) {
      localSocket.socket.send(command);
      if (callback) {
        localSocket.doneCallback = callback;
      }
    }
  },

  actions: {
    logoutUser: function () {

      window.ENV.firebase.child('presence/online').child(this.get('session.user.uid')).remove();

      this.get('session.auth').logout();
      this.set('session.user', null);
      this.transitionTo('login');
    },

    notify: function (type, message, options) {

      options = options || {};

      var notifications = this.get('notifications'),
          notification = Ember.Object.create({
            className: 'wy-tray-item' + (type ? '-' + type : ''),
            message: message
          });

      if (options.icon) {
        notification.set('iconClass', 'icon icon-' + options.icon);
      }

      if (options.className) {
        notification.set('extraClassName', options.className);
      }

      notifications.pushObject(notification);

      Ember.run.later(this, function () {
        notification.set('state', 'on');
      }, 10);

      Ember.run.later(this, function () {
        notification.set('state', null);
      }, 4500);

      Ember.run.later(this, function () {
        notifications.removeObject(notification);
      }, 5000);

    },

    chooseLanguage: function (language) {
      Ember.Logger.log('Changing language to %@'.fmt(language));
      window.localStorage.setItem('webhook-cms-language', language);

      Ember.Logger.log('Resetting app.');
      window.App.reset();
    },

    buildSignal: function (publishDate) {
      Ember.Logger.info('Sending build signal:%@'.fmt(publishDate || 'No publish date.'));

      var route = this;

      var user = route.get('session.user.email');

      if (!route.get('buildEnvironment.local')) {

        var data = {
          userid: user,
          sitename: route.get('session.site.name'),
          id: uniqueId()
        };

        if (publishDate) {
          data.build_time = publishDate;
        }

        window.ENV.firebase.root().child('management/commands/build/' + route.get('session.site.name')).set(data);
        route.set('buildEnvironment.building', true);

      } else {

        route.send('gruntCommand', 'build', function () {
          route.send('notify', 'success', 'Local build complete', { icon: 'refresh' });
        });

      }
    },

    gruntCommand: function (command, callback) {
      this.gruntCommand.apply(this, arguments);
    }
  }
});
<commit_msg>message listeners<commit_after>function uniqueId() {
  return Date.now() + 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}

export default Ember.Route.extend({
  notifications: [],

  getBuildEnvironment: function () {

    var route = this;

    if (Ember.isEmpty(route.get('buildEnvironment.isInitialized'))) {
      Ember.Logger.log('ApplicationRoute::getBuildEnvironment');
    } else {
      Ember.Logger.log('ApplicationRoute::getBuildEnvironment::skip');
      return Ember.RSVP.Promise.resolve();
    }

    var buildEnv = route.get('buildEnvironment');

    buildEnv.set('isInitialized', true);
    buildEnv.set('keepReload', Ember.$('meta[name="keepReload"]').attr('content'));

    var siteName = Ember.$('meta[name="siteName"]').attr('content');

    buildEnv.set('siteDisplayName', window.ENV.selfHosted ? siteName.replace('/,1/g', '.') : siteName);
    buildEnv.set('selfHosted', window.ENV.selfHosted);

    if (!window.ENV.selfHosted) {
      window.ENV.siteDNS = siteName + '.webhook.org';
    }
    window.ENV.firebaseRoot.child('/management/sites/' + siteName + '/dns').on('value', function (snap) {
      if (snap.val()) {
        window.ENV.siteDNS = snap.val();
      }
    });

    if (window.ENV.uploadUrl.indexOf('http://') !== 0) {
      window.ENV.uploadUrl = 'http://' + window.ENV.uploadUrl;
    }
    if (window.ENV.uploadUrl.substr(-1) !== '/') {
      window.ENV.uploadUrl = window.ENV.uploadUrl + '/';
    }

    var req = new XMLHttpRequest();
    req.open('GET', document.location, false);
    req.send(null);
    var headers = req.getAllResponseHeaders().toLowerCase();

    return new Ember.RSVP.Promise(function (resolve, reject) {

      if (headers.indexOf('x-webhook-local') !== -1) {
        buildEnv.set('local', true);
        var localSocket = Ember.Object.create({
          socket: new window.WebSocket('ws://' + document.location.hostname + ':6557')
        });

        buildEnv.set('localSocket', localSocket);

        localSocket.reopen({
          connected: function () {
            return this.get('socket.readyState') === 1;
          }.property('socket.readyState')
        });

        localSocket.socket.onmessage = function (event) {
          var storedCallback;
          if (event.data === 'done') {
            storedCallback = localSocket.get('doneCallback');
            localSocket.set('doneCallback', null);

            if (storedCallback) {
              storedCallback();
            }
          } else if (event.data.indexOf('done:') === 0) {
            var data = JSON.parse(event.data.replace('done:', ''));

            storedCallback = localSocket.get('doneCallback');
            localSocket.set('doneCallback', null);

            if (storedCallback) {
              storedCallback(data);
            }
          } else if (event.data.indexOf('message:') === 0) {
            var message = JSON.parse(event.data.replace('message:', ''));
            localSocket.set('message', message);
          }
        };

        localSocket.socket.onopen = function () {
          localSocket.set('connected', true);
          resolve();
        };

        localSocket.socket.onerror = function () {
          resolve();
        };

        if (!$('meta[name=suppressAlert]').attr('content')) {
          localSocket.socket.onclose = function () {
            localSocket.set('lostConnection', true);
          };
        }

        // Shut down LiveReload
        if (window.LiveReload && !buildEnv.get('keepReload')) {
          var shutDown = new CustomEvent('LiveReloadShutDown');
          document.addEventListener("LiveReloadConnect", function () {
            document.dispatchEvent(shutDown);
          }, false);
        }

      } else {
        resolve();
      }
    });
  },

  getMessageSuport: function () {

    var route = this;

    if (Ember.isEmpty(route.get('session.supportedMessages'))) {
      Ember.Logger.log('ApplicationRoute::getMessageSupport');
    } else {
      Ember.Logger.log('ApplicationRoute::getMessageSupport::skip');
      return Ember.RSVP.Promise.resolve();
    }

    route.set('session.supportedMessages', Ember.Object.create());

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var localSocket = route.get('buildEnvironment.localSocket');
      if (!localSocket || !localSocket.connected) {

        Ember.$.ajax({
          dataType: 'jsonp',
          jsonpCallback: 'supportedJSONPCallback',
          url: '/.wh/_supported/',
          success: resolve,
          error: reject
        });

      } else {

        route.gruntCommand('supported_messages', resolve);

      }

    }).then(function (messages) {
      messages.forEach(function (message) {
        route.get('session.supportedMessages').set(message, true);
      });
      Ember.Logger.info('Server Messages Supported:', messages.join(', '));
    }, function (error) {
      Ember.Logger.warn('Failed to retrieve supported grunt messages.');
    });
  },

  // Confirm that user has completed validation steps
  validateUser: function (user) {

    Ember.Logger.log('ApplicationRoute::validateUser');

    var session = this.get('session');
    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var getToken = function (snapshot) {
        var token = snapshot.val();
        session.set('site.token', token);

        window.ENV.firebase = window.ENV.firebaseRoot.child('buckets/' + siteName + '/' + token + '/dev');

        // if you just logged in, we have to set the firebase property
        DS.FirebaseAdapter.reopen({
          firebase: window.ENV.firebase
        });

        Ember.Logger.log('ApplicationRoute::validateUser::✓');

        resolve(user);
      };

      managementSiteRef.child('key').once('value', getToken, function (error) {

        if (error.code === 'PERMISSION_DENIED') {
          var escapedEmail = user.email.replace(/\./g, ',1');
          // Try to add to user list, if this is allowed they were a potential user
          managementSiteRef.child('users').child(escapedEmail).set(user.email, function () {
            managementSiteRef.root().child('management/users').child(escapedEmail).child('sites/user').child(siteName).set(true, function (error) {
              // Try to delete self from potential user list
              managementSiteRef.child('potential_users').child(escapedEmail).remove(function () {
                // Redo original authorization call
                managementSiteRef.child('key').once('value', getToken, reject);
              }, reject);
            });
          }, reject);
        } else {
          reject(error);
        }

      });

    });

  },

  initializeUser: function (user) {

    Ember.Logger.log('ApplicationRoute::initializeUser');

    var route = this;
    var session = route.get('session');
    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    if (!route.get('buildEnvironment.local')) {
      this.setupMessageListener();
    }

    Ember.Logger.info('Logged in as ' + user.email);

    var escapedEmail = user.email.replace(/\./g, ',1');

    var ownerCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      session.set('isOwner', false);
      managementSiteRef.once('value', function (snapshot) {
        var siteData = snapshot.val();

        if (siteData.owners[escapedEmail]) {
          Ember.Logger.info('Logged in user is a site owner.');
          session.set('isOwner', true);
        } else if (siteData.users[escapedEmail]) {
          Ember.Logger.info('Logged in user is a site user.');
        } else {
          Ember.Logger.error('Logged in user is neither a site owner or site user??');
        }

        Ember.run(null, resolve);

      });
    });

    // Default billing values
    var billing = Ember.Object.create({
      active: true,
      status: 'paid',
      url: 'http://billing.webhook.com/site/' + siteName + '/',
    });
    billing.reopen({
      isPaid: function () {
        return this.get('status') === 'paid';
      }.property('status'),
      isTrial: function () {
        return this.get('status') === 'trialing';
      }.property('status')
    });
    session.set('billing', billing);

    // Grab actual billing values
    var billingRef = window.ENV.firebaseRoot.child('billing/sites/' + siteName);

    var activeCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('active').once('value', function (snapshot) {
        session.set('billing.active', snapshot.val());
        Ember.run(null, resolve);
      });
    });

    var statusCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('status').once('value', function (snapshot) {
        session.set('billing.status', snapshot.val());
        Ember.run(null, resolve);
      });
    });

    var endTrialCheck = new Ember.RSVP.Promise(function (resolve, reject) {
      billingRef.child('endTrial').once('value', function (snapshot) {
        var endTrial = snapshot.val();
        if (endTrial) {
          var endTrialDays = Math.ceil(moment(snapshot.val()).diff(moment(), 'days', true));
          session.set('billing.endTrial', endTrial);
          session.set('billing.endTrialDays', endTrialDays);
          session.set('billing.endTrialIsLastDay', endTrialDays === 1);
        }
        Ember.run(null, resolve);
      });
    });

    return Ember.RSVP.Promise.all([ownerCheck, activeCheck, statusCheck, endTrialCheck]).then(function () {
      session.set('user', user);
    });

  },

  getSession: function () {

    var route = this;
    var session = route.get('session');

    if (Ember.isEmpty(session.get('auth'))) {
      Ember.Logger.log('ApplicationRoute::getSession');
    } else {
      Ember.Logger.log('ApplicationRoute::getSession::skip');
      return Ember.RSVP.Promise.resolve();
    }

    var siteName = session.get('site.name');

    var managementSiteRef = window.ENV.firebaseRoot.child('management/sites/' + siteName);

    return new Ember.RSVP.Promise(function (resolve, reject) {

      var firebaseAuth = new FirebaseSimpleLogin(window.ENV.firebaseRoot, function (error, user) {

        if (route.get('isDestroyed')) {
          return;
        }

        if (user) {
          // Logged in
          route.validateUser(user).then(route.initializeUser.bind(route)).then(resolve, function (error) {
            session.set('user', null);
            session.set('site.token', null);
            session.set('error', error);
          });
        } else if (error) {
          // an error occurred while attempting login
          session.set('user', null);
          session.set('site.token', null);
          session.set('error', error);
          reject(error);
        } else {
          // user is logged out
          session.set('user', null);
          session.set('site.token', null);
          resolve();
        }

      });

      session.set('auth', firebaseAuth);

    });
  },

  beforeModel: function () {
    Ember.Logger.log('ApplicationRoute::beforeModel');

    var route = this;

    return this.getBuildEnvironment().then(function () {
      return Ember.RSVP.Promise.all([
        route.getSession(),
        route.getMessageSuport()
      ]);
    });
  },

  setupController: function (controller) {
    controller.set('notifications', this.get('notifications'));
    this._super.apply(this, arguments);
  },

  gruntCommand: function (command, callback) {
    Ember.Logger.log('%cgruntCommand -> ' + command, 'color: purple; font-weight: bold');

    var localSocket = this.get('buildEnvironment.localSocket');
    if (localSocket && localSocket.connected) {
      localSocket.socket.send(command);
      if (callback) {
        localSocket.doneCallback = callback;
      }
    }
  },

  messageListener: null,
  setupMessageListener: function () {

    var route = this;
    var listener = this.get('messageListener');
    var siteName = this.get('session.site.name');
    var buildEnv = this.get('buildEnvironment');

    var ref = window.ENV.firebase.root().child('management/sites/' + siteName + '/messages');
    if (listener) {
      ref.off('child_added', listener);
      listener = null;
    }

    var initialIds = {};
    ref.once('value', function(totalData) {
      var val = totalData.val();

      for(var key in val) {
        initialIds[key] = true;
      }

      listener = ref.on('child_added', function(snap) {
        var now = Date.now();
        var message = snap.val();
        var id = snap.name();

        if(!initialIds[id]) {
          if(message.code === 'BUILD') {
            if(message.status === 0) {
              route.send('notify', 'success', 'Site build complete', { icon: 'refresh' });
            } else {
              route.send('notify', 'danger', 'Site build failed', { icon: 'remove' });
            }
            buildEnv.set('building', false);
          }
        }
      });

      route.set('messageListener', listener);

    });
  },

  actions: {
    logoutUser: function () {

      window.ENV.firebase.child('presence/online').child(this.get('session.user.uid')).remove();

      this.get('session.auth').logout();
      this.set('session.user', null);
      this.transitionTo('login');
    },

    notify: function (type, message, options) {

      options = options || {};

      var notifications = this.get('notifications'),
          notification = Ember.Object.create({
            className: 'wy-tray-item' + (type ? '-' + type : ''),
            message: message
          });

      if (options.icon) {
        notification.set('iconClass', 'icon icon-' + options.icon);
      }

      if (options.className) {
        notification.set('extraClassName', options.className);
      }

      notifications.pushObject(notification);

      Ember.run.later(this, function () {
        notification.set('state', 'on');
      }, 10);

      Ember.run.later(this, function () {
        notification.set('state', null);
      }, 4500);

      Ember.run.later(this, function () {
        notifications.removeObject(notification);
      }, 5000);

    },

    chooseLanguage: function (language) {
      Ember.Logger.log('Changing language to %@'.fmt(language));
      window.localStorage.setItem('webhook-cms-language', language);

      Ember.Logger.log('Resetting app.');
      window.App.reset();
    },

    buildSignal: function (publishDate) {
      Ember.Logger.info('Sending build signal:%@'.fmt(publishDate || 'No publish date.'));

      var route = this;

      var user = route.get('session.user.email');

      if (!route.get('buildEnvironment.local')) {

        var data = {
          userid: user,
          sitename: route.get('session.site.name'),
          id: uniqueId()
        };

        if (publishDate) {
          data.build_time = publishDate;
        }

        window.ENV.firebase.root().child('management/commands/build/' + route.get('session.site.name')).set(data);
        route.set('buildEnvironment.building', true);

      } else {

        route.send('gruntCommand', 'build', function () {
          route.send('notify', 'success', 'Local build complete', { icon: 'refresh' });
        });

      }
    },

    gruntCommand: function (command, callback) {
      this.gruntCommand.apply(this, arguments);
    }
  }
});
<|endoftext|>


<commit_before>/* global $ */
'use strict'

import React from 'react'
import { Link } from 'react-router'
import Clock from '../Clock'
import axios from 'axios'

import './smoothslides.theme.css'
import './ScreenSaver.styl'

const jsonUrl = 'http://localhost:3030/images/json'

class ScreenSaver extends React.Component {

  constructor (props) {
    super(props)
    this.getImages = this.getImages.bind(this)
    this.state = {
      images: [],
      loading: true,
      error: null
    }
  }

  componentWillMount () {
    this.getImages()
  }

  componentDidUpdate () {
    const screenSaverElement = $('#ScreenSaver')
    screenSaverElement.smoothSlides({
      matchImageSize: false,
      navigation: false,
      pagination: false
    })
    screenSaverElement.on('smoothslidesReordered', (e) => {
      const renderedIndexes = $('.ss-slide img').map((index, element) => {
        const key = $(element).attr('key')
        return (typeof key === typeof undefined) ? '' : key.split('image')[1]
      }).get()
      const index = this.randomIndexOfArrayWithExclusions(this.state.images, renderedIndexes)
      $('.ss-slide:first img', screenSaverElement).attr({
        'key': `image${index}`,
        'src': this.state.images[index]
      })
    })
  }

  getImages () {
    let self = this
    let requestImages = new Promise((resolve, reject) => {
      axios.get(jsonUrl)
        .then(response => {
          resolve(response.data)
        })
        .catch(error => {
          console.log(error)
        })
    })

    Promise.all([requestImages])
      .then(imageArrays => {
        const tags = ['todays_force']
        let imageUrls = []

        for (let i = 0; i < tags.length; i++) {
          let tag = tags[i]
          let imageFiles = imageArrays[0][tag]

          imageFiles.forEach((image) => {
            imageUrls.push(self.buildImageUrl(tag, image.filename))
          })
        }

        let randomImageUrls = []
        let _tmp = imageUrls.slice()
        for (var i = 0; i < imageUrls.length; i++) {
          var index = Math.ceil(Math.random() * 10) % _tmp.length
          randomImageUrls.push(_tmp.splice(index, 1)[0])
        }

        self.setState({
          loading: false,
          images: randomImageUrls
        })
      })
      .catch((error) => {
        self.setState({
          loading: false,
          error: 'Unable to load images.'
        })
        console.log(error)
      })
  }

  buildImageUrl (tag, filename) {
    return ('http://localhost:3030/' + tag + '/' + filename)
  }

  randomIndexOfArrayWithExclusions (arr, exclusions) {
    let index
    do {
      index = Math.floor(Math.random() * arr.length)
    } while (exclusions.indexOf(index) !== -1)
    return index
  }

  render () {
    const imageHtml = (this.state.images)
    ? this.state.images.slice(0, 4).map((imageUrl, index) => {
      const key = `image${index}`
      return ()
    })
    :
Loading Images

    return (

        <Link className='BackLink' to='/'>Back</Link>
        <Clock />

          {imageHtml}

    )
  }
}

ScreenSaver.propTypes = {
  images: React.PropTypes.array
}

export default ScreenSaver
<commit_msg>Fixing indexing of screen saver images<commit_after>/* global $ */
'use strict'

import React from 'react'
import { Link } from 'react-router'
import Clock from '../Clock'
import axios from 'axios'

import './smoothslides.theme.css'
import './ScreenSaver.styl'

const jsonUrl = 'http://localhost:3030/images/json'

class ScreenSaver extends React.Component {

  constructor (props) {
    super(props)
    this.getImages = this.getImages.bind(this)
    this.state = {
      images: [],
      loading: true,
      error: null
    }
  }

  componentWillMount () {
    this.getImages()
  }

  componentDidUpdate () {
    const screenSaverElement = $('#ScreenSaver')
    screenSaverElement.smoothSlides({
      matchImageSize: false,
      navigation: false,
      pagination: false
    })
    screenSaverElement.on('smoothslidesReordered', (e) => {
      const lastRenderedIndex = parseInt($('.ss-slide:nth-child(2) img', screenSaverElement).attr('data-index'))
      const nextIndex = (lastRenderedIndex === (this.state.images.length - 1))
        ? 0
        : lastRenderedIndex + 1
      $('.ss-slide:first img', screenSaverElement).attr({
        'src': this.state.images[nextIndex],
        'data-index': nextIndex
      })
    })
  }

  getImages () {
    let self = this
    let requestImages = new Promise((resolve, reject) => {
      axios.get(jsonUrl)
        .then(response => {
          resolve(response.data)
        })
        .catch(error => {
          console.log(error)
        })
    })

    Promise.all([requestImages])
      .then(imageArrays => {
        const tags = ['todays_force']
        let imageUrls = []

        for (let i = 0; i < tags.length; i++) {
          let tag = tags[i]
          let imageFiles = imageArrays[0][tag]

          imageFiles.forEach((image) => {
            imageUrls.push(self.buildImageUrl(tag, image.filename))
          })
        }

        let randomImageUrls = []
        let _tmp = imageUrls.slice()
        for (var i = 0; i < imageUrls.length; i++) {
          var index = Math.ceil(Math.random() * 10) % _tmp.length
          randomImageUrls.push(_tmp.splice(index, 1)[0])
        }

        self.setState({
          loading: false,
          images: randomImageUrls
        })
      })
      .catch((error) => {
        self.setState({
          loading: false,
          error: 'Unable to load images.'
        })
        console.log(error)
      })
  }

  buildImageUrl (tag, filename) {
    return ('http://localhost:3030/' + tag + '/' + filename)
  }

  randomIndexOfArrayWithExclusions (arr, exclusions) {
    let index
    do {
      index = Math.floor(Math.random() * arr.length)
    } while (exclusions.indexOf(index) !== -1)
    return index
  }

  render () {
    const imageHtml = (this.state.images)
    ? this.state.images.slice(0, 4).map((imageUrl, index) => {
      const key = `image${index}`
      return ()
    })
    :
Loading Images

    return (

        <Link className='BackLink' to='/'>Back</Link>
        <Clock />

          {imageHtml}

    )
  }
}

ScreenSaver.propTypes = {
  images: React.PropTypes.array
}

export default ScreenSaver
<|endoftext|>


<commit_before>// routes/note_routes.js

var ObjectID = require('mongodb').ObjectID;

module.exports = function(app, db) {

  app.get('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: id };
    db.collection('notes').find(details).toArray(function(err, results){
      var ret = [];
    for(var i =0;i<results.length;i++) {
      console.log(results[i]);
      if(results[i]["status"] == 1) {
        ret.push(results[i]);
      }
    }
    console.log(ret); // output all records
    res.send(ret);
   });
  });

  app.post('/notes', (req, res) => {
    const note = { key:req.body.key, val:req.body.val,
                   likes: req.body.likes, cont: req.body.cont,
                   cat: req.body.cat, status: 0};
    db.collection('notes').insert(note, (err, result) => {
      if (err) {
        res.send({ 'error': 'An error has occurred' });
      } else {
        res.send(result.ops[0]);
      }
    });
  });

  app.delete('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: query };
    db.collection('notes').remove(details, (err, item) => {
      if (err) {
        res.send({'error':'An error has occurred'});
      } else {
        res.send('Note ' + id + ' deleted!');
      }
    });
  });

  app.put('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: id };
    const note = { key:req.body.key, val:req.body.val,
                   likes: req.body.likes, cont: req.body.cont,
                   cat: req.body.cat, status: req.body.stat};
    db.collection('notes').update(details, note, (err, result) => {
      if (err) {
          res.send({'error':'An error has occurred'});
      } else {
          res.send(note);
      }
    });
  });

};
<commit_msg>added functionality for instant search<commit_after>// routes/note_routes.js

var ObjectID = require('mongodb').ObjectID;

module.exports = function(app, db) {

  app.get('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: {$regex : `.*${id}.*`}};
    db.collection('notes').find(details).toArray(function(err, results){
      var ret = [];
    for(var i =0;i<results.length;i++) {
      if(results[i]["status"] == 1) {
        ret.push(results[i]);
      }
    }
    console.log(ret); // output all records
    res.send(ret);
   });
  });

  app.post('/notes', (req, res) => {
    const note = { key:req.body.key, val:req.body.val,
                   likes: req.body.likes, cont: req.body.cont,
                   cat: req.body.cat, status: 0};
    db.collection('notes').insert(note, (err, result) => {
      if (err) {
        res.send({ 'error': 'An error has occurred' });
      } else {
        res.send(result.ops[0]);
      }
    });
  });

  app.delete('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: query };
    db.collection('notes').remove(details, (err, item) => {
      if (err) {
        res.send({'error':'An error has occurred'});
      } else {
        res.send('Note ' + id + ' deleted!');
      }
    });
  });

  app.put('/notes/:id', (req, res) => {
    const id = req.params.id;
    const details = { key: id };
    const note = { key:req.body.key, val:req.body.val,
                   likes: req.body.likes, cont: req.body.cont,
                   cat: req.body.cat, status: req.body.stat};
    db.collection('notes').update(details, note, (err, result) => {
      if (err) {
          res.send({'error':'An error has occurred'});
      } else {
          res.send(note);
      }
    });
  });

};
<|endoftext|>


<commit_before>//data we need to serve via ajax.
var orm = require('../../config/orm.js');
var hospitalArray = require('../data/resources.js');
var bloodArray = require('../data/blood.js');

//actually routing

module.exports = function(app){

	app.get('/resources', function(req, res){

		res.json(hospitalArray);
		res.json(bloodArray);
	})

	app.post('/resources', function (req, res){

		hospitalArray.push(req.body);
		bloodArray.push(req.body);
	})

	app.post('/newTask', function (req, res){
		orm.AvailableTasks(req.body.description, req.body.contactName, req.body.contactNumber, req.body.Address, req.body.TaskTime, req.body.membersNeeded);
		res.send(function() {
			alert("Task Created successfully!");
	})

	app.post('/newMember', function (req, res){
		orm.AvailableTasks(req.body.memberName, req.body.emailAddress, req.body.contactNumber, req.body.twitterHandle, req.body.bloodType);
		res.send(function() {
			alert("User added successfully!");
		})
	})

	app.post('/newCorpMember', function (req, res){
		orm.AvailableTasks(req.body.description, req.body.contactName, req.body.contactNumber, req.body.Address, req.body.TaskTime, req.body.membersNeeded);
		res.send(function() {
			alert("Corporate Member Added successfully!");
	})
};<commit_msg>added dropped close tag in api-routes.js<commit_after>//data we need to serve via ajax.
var orm = require('../../config/orm.js');
var hospitalArray = require('../data/resources.js');
var bloodArray = require('../data/blood.js');

//actually routing

module.exports = function(app){

	app.get('/resources', function(req, res){

		res.json(hospitalArray);
		res.json(bloodArray);
	})

	app.post('/resources', function (req, res){

		hospitalArray.push(req.body);
		bloodArray.push(req.body);
	})

	app.post('/newTask', function (req, res){
		orm.AvailableTasks(req.body.description, req.body.contactName, req.body.contactNumber, req.body.Address, req.body.TaskTime, req.body.membersNeeded);
		res.send(function() {
			alert("Task Created successfully!");
		});
	});

	app.post('/newMember', function (req, res){
		orm.AvailableTasks(req.body.memberName, req.body.emailAddress, req.body.contactNumber, req.body.twitterHandle, req.body.bloodType);
		res.send(function() {
			alert("User added successfully!");
		});
	})

	app.post('/newCorpMember', function (req, res){
		orm.AvailableTasks(req.body.description, req.body.contactName, req.body.contactNumber, req.body.Address, req.body.TaskTime, req.body.membersNeeded);
		res.send(function() {
			alert("Corporate Member Added successfully!");
		})
	});
}
<|endoftext|>


<commit_before>"use strict";

import { SEARCH_KEY_NAME, OLUTTAMO_SEARCH_SHORTCUT } from "./constants";

// Enable chromereload by uncommenting this line:
// import "./lib/livereload";

import createMenu from "./lib/contextMenus";

chrome.runtime.onInstalled.addListener(function (details) {
    createMenu();
});

chrome.commands.onCommand.addListener(shortcut => {
    if (shortcut === OLUTTAMO_SEARCH_SHORTCUT){
        chrome.browserAction.setBadgeText({text: "<3"});
        chrome.tabs.executeScript(null, {
            file: "./scripts/getSelection.js",
            runAt: "document_end",
        });
    }
});

chrome.storage.onChanged.addListener(function(changes, namespace) {
    if (changes[SEARCH_KEY_NAME]){
        chrome.browserAction.setBadgeText({text: "<3"});
        chrome.tabs.executeScript(null, {
            file: "./scripts/search.js",
            runAt: "document_end",
        });
    }
});
<commit_msg>Add setBadgeDetails func<commit_after>"use strict";

import { BEER_KEY_NAME, SEARCH_KEY_NAME, OLUTTAMO_SEARCH_SHORTCUT } from "./constants";

// Enable chromereload by uncommenting this line:
import "./lib/livereload";

import createMenu from "./lib/contextMenus";

chrome.runtime.onInstalled.addListener((details) => {
    createMenu();
});

chrome.commands.onCommand.addListener(shortcut => {
    if (shortcut === OLUTTAMO_SEARCH_SHORTCUT){
        setBadgeDetails("...", "#8CF0C8");
        chrome.tabs.executeScript(null, {
            file: "./scripts/getSelection.js",
            runAt: "document_end",
        });
    }
});

chrome.storage.onChanged.addListener((changes, namespace) => {
    if (changes[SEARCH_KEY_NAME]){
        setBadgeDetails("...", "#8CF0C8");
        chrome.tabs.executeScript(null, {
            file: "./scripts/search.js",
            runAt: "document_end",
        });
    } else if (changes[BEER_KEY_NAME]){
        setBadgeDetails("<3", "#9669AF");
    }
});

function setBadgeDetails(text, color){
    chrome.browserAction.setBadgeText({text: text});
    chrome.browserAction.setBadgeBackgroundColor({color: color});
}
<|endoftext|>


<commit_before>

var tag = ["javascript","angularjs","html","css","java","cpp","sql"];

		function setColor(obj,color){
			obj.style.color = color;
		}

		//central angular app
		//var app = angular.module("profile",[]);

		app.factory('Search',function(){
			return {search:true,text:""};
		});

		app.factory('Naruto',function(){
			return {
				Sasuke:"",
				Sakura:"Search Team",
				setSearchE: function(){},
				setSearchP: function(){},
				setSearchT: function(){},
				searchName: function(){}
			};
		});

		//controll search page
		app.controller("searchPage",function($scope,Naruto,$cookies,$window,$firebaseObject){

			//data lists got from firebase
			var tag = [];
			var users = [];
			var teams = [];

			var tagini;
			$scope.teamini;

			//initialize data lists
			var ref = firebase.database().ref("eventList");
			var event_list = $firebaseObject(ref);
			$scope.userList = $firebaseObject(firebase.database().ref("userList"));
			//get tags,teams and users from tag list in firebase
			$scope.loadData = function(){
				$scope.hehe = "";
				$scope.currentTag = [];
				$scope.resultTag = [];
				$scope.filterResult = [];
				Naruto.Saskue = "";

				tagini = event_list[Naruto.Luffy]["skillTable"];

				$scope.teamini = event_list[Naruto.Luffy]["teamList"];
				angular.forEach(tagini, function(value,key){
					tag.push(key);
				});
				angular.forEach($scope.teamini,function(value,key){
					teams.push(key);
				});

				angular.forEach($scope.userList,function(value,key){
					//only get the user that belongs to current event
					if($scope.userList[key].Membership !== undefined && $scope.userList[key].Membership[Naruto.Luffy] !== undefined){
						users.push($scope.userList[key].name);

					}
				});
			}

			/*
			$scope.userList.$loaded(function(){
				event_list.$loaded(function(){
					tagini = event_list[Naruto.luffy]["skills"];

					$scope.teamini = event_list[Naruto.luffy]["teamList"];
					angular.forEach(tagini, function(value,key){
						tag.push(key);
					});
					angular.forEach($scope.teamini,function(value,key){
						teams.push(key);
					});

					angular.forEach($scope.userList,function(value,key){
						//only get the user that belongs to current event
						if($scope.userList[key].Membership !== undefined &&
								$scope.userList[key].Membership["comp3111"] !== undefined){
							users.push($scope.userList[key].name);

						}

					});

				});
			});
			*/
			$scope.teamyeah = false;
			$scope.useryeah = true;
			$scope.currentTag = [];
			$scope.resultTag = [];
			$scope.searchResult = [];
			$scope.filterResult = [];
			$scope.hehe = "";
			// $scope.startSearch = Search;
			/*for(key in tag){
				$scope.currentTag.push(key);
			};*/

			$scope.Naruto = Naruto;
			$scope.searchType = Naruto.Sakura;
			var setSearchT = function(){
				Naruto.Sakura = "Search Team";
				$scope.teamyeah = false;
				$scope.useryeah = true;
				$scope.clear();
			};
			var setSearchP = function(){
				Naruto.Sakura = "Search Person";
				$scope.useryeah = false;
				$scope.teamyeah = true;
				$scope.clear();
			};
			$scope.setSearchE = function(){
				$scope.searchType = "Search Event";
			};
			// Naruto.setSearchE = $scope.setSearchE;
			Naruto.setSearchP = setSearchP;
			Naruto.setSearchT = setSearchT;

			$scope.reset = function(index){
				var key;
				$scope.resultTag.push($scope.currentTag[index]);
				var temp = $scope.currentTag;
				$scope.currentTag = [];
				for(var i = 0;i < temp.length;i++){
					if(i != index){
						$scope.currentTag.push(temp[i]);
					}
				}
				filterFunction();
			};

			$scope.delete = function(index){
				var temp = $scope.resultTag;

				$scope.resultTag = [];
				for(var i = 0;i < temp.length;i++){
					if(i != index){
						$scope.resultTag.push(temp[i]);
					}
					else{
						$scope.currentTag.push(temp[i]);
					}
				}
				filterFunction();

			};

			$scope.clear = function(){
				$scope.hehe = "";
				$scope.currentTag = [];
				$scope.resultTag = [];
				$scope.filterResult = [];
			};

			var currentEvent;
			$scope.autocomplete = function(){

				if(currentEvent != Naruto.Luffy){
					currentEvent = Naruto.Luffy;
					$scope.loadData();

				}
				var iChars = "~`!@#$%^&*+=-[]\\\';,/{}|\":<>?";
				for (var i = 0; i < $scope.hehe.length; i++){
					if (iChars.indexOf($scope.hehe.charAt(i)) != -1){
						alert ("File name has special characters ~`!@#$%^&*+=-[]\\\';,/{}|\":<>? \nThese are not allowed\n");
						$scope.hehe = "";
						break;
					}

				}
				$scope.currentTag = [];
				var reg = $scope.hehe;
				reg.replace(' ','');
				if(reg !== ""){
					for(var i = 0;i < tag.length;i++){
						var k = 0;
						var valid = false;
						for(var j = 0;j < reg.length;j++){

							while(k < tag[i].length){
								if(tag[i].charAt(k) == reg.charAt(j)){
									k++;
									if(j == reg.length-1){
										valid = true;
									}
									break;
								}else{
									k++;
								}

							}
						}
						if(valid){
							$scope.currentTag.push(tag[i]);
						}

					}
				}
			};

			var filterFunction = function(){
				$scope.filterResult = [];
				if(Naruto.Sakura == "Search Team"){
					for(var i = 0;i < tagini[$scope.resultTag[0]].teams.length;i++){
						$scope.filterResult.push(tagini[$scope.resultTag[0]].teams[i]);
					}
					for(var i = 1;i < $scope.resultTag.length;i++){
						//get the team list related to current result tag
						var list = tagini[$scope.resultTag[i]].teams;
						for(var k = 0;k < $scope.filterResult.length;k++){
							var bingo = false;
							for(var j = 0;j < list.length;j++){
								if(list[j] == $scope.filterResult[k]){
									bingo = true;
									break;
								}
							}
							if(!bingo){
								$scope.filterResult[k] = "@";
							}
						}
						var temp = [];
						for(var k = 0;k <　$scope.filterResult.length;k++){
							if($scope.filterResult[k] != "@"){
								temp.push($scope.filterResult[k]);
							}
						}
						$scope.filterResult = temp;
					}
				}else{
					for(var i = 0;i < tagini[$scope.resultTag[0]].users.length;i++){
						$scope.filterResult.push(tagini[$scope.resultTag[0]].users[i]);
					}
					for(var i = 1;i < $scope.resultTag.length;i++){
						//get the team list related to current result tag
						var list = tagini[$scope.resultTag[i]].users;
						for(var k = 0;k < $scope.filterResult.length;k++){
							var bingo = false;
							for(var j = 0;j < list.length;j++){
								if(list[j] == $scope.filterResult[k]){
									bingo = true;
									break;
								}
							}
							if(!bingo){
								$scope.filterResult[k] = "@";
							}
						}
						var temp = [];
						for(var k = 0;k <　$scope.filterResult.length;k++){
							if($scope.filterResult[k] != "@"){
								temp.push($scope.filterResult[k]);
							}
						}
						$scope.filterResult = temp;
					}
				}
			}

			//private search method
			var findResult = function(type){
				var list = [];
				if(type == "team"){
					list = teams;
				}
				else if(type == "user"){
					list = users;
				}
				else{}//event list is not yet defined
				var m = Naruto.Sasuke.length;

				//retrive every tags in tagList
				for(var i = 0;i < list.length;i++){
					var n = list[i].length;

					//create a new 2D array,initialize with all 0
					var arr = []
					for (var row = 0; row < m+1; row++) {
						arr[row] = [];
						for(var column = 0; column < n+1; column++){
							arr[row].push(0);
						}
					}

					//record the maximum number of matched subsequence in two strings
					var max = 0;

					//start to fill the 2D array
					for(var x = 1;x <= m; x++){
						for(var y = 1;y <= n;y++){
							if(Naruto.Sasuke.charAt(x-1) == list[i].charAt(y-1)){
								arr[x][y] = arr[x-1][y-1] + 1;
								if(arr[x][y] > max){
									max = arr[x][y];
								}
							}
							else if(arr[x-1][y] > arr[x][y-1]){
								arr[x][y] = arr[x-1][y];
							}
							else{
								arr[x][y] = arr[x][y-1];
							}
						}
					}
					var match = arr[m][n]/Naruto.Sasuke.length
					if(match>= 0.6){
						$scope.searchResult.push(list[i]);
					}
				}

				// tag = $scope.searchResult;
			}

			var searchName = function(){
				if(currentEvent != Naruto.Luffy){
					currentEvent = Naruto.Luffy;
					$scope.loadData();

				}
				$scope.searchResult = [];
				if($scope.resultTag.length == 0){
					if(Naruto.Sakura == "Search Team"){
						findResult('team');

						$scope.filterResult = $scope.searchResult;
					}
					else{
						findResult('user');
						var idList = [];
						for(var i = 0;i < $scope.searchResult.length;i++){
							angular.forEach($scope.userList,function(value,key){
								//only get the user that belongs to current event
								if($scope.userList[key].name == $scope.searchResult[i]){
									idList.push(key);
								}
							});
						}
						$scope.filterResult = idList;
					}
				}
				else{

				}
			}
			Naruto.searchName = searchName;
		});

		//controll navigation bar
		app.controller("navbar",function($scope,Naruto,Search,$firebaseObject){
			// $scope.searchKey = "";
			// $scope.searchType = Naruto.Sakura;
			// Naruto.Sasuke = $scope.searchKey;
			var ref = firebase.database().ref("eventList");
			$scope.event_list = $firebaseObject(ref);
			$scope.Naruto = Naruto;

			// $scope.setThisSearchP = function(){
			// 	Naruto.setSearchP();
			// };
			// $scope.setThisSearchT = function(){
			// 	Naruto.setSearchT();
			// }
			// $scope.searchThisName = function(){
			// 	Naruto.searchName();
			// };
			$scope.start = function(){
				Search.search = false;

			};
			$scope.haha = function(){
				alert("hah");
			}
		});

		/*
		function addTag(obj){
			var filterTags = document.getElementById("searchTag");
			var key = obj.innerHTML;
			tag[key] = false;
			filterTags.appendChild(obj);
			obj.removeAttribute("onclick")
			var span = document.createElement("span")
			var text = document.createTextNode(String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160));
			span.appendChild(text);
			obj.appendChild(span);

			var close = document.createElement("span");
			close.setAttribute("style","font-weight:bold");

			text = document.createTextNode(String.fromCharCode(215));
			close.appendChild(text);
			obj.appendChild(close);
			close.setAttribute("ng-click","reset()")
			close.addEventListener("mouseover", setRed);
			close.addEventListener("mouseleave", setBlack);
			close.addEventListener("click", deleteTag);

		}

		function deleteTag(){
			var currentTag = this.parentNode;

			document.getElementById("predefineTag").appendChild(currentTag);
			currentTag.removeChild(this);
			currentTag.removeChild(currentTag.lastChild);
			var tag = currentTag.innerHTML;
			tag[key] = true;
		}
		*/
<commit_msg>combine filter and search name<commit_after>

var tag = ["javascript","angularjs","html","css","java","cpp","sql"];

		function setColor(obj,color){
			obj.style.color = color;
		}

		//central angular app
		//var app = angular.module("profile",[]);

		app.factory('Search',function(){
			return {search:true,text:""};
		});

		app.factory('Naruto',function(){
			return {
				Sasuke:"",
				Sakura:"Search Team",
				setSearchE: function(){},
				setSearchP: function(){},
				setSearchT: function(){},
				searchName: function(){}
			};
		});

		//controll search page
		app.controller("searchPage",function($scope,Naruto,$cookies,$window,$firebaseObject){

			//data lists got from firebase
			var tag = [];
			var users = [];
			var teams = [];

			var tagini;
			$scope.teamini;

			//initialize data lists
			var ref = firebase.database().ref("eventList");
			var event_list = $firebaseObject(ref);
			$scope.userList = $firebaseObject(firebase.database().ref("userList"));
			//get tags,teams and users from tag list in firebase
			$scope.loadData = function(){
				$scope.hehe = "";
				$scope.currentTag = [];
				$scope.resultTag = [];
				$scope.filterResult = [];
				Naruto.Saskue = "";

				tagini = event_list[Naruto.Luffy]["skillTable"];

				$scope.teamini = event_list[Naruto.Luffy]["teamList"];
				angular.forEach(tagini, function(value,key){
					tag.push(key);
				});
				angular.forEach($scope.teamini,function(value,key){
					teams.push(key);
				});

				angular.forEach($scope.userList,function(value,key){
					//only get the user that belongs to current event
					if($scope.userList[key].Membership !== undefined && $scope.userList[key].Membership[Naruto.Luffy] !== undefined){
						users.push($scope.userList[key].name);

					}
				});
			}

			/*
			$scope.userList.$loaded(function(){
				event_list.$loaded(function(){
					tagini = event_list[Naruto.luffy]["skills"];

					$scope.teamini = event_list[Naruto.luffy]["teamList"];
					angular.forEach(tagini, function(value,key){
						tag.push(key);
					});
					angular.forEach($scope.teamini,function(value,key){
						teams.push(key);
					});

					angular.forEach($scope.userList,function(value,key){
						//only get the user that belongs to current event
						if($scope.userList[key].Membership !== undefined &&
								$scope.userList[key].Membership["comp3111"] !== undefined){
							users.push($scope.userList[key].name);

						}

					});

				});
			});
			*/
			$scope.teamyeah = false;
			$scope.useryeah = true;
			$scope.currentTag = [];
			$scope.resultTag = [];
			$scope.searchResult = [];
			$scope.filterResult = [];
			$scope.hehe = "";
			// $scope.startSearch = Search;
			/*for(key in tag){
				$scope.currentTag.push(key);
			};*/

			$scope.Naruto = Naruto;
			$scope.searchType = Naruto.Sakura;
			var setSearchT = function(){
				Naruto.Sakura = "Search Team";
				$scope.teamyeah = false;
				$scope.useryeah = true;
				$scope.clear();
			};
			var setSearchP = function(){
				Naruto.Sakura = "Search Person";
				$scope.useryeah = false;
				$scope.teamyeah = true;
				$scope.clear();
			};
			$scope.setSearchE = function(){
				$scope.searchType = "Search Event";
			};
			// Naruto.setSearchE = $scope.setSearchE;
			Naruto.setSearchP = setSearchP;
			Naruto.setSearchT = setSearchT;

			$scope.reset = function(index){
				var key;
				$scope.resultTag.push($scope.currentTag[index]);
				var temp = $scope.currentTag;
				$scope.currentTag = [];
				for(var i = 0;i < temp.length;i++){
					if(i != index){
						$scope.currentTag.push(temp[i]);
					}
				}
				filterFunction();
			};

			$scope.delete = function(index){
				var temp = $scope.resultTag;

				$scope.resultTag = [];
				for(var i = 0;i < temp.length;i++){
					if(i != index){
						$scope.resultTag.push(temp[i]);
					}
					else{
						$scope.currentTag.push(temp[i]);
					}
				}
				filterFunction();

			};

			$scope.clear = function(){
				$scope.hehe = "";
				$scope.currentTag = [];
				$scope.resultTag = [];
				$scope.filterResult = [];
			};

			var currentEvent;
			$scope.autocomplete = function(){

				if(currentEvent != Naruto.Luffy){
					currentEvent = Naruto.Luffy;
					$scope.loadData();

				}
				var iChars = "~`!@#$%^&*+=-[]\\\';,/{}|\":<>?";
				for (var i = 0; i < $scope.hehe.length; i++){
					if (iChars.indexOf($scope.hehe.charAt(i)) != -1){
						alert ("File name has special characters ~`!@#$%^&*+=-[]\\\';,/{}|\":<>? \nThese are not allowed\n");
						$scope.hehe = "";
						break;
					}

				}
				$scope.currentTag = [];
				var reg = $scope.hehe;
				reg.replace(' ','');
				if(reg !== ""){
					for(var i = 0;i < tag.length;i++){
						var k = 0;
						var valid = false;
						for(var j = 0;j < reg.length;j++){

							while(k < tag[i].length){
								if(tag[i].charAt(k) == reg.charAt(j)){
									k++;
									if(j == reg.length-1){
										valid = true;
									}
									break;
								}else{
									k++;
								}

							}
						}
						if(valid){
							$scope.currentTag.push(tag[i]);
						}

					}
				}
			};

			var filterFunction = function(){
				var templist = [];
				if(Naruto.Sakura.length != 0){
					for(var i = 0;i < $scope.filterResult.length;i++){
						templist.push($scope.filterResult[i]);
					}
				}

				$scope.filterResult = [];
				if(Naruto.Sakura == "Search Team"){
					for(var i = 0;i < tagini[$scope.resultTag[0]].teams.length;i++){
						$scope.filterResult.push(tagini[$scope.resultTag[0]].teams[i]);
					}
					for(var i = 1;i < $scope.resultTag.length;i++){
						//get the team list related to current result tag
						var list = tagini[$scope.resultTag[i]].teams;
						for(var k = 0;k < $scope.filterResult.length;k++){
							var bingo = false;
							for(var j = 0;j < list.length;j++){
								if(list[j] == $scope.filterResult[k]){
									bingo = true;
									break;
								}
							}
							if(!bingo){
								$scope.filterResult[k] = "@";
							}
						}
						var temp = [];
						for(var k = 0;k <　$scope.filterResult.length;k++){
							if($scope.filterResult[k] != "@"){
								temp.push($scope.filterResult[k]);
							}
						}
						$scope.filterResult = temp;
					}

				}else{
					for(var i = 0;i < tagini[$scope.resultTag[0]].users.length;i++){
						$scope.filterResult.push(tagini[$scope.resultTag[0]].users[i]);
					}
					for(var i = 1;i < $scope.resultTag.length;i++){
						//get the team list related to current result tag
						var list = tagini[$scope.resultTag[i]].users;
						for(var k = 0;k < $scope.filterResult.length;k++){
							var bingo = false;
							for(var j = 0;j < list.length;j++){
								if(list[j] == $scope.filterResult[k]){
									bingo = true;
									break;
								}
							}
							if(!bingo){
								$scope.filterResult[k] = "@";
							}
						}
						var temp = [];
						for(var k = 0;k <　$scope.filterResult.length;k++){
							if($scope.filterResult[k] != "@"){
								temp.push($scope.filterResult[k]);
							}
						}
						$scope.filterResult = temp;
					}

				}
				//conbine filter and search name function
				if(templist.length != 0){
					for(var i = 0;i < templist.length;i++){
						var bingo = false;
						for(var j = 0;j <$scope.filterResult.length;j++){
							if($scope.filterResult[j] == templist[i]){
								bingo = true;
								break;
							}
							if(!bingo){
								templist[i] = "@";
							}
						}
						$scope.filterResult = [];
						for(var k = 0;k < templist.length;k++){
							if(templist[k] != "@"){
								$scope.filterResult.push(templist[k]);
							}
						}
					}
				}
			}

			//private search method
			var findResult = function(type){
				var list = [];
				if(type == "team"){
					list = teams;
				}
				else if(type == "user"){
					list = users;
				}
				else{
					alert("unknown error occur");
				}//event list is not yet defined
				var m = Naruto.Sasuke.length;

				//retrive every tags in tagList
				for(var i = 0;i < list.length;i++){
					var n = list[i].length;

					//create a new 2D array,initialize with all 0
					var arr = []
					for (var row = 0; row < m+1; row++) {
						arr[row] = [];
						for(var column = 0; column < n+1; column++){
							arr[row].push(0);
						}
					}

					//record the maximum number of matched subsequence in two strings
					var max = 0;

					//start to fill the 2D array
					for(var x = 1;x <= m; x++){
						for(var y = 1;y <= n;y++){
							if(Naruto.Sasuke.charAt(x-1) == list[i].charAt(y-1)){
								arr[x][y] = arr[x-1][y-1] + 1;
								if(arr[x][y] > max){
									max = arr[x][y];
								}
							}
							else if(arr[x-1][y] > arr[x][y-1]){
								arr[x][y] = arr[x-1][y];
							}
							else{
								arr[x][y] = arr[x][y-1];
							}
						}
					}
					var match = arr[m][n]/Naruto.Sasuke.length
					if(match>= 0.6){
						$scope.searchResult.push(list[i]);
					}
				}

				// tag = $scope.searchResult;
			}

			var searchName = function(){
				if(currentEvent != Naruto.Luffy){
					currentEvent = Naruto.Luffy;
					$scope.loadData();

				}
				$scope.searchResult = [];
				if($scope.resultTag.length == 0){
					if(Naruto.Sakura == "Search Team"){
						findResult('team');

						$scope.filterResult = $scope.searchResult;
					}
					else{
						findResult('user');
						var idList = [];
						for(var i = 0;i < $scope.searchResult.length;i++){
							angular.forEach($scope.userList,function(value,key){
								//only get the user that belongs to current event
								if($scope.userList[key].name == $scope.searchResult[i]){
									idList.push(key);
								}
							});
						}
						$scope.filterResult = idList;
					}
				}
				else{
					if(Naruto.Sakura == "Search Team"){
						findResult('team');
						for(var i = 0;i < $scope.filterResult.length;i++){
							var bingo = false;
							for(var j = 0;j < $scope.searchResult.length;j++){
								if($scope.searchResult[j] == $scope.filterResult[i]){
									bingo = true;
								}
							}
							if(!bingo){
								$scope.filterResult[i] = "@";
							}
						}
							var temp = [];
							for(var k = 0;k <　$scope.filterResult.length;k++){
								if($scope.filterResult[k] != "@"){
									temp.push($scope.filterResult[k]);
								}
							}
							$scope.filterResult = temp;

					}else{

						findResult('user');

						for(var i = 0;i < $scope.filterResult.length;i++){
							var bingo = false;
							for(var j = 0;j < $scope.searchResult.length;j++){
								if($scope.searchResult[j] == $scope.userList[$scope.filterResult[i]].name){
									bingo = true;
									alert($scope.searchResult[j]);
									break;
								}
							}
							if(!bingo){
								$scope.filterResult[i] = "@";
							}
						}
						var temp = [];
						for(var k = 0;k <　$scope.filterResult.length;k++){
							if($scope.filterResult[k] != "@"){
								temp.push($scope.filterResult[k]);
							}
						}
						$scope.filterResult = temp;
					}
				}
			}
			Naruto.searchName = searchName;
		});

		//controll navigation bar
		app.controller("navbar",function($scope,Naruto,Search,$firebaseObject){
			// $scope.searchKey = "";
			// $scope.searchType = Naruto.Sakura;
			// Naruto.Sasuke = $scope.searchKey;
			var ref = firebase.database().ref("eventList");
			$scope.event_list = $firebaseObject(ref);
			$scope.Naruto = Naruto;

			// $scope.setThisSearchP = function(){
			// 	Naruto.setSearchP();
			// };
			// $scope.setThisSearchT = function(){
			// 	Naruto.setSearchT();
			// }
			// $scope.searchThisName = function(){
			// 	Naruto.searchName();
			// };
			$scope.start = function(){
				Search.search = false;

			};

		});

		/*
		function addTag(obj){
			var filterTags = document.getElementById("searchTag");
			var key = obj.innerHTML;
			tag[key] = false;
			filterTags.appendChild(obj);
			obj.removeAttribute("onclick")
			var span = document.createElement("span")
			var text = document.createTextNode(String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160) + String.fromCharCode(160));
			span.appendChild(text);
			obj.appendChild(span);

			var close = document.createElement("span");
			close.setAttribute("style","font-weight:bold");

			text = document.createTextNode(String.fromCharCode(215));
			close.appendChild(text);
			obj.appendChild(close);
			close.setAttribute("ng-click","reset()")
			close.addEventListener("mouseover", setRed);
			close.addEventListener("mouseleave", setBlack);
			close.addEventListener("click", deleteTag);

		}

		function deleteTag(){
			var currentTag = this.parentNode;

			document.getElementById("predefineTag").appendChild(currentTag);
			currentTag.removeChild(this);
			currentTag.removeChild(currentTag.lastChild);
			var tag = currentTag.innerHTML;
			tag[key] = true;
		}
		*/
<|endoftext|>