<commit_before>var express = require('express')
var bodyParser = require('body-parser')
var app = express()
var mongoOp = require('./model/mongo')
var router = express.Router()
var config = require('./config')
var twilio = require('twilio')
var sendSms = require('./sendsms')
var cookieParser = require('cookie-parser')
var logging = require('./logging')
var reminders = require('./reminders')
var listItems = require('./listitems')
var messagePreProcessor = require('./messagepreprocessor')
var stringProcessor = require('./stringprocessor')

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({"extended" : false}))

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

app.set('port', (process.env.PORT || 5000));

router.get("/",function(req,res){
  res.json({"error" : false,"message" : "available"});
});

router.route("/twilio")
.get(function(req,res){
  console.log('----Twilio From: ' + req.query['From'] + ' Message ' + req.query['Body'])
  var bodyText = req.query['Body'].toLowerCase()
  var fromPhoneNumber = req.query['From']
  var familyId = 0
  var timeZone = ''

  // Get Cached ListName
  var cachedListName
  if (req.cookies !== undefined && req.cookies.listName !== undefined) {
    cachedListName = req.cookies.listName
  }

  // Check FamilyId
  mongoOp.FamilyMembers.findOne({ 'phoneNumber': fromPhoneNumber }, function (err, familyMember) {
    if (err != null || familyMember == null) {
      sendSMSResponse(fromPhoneNumber, 0, bodyText, "Sorry, don't see you as a member of a family.", res)
    } else {
      familyId = familyMember.familyId
      logging.log(fromPhoneNumber, familyId, bodyText, 'request', '')

      timeZone = familyMember.timeZone
      if (familyMember.timeZone == null) timeZone = 'America/New_York'

      var fromUserName = familyMember.name

      // Preprocess Message Text
      bodyText = messagePreProcessor.preProcessMessage(bodyText, cachedListName, fromUserName)

      // MAIN LOGIC
      if (bodyText === "get lists" || bodyText === "get" || bodyText === "lists") {
        mongoOp.Lists.find({'familyId': familyId}, 'listKey', function(err, lists) {
          if(err){
            logging.logError(fromPhoneNumber, familyId, bodyText, err);
          } else {
            var concatText = "";

            lists.forEach(function(list){
              concatText = concatText.concat('\n#' + list.listKey);
            });
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, concatText, res);
          }
        });

      // Basic welcome message
      } else if (bodyText.startsWith("** welcome ")) {
        response = true;
        var welcomeSendUserId = bodyText.substr(11);
        console.log('*** Send welcome to ' + welcomeSendUserId);
        mongoOp.FamilyMembers.findOne({'userId': welcomeSendUserId}, 'phoneNumber', function(err, familyMember) {
          sendSms.sendSms(familyMember.phoneNumber,'Welcome to FLOCK!\nYou now have the power to crowdsource your family lists. \nLearn more - text the word “flock” to this number to see a list of available commands.\nHave fun!\n❤FLOCK', function(){});
          sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Welcome sent to ' + familyMember.phoneNumber, res);
        })

      // Get list items
      } else if (bodyText.startsWith('get #') || bodyText.startsWith('show #') || bodyText.startsWith('list #') || bodyText.startsWith('retrieve #') || bodyText.startsWith('display #')) {
        var listName = stringProcessor.removeFirstWord(bodyText).substr(1)
        console.log('*** get list!!!!')
        var list = {'listKey': listName, 'familyId': familyId}

        listItems.listItemsTextForList(list, function (err, text) {
          console.log('****TEST1:' + err + ':' + text)
          if (err) {
            logging.logError(fromPhoneNumber, familyId, bodyText, err)
          } else {
            cacheListName(list.listKey, res)
          }
          if (text === '') {
            console.log('****TEST5:')
          }
          if (text == null) {
            console.log('****TEST6:')
          }
          if (text) {
            var smsText
            console.log('****TEST2:' + text + 'X')
            if (text === '') {
              smsText = 'Currently no items in #' + list.listKey + '.'
            } else {
              smsText = '\n#' + list.listKey + ':' + text
            }
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, smsText, res)
          }
        })

        // mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
        //   if (list == null) {
        //     sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
        //   } else {
        //     mongoOp.ListItems.find({'listKey':listName, 'familyId': familyId}, function(err, listItems){
        //       if(err){
        //         logging.logError(fromPhoneNumber, familyId, bodyText, err);
        //       } else {
        //         var concatText = "";
        //         var itemNumber = 0;
        //         listItems.forEach(function(listItem){
        //           itemNumber++;
        //           concatText = concatText.concat('\n• ' + listItem.listItemName);
        //         });
        //         if (itemNumber == 0) {
        //           concatText = concatText.concat('No items in #' + listName + '.');
        //         }
        //         cacheListName(listName,res);
        //         sendSMSResponse(fromPhoneNumber, familyId, bodyText, '\n#'+ listName + ':' + concatText, res);
        //      }
        //     });
        //   }
        // });

      } else if (bodyText.startsWith('#')) {

        var listName = getFirstWord(bodyText).substr(1);
        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {
            var addVerbPhrase = '#' + listName + ' add ';
            var removeVerbPhrase = '#' + listName + ' remove ';

            //Add item
            if (bodyText.startsWith(addVerbPhrase)) {
              var listItemName = bodyText.substr(addVerbPhrase.length);

              var newItem = new mongoOp.ListItems({
                "listKey" : listName,
                "listItemName" : listItemName,
                "familyId" : familyId
              });
              newItem.save(function (err, data) {
                if (err) logging.logError(fromPhoneNumber, familyId, bodyText, err);
                else {
                  console.log('----saved ', data );
                  cacheListName(listName,res);
                  sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
                }
              });

            //Remove list item
            } else if (bodyText.startsWith(removeVerbPhrase)) {
              var listItemName = bodyText.substr(removeVerbPhrase.length);
              listItems.deleteListItemByName(familyId, listName, listItemName, function(err){
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, err ? err : config.successText, res);
              });
            }

          }
        });

      // Create list.
      } else if (bodyText.startsWith('create #')) {
        var newListName = bodyText.substr(8)
        mongoOp.Lists.findOne({'listKey': newListName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list != null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'List already exists!', res);
          } else {

            if (newListName.includes(' ')) {
              sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, but please don't include spaces in list names.", res);
              return;
            }

            var newList = new mongoOp.Lists({
              "listKey" : newListName,
              "listDescription" : "",
              "familyId" : familyId
            });
            newList.save(function (err, data) {
              if (err) logging.logError(fromPhoneNumber, familyId, bodyText, err);
              else {
                console.log('----saved ', data );
                cacheListName(newListName,res);
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
              }
            });
          }
        });

      // Clear list.
      } else if (bodyText.startsWith('clear #')) {
        var listName = bodyText.substr(7)
        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {

            mongoOp.ListItems.remove({"listKey" : list.listKey, 'familyId': familyId}, function(err, removeResult) {
              if (err) {
                logging.logError(fromPhoneNumber, familyId, bodyText, err);
                return;
              }
              console.log('----cleared ' + list.listKey + ' ' + removeResult.result.n);
              if (removeResult.result.n === 0) {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, "#" + list.listKey + " already empty.", res);
              } else {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
              }
            });

          }

        });

      // delete list.
      // TODO: can also use the word remove
      // TODO: Needs to delete items too
      } else if (bodyText.startsWith('delete #')) {
        var listName = bodyText.substr(8)

        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {

            mongoOp.Lists.remove({"listKey" : list.listKey, 'familyId': familyId}, function(err, removeResult) {
              if (err) {
                logging.logError(fromPhoneNumber, familyId, bodyText, err);
                return;
              }
              console.log('----deleted ' + list.listKey + ' ' + removeResult.result.n);
              sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
            });
          }
        });

      // send list
      } else if (bodyText.startsWith('send @')) {
        var messageCommandRemoved = bodyText.substr(6);
        var userName = getFirstWord(messageCommandRemoved);
        var listName = bodyText.substr(6 + userName.length + 2); //will strip out #
        console.log('send message to ' + userName + ' list: ' + listName);
        mongoOp.FamilyMembers.findOne({'name': userName, 'familyId': familyId}, 'phoneNumber', function(err, familyMember) {
          if (err) {
            logging.logError(fromPhoneNumber, familyId, bodyText, err);
            return;
          }

          //TODO: DUPLICATE CALL!!!!
          mongoOp.FamilyMembers.findOne({'phoneNumber': fromPhoneNumber, 'familyId': familyId}, 'name', function(err, fromFamilyMember) {
            if (err) {
              logging.logError(fromPhoneNumber, familyId, bodyText, err);
              return;
            }

            // Centralize!
            mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
              if (list == null) {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
              } else {
                mongoOp.ListItems.find({'listKey':listName, 'familyId': familyId}, function(err, listItems){
                  if(err){
                    logging.logError(fromPhoneNumber, familyId, bodyText, err);
                  } else {
                    console.log(familyMember);
                    var concatText = "";
                    listItems.forEach(function(listItem){
                      concatText = concatText.concat('\n• ' + listItem.listItemName);
                    });
                    cacheListName(listName,res);
                    sendSms.sendSms(familyMember.phoneNumber, "\n@" + fromFamilyMember.name + " sent you #"+ listName + ":" + concatText + "\nType 'get #" + listName + "'' to retrieve later.", function(){});
                    sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
                  }
                });
              }
            });
          });
        });

      // remind @nick some text date
      } else if (bodyText.startsWith('remind @')) {
        var remindText = bodyText.substr(8)
        console.log('%%%% timezone ' + timeZone)
        reminders.addReminder(remindText, familyId, timeZone, function (err, additionalMessage) {
          if (err == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, additionalMessage + ' ❤FLOCK', res)
          } else {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, err, res)
          }
        })

      // remind error/help
      } else if (bodyText.startsWith('remind')) {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, don't understand 😕.\nPlease use format 'remind @who when what'.\n'what' can be a simple message or an #list.\n'when' can be a day, date, time or any combo.", res)

      // help
      } else if (bodyText === 'flock') {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Welcome to ❤FLOCK\nThe Family Operating System\n\nUse the following commands:\n• get -OR- get lists\n• create #list\n• get #list\n• #list add item -OR - just 'add item' if already got list\n• #list remove item -OR- just 'remove item' if already got list\n• clear #list\n• delete #list", res);

      // catch all
      } else {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, don't understand.  Type 'flock' for more info.", res);
      }

    }
  });
});

app.use('/',router);

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

function sendSMSResponse(phoneNumber, familyId, inMessage, outMessage, response) {
  var twilioResponse = new twilio.TwimlResponse();
  twilioResponse.message(outMessage);
  response.send(twilioResponse.toString());
  logging.log(phoneNumber, familyId, inMessage, "response", outMessage);
};

function cacheListName(listName,response) {
  response.cookie('listName', listName, { maxAge: 1000 * 60 * 60 });
};

function getFirstWord(str) {
  if (str.indexOf(' ') === -1)
    return str;
  else
    return str.substr(0, str.indexOf(' '));
};
<commit_msg>scheduler<commit_after>var express = require('express')
var bodyParser = require('body-parser')
var app = express()
var mongoOp = require('./model/mongo')
var router = express.Router()
var config = require('./config')
var twilio = require('twilio')
var sendSms = require('./sendsms')
var cookieParser = require('cookie-parser')
var logging = require('./logging')
var reminders = require('./reminders')
var listItems = require('./listitems')
var messagePreProcessor = require('./messagepreprocessor')
var stringProcessor = require('./stringprocessor')

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({"extended" : false}))

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

app.set('port', (process.env.PORT || 5000));

router.get("/",function(req,res){
  res.json({"error" : false,"message" : "available"});
});

router.route("/twilio")
.get(function(req,res){
  console.log('----Twilio From: ' + req.query['From'] + ' Message ' + req.query['Body'])
  var bodyText = req.query['Body'].toLowerCase()
  var fromPhoneNumber = req.query['From']
  var familyId = 0
  var timeZone = ''

  // Get Cached ListName
  var cachedListName
  if (req.cookies !== undefined && req.cookies.listName !== undefined) {
    cachedListName = req.cookies.listName
  }

  // Check FamilyId
  mongoOp.FamilyMembers.findOne({ 'phoneNumber': fromPhoneNumber }, function (err, familyMember) {
    if (err != null || familyMember == null) {
      sendSMSResponse(fromPhoneNumber, 0, bodyText, "Sorry, don't see you as a member of a family.", res)
    } else {
      familyId = familyMember.familyId
      logging.log(fromPhoneNumber, familyId, bodyText, 'request', '')

      timeZone = familyMember.timeZone
      if (familyMember.timeZone == null) timeZone = 'America/New_York'

      var fromUserName = familyMember.name

      // Preprocess Message Text
      bodyText = messagePreProcessor.preProcessMessage(bodyText, cachedListName, fromUserName)

      // MAIN LOGIC
      if (bodyText === "get lists" || bodyText === "get" || bodyText === "lists") {
        mongoOp.Lists.find({'familyId': familyId}, 'listKey', function(err, lists) {
          if(err){
            logging.logError(fromPhoneNumber, familyId, bodyText, err);
          } else {
            var concatText = "";

            lists.forEach(function(list){
              concatText = concatText.concat('\n#' + list.listKey);
            });
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, concatText, res);
          }
        });

      // Basic welcome message
      } else if (bodyText.startsWith("** welcome ")) {
        response = true;
        var welcomeSendUserId = bodyText.substr(11);
        console.log('*** Send welcome to ' + welcomeSendUserId);
        mongoOp.FamilyMembers.findOne({'userId': welcomeSendUserId}, 'phoneNumber', function(err, familyMember) {
          sendSms.sendSms(familyMember.phoneNumber,'Welcome to FLOCK!\nYou now have the power to crowdsource your family lists. \nLearn more - text the word “flock” to this number to see a list of available commands.\nHave fun!\n❤FLOCK', function(){});
          sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Welcome sent to ' + familyMember.phoneNumber, res);
        })

      // Get list items
      } else if (bodyText.startsWith('get #') || bodyText.startsWith('show #') || bodyText.startsWith('list #') || bodyText.startsWith('retrieve #') || bodyText.startsWith('display #')) {
        var listName = stringProcessor.removeFirstWord(bodyText).substr(1)
        console.log('*** get list!!!!')
        var list = {'listKey': listName, 'familyId': familyId}

        listItems.listItemsTextForList(list, function (err, text) {
          console.log('****TEST1:' + err + ':' + text)
          if (err) {
            logging.logError(fromPhoneNumber, familyId, bodyText, err)
          } else {
            cacheListName(list.listKey, res)
          }
          if (text === '') {
            console.log('****TEST5:')
          }
          if (text == null) {
            console.log('****TEST6:')
          }
          if (text != null) {
            var smsText
            console.log('****TEST2:' + text + 'X')
            if (text === '') {
              smsText = 'Currently no items in #' + list.listKey + '.'
            } else {
              smsText = '\n#' + list.listKey + ':' + text
            }
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, smsText, res)
          }
        })

        // mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
        //   if (list == null) {
        //     sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
        //   } else {
        //     mongoOp.ListItems.find({'listKey':listName, 'familyId': familyId}, function(err, listItems){
        //       if(err){
        //         logging.logError(fromPhoneNumber, familyId, bodyText, err);
        //       } else {
        //         var concatText = "";
        //         var itemNumber = 0;
        //         listItems.forEach(function(listItem){
        //           itemNumber++;
        //           concatText = concatText.concat('\n• ' + listItem.listItemName);
        //         });
        //         if (itemNumber == 0) {
        //           concatText = concatText.concat('No items in #' + listName + '.');
        //         }
        //         cacheListName(listName,res);
        //         sendSMSResponse(fromPhoneNumber, familyId, bodyText, '\n#'+ listName + ':' + concatText, res);
        //      }
        //     });
        //   }
        // });

      } else if (bodyText.startsWith('#')) {

        var listName = getFirstWord(bodyText).substr(1);
        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {
            var addVerbPhrase = '#' + listName + ' add ';
            var removeVerbPhrase = '#' + listName + ' remove ';

            //Add item
            if (bodyText.startsWith(addVerbPhrase)) {
              var listItemName = bodyText.substr(addVerbPhrase.length);

              var newItem = new mongoOp.ListItems({
                "listKey" : listName,
                "listItemName" : listItemName,
                "familyId" : familyId
              });
              newItem.save(function (err, data) {
                if (err) logging.logError(fromPhoneNumber, familyId, bodyText, err);
                else {
                  console.log('----saved ', data );
                  cacheListName(listName,res);
                  sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
                }
              });

            //Remove list item
            } else if (bodyText.startsWith(removeVerbPhrase)) {
              var listItemName = bodyText.substr(removeVerbPhrase.length);
              listItems.deleteListItemByName(familyId, listName, listItemName, function(err){
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, err ? err : config.successText, res);
              });
            }

          }
        });

      // Create list.
      } else if (bodyText.startsWith('create #')) {
        var newListName = bodyText.substr(8)
        mongoOp.Lists.findOne({'listKey': newListName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list != null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'List already exists!', res);
          } else {

            if (newListName.includes(' ')) {
              sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, but please don't include spaces in list names.", res);
              return;
            }

            var newList = new mongoOp.Lists({
              "listKey" : newListName,
              "listDescription" : "",
              "familyId" : familyId
            });
            newList.save(function (err, data) {
              if (err) logging.logError(fromPhoneNumber, familyId, bodyText, err);
              else {
                console.log('----saved ', data );
                cacheListName(newListName,res);
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
              }
            });
          }
        });

      // Clear list.
      } else if (bodyText.startsWith('clear #')) {
        var listName = bodyText.substr(7)
        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {

          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {

            mongoOp.ListItems.remove({"listKey" : list.listKey, 'familyId': familyId}, function(err, removeResult) {
              if (err) {
                logging.logError(fromPhoneNumber, familyId, bodyText, err);
                return;
              }
              console.log('----cleared ' + list.listKey + ' ' + removeResult.result.n);
              if (removeResult.result.n === 0) {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, "#" + list.listKey + " already empty.", res);
              } else {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
              }
            });

          }

        });

      // delete list.
      // TODO: can also use the word remove
      // TODO: Needs to delete items too
      } else if (bodyText.startsWith('delete #')) {
        var listName = bodyText.substr(8)

        mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
          if (list == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
          } else {

            mongoOp.Lists.remove({"listKey" : list.listKey, 'familyId': familyId}, function(err, removeResult) {
              if (err) {
                logging.logError(fromPhoneNumber, familyId, bodyText, err);
                return;
              }
              console.log('----deleted ' + list.listKey + ' ' + removeResult.result.n);
              sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
            });
          }
        });

      // send list
      } else if (bodyText.startsWith('send @')) {
        var messageCommandRemoved = bodyText.substr(6);
        var userName = getFirstWord(messageCommandRemoved);
        var listName = bodyText.substr(6 + userName.length + 2); //will strip out #
        console.log('send message to ' + userName + ' list: ' + listName);
        mongoOp.FamilyMembers.findOne({'name': userName, 'familyId': familyId}, 'phoneNumber', function(err, familyMember) {
          if (err) {
            logging.logError(fromPhoneNumber, familyId, bodyText, err);
            return;
          }

          //TODO: DUPLICATE CALL!!!!
          mongoOp.FamilyMembers.findOne({'phoneNumber': fromPhoneNumber, 'familyId': familyId}, 'name', function(err, fromFamilyMember) {
            if (err) {
              logging.logError(fromPhoneNumber, familyId, bodyText, err);
              return;
            }

            // Centralize!
            mongoOp.Lists.findOne({'listKey': listName, 'familyId': familyId}, 'listKey', function(err, list) {
              if (list == null) {
                sendSMSResponse(fromPhoneNumber, familyId, bodyText, '#' + listName + ' does not exist.', res);
              } else {
                mongoOp.ListItems.find({'listKey':listName, 'familyId': familyId}, function(err, listItems){
                  if(err){
                    logging.logError(fromPhoneNumber, familyId, bodyText, err);
                  } else {
                    console.log(familyMember);
                    var concatText = "";
                    listItems.forEach(function(listItem){
                      concatText = concatText.concat('\n• ' + listItem.listItemName);
                    });
                    cacheListName(listName,res);
                    sendSms.sendSms(familyMember.phoneNumber, "\n@" + fromFamilyMember.name + " sent you #"+ listName + ":" + concatText + "\nType 'get #" + listName + "'' to retrieve later.", function(){});
                    sendSMSResponse(fromPhoneNumber, familyId, bodyText, 'Got it! ❤FLOCK', res);
                  }
                });
              }
            });
          });
        });

      // remind @nick some text date
      } else if (bodyText.startsWith('remind @')) {
        var remindText = bodyText.substr(8)
        console.log('%%%% timezone ' + timeZone)
        reminders.addReminder(remindText, familyId, timeZone, function (err, additionalMessage) {
          if (err == null) {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, additionalMessage + ' ❤FLOCK', res)
          } else {
            sendSMSResponse(fromPhoneNumber, familyId, bodyText, err, res)
          }
        })

      // remind error/help
      } else if (bodyText.startsWith('remind')) {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, don't understand 😕.\nPlease use format 'remind @who when what'.\n'what' can be a simple message or an #list.\n'when' can be a day, date, time or any combo.", res)

      // help
      } else if (bodyText === 'flock') {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Welcome to ❤FLOCK\nThe Family Operating System\n\nUse the following commands:\n• get -OR- get lists\n• create #list\n• get #list\n• #list add item -OR - just 'add item' if already got list\n• #list remove item -OR- just 'remove item' if already got list\n• clear #list\n• delete #list", res);

      // catch all
      } else {
        sendSMSResponse(fromPhoneNumber, familyId, bodyText, "Sorry, don't understand.  Type 'flock' for more info.", res);
      }

    }
  });
});

app.use('/',router);

app.listen(app.get('port'), function() {
  console.log('Node app is running on port', app.get('port'));
});

function sendSMSResponse(phoneNumber, familyId, inMessage, outMessage, response) {
  var twilioResponse = new twilio.TwimlResponse();
  twilioResponse.message(outMessage);
  response.send(twilioResponse.toString());
  logging.log(phoneNumber, familyId, inMessage, "response", outMessage);
};

function cacheListName(listName,response) {
  response.cookie('listName', listName, { maxAge: 1000 * 60 * 60 });
};

function getFirstWord(str) {
  if (str.indexOf(' ') === -1)
    return str;
  else
    return str.substr(0, str.indexOf(' '));
};
<|endoftext|>


<commit_before>// Load and store all data from selected form to Session Storage.
// All different types of input fields need to have unique names
// or else the script won't work.

function easyContactForm(formSelector, statusMessages){

	function log(str) {
		var logPrefix = "Simple contact form app:";
		if (str)
			console.log( logPrefix + ' ' + str );
		else
			console.log( logPrefix );
	}

	function getFormData(){
		var formData = formContainer.querySelectorAll("*");
		var data = {};

		for ( var i = 0, len = formData.length; i < len; i ++ ){
			var item  = formData[i];
			var name  = item.name;
			var value = item.value;
			if (name !== "" && name !== undefined)
				data[name] = value;
		}
		return data;
	}

	function addListenersToForm(){

		// Submit
		form.addEventListener('submit', function(event){
			event.preventDefault();
			if (this.checkValidity()) {
				ajaxRequest();
			}
		});

		// Reset - is this one needed?
		clearButton = form.querySelector('button[type="reset"]');
		if ( clearButton )
		{
			clearButton.addEventListener('click', function(event){
				event.preventDefault();
				if (confirm("Are you sure?")) {
					storageFunctions.reset(formContainer);
				}
			});
		}

		// Input
		form.addEventListener('input', function(event){
			storageFunctions.store();
		});
	}

	var storageFunctions = {
		// Functions to use with sessionStorage when data is entered to the form
		restore: function(){
			function restoreFormData(obj){
				var element = formContainer.querySelector('*[name="'+ obj.name +'"]');
				element.value = obj.value;
			}

			var loadedFormData = sessionStorage.getItem(formContainer.id);
			if (loadedFormData != "undefined" ){
				var loadedFormObject = JSON.parse(loadedFormData);
				log("Restored form data from sessionStorage");
				for (var key in loadedFormObject) {
					var value = loadedFormObject[key];
					restoreFormData({name: key, value: value});
				}
			}
			else log("Nothing to restore from sessionStorage");
		},
		store: function(json){
			var formData = getFormData();
			var jsonString = JSON.stringify(formData);
			var storedFormData = sessionStorage.setItem(formContainer.id, jsonString);
			log("Writing form data to sessionStorage");
		},
		reset: function(json){
			form.reset();
			sessionStorage.removeItem(formContainer.id);
			log("Form reset");
		},
	};

	function ajaxRequest() {
		var url = form.action;
		log("Preparing XMLHttpRequest");
		var xhttp    = new XMLHttpRequest();
		var formData = getFormData();
		var dataObj  = {
			data: formData,
			ajax: true
		};

		var dataObjString = JSON.stringify(dataObj);
		status.sending();
		xhttp.open("POST", url, true);
		xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
		xhttp.onreadystatechange = function() {
			if (xhttp.readyState == 4 && xhttp.status == 200) {
				var response = JSON.parse(xhttp.responseText);
				log("XMLHttpRequest: " + response.status);

				// Display status panel.
				status.done(response);
			}
			else
				// Display status panel with error message.
			status.error();
		};
		xhttp.send(dataObjString);
	}

	function newElement(obj){
		// obj = {content: "", type: "", class: [], attr: {key: attr}}

		var el = document.createElement(obj.element);
		if (obj.content) {
			var text = document.createTextNode(obj.content);
			el.appendChild(text);
		}
		if (obj.type) {
			el.type = obj.type;
		}
		if (obj.class) {
			for (var i = 0, len = obj.class.length; i < len; i ++ ) {
				var item = obj.class[i];
				el.classList.add(item);
			}
			if (obj.attr) {
				for (var key in obj.attr) {
					el.setAttribute(key, obj.attr[key]);
				}
			}
		}
		return el;
	}

	var status = {
		getContainer: function(){
			var container = statusEl.querySelector('.container');
			return container;
		},
		getPanel: function(){
			var panel = statusEl.querySelector('.panel');
			return panel;
		},
		newPanel: function(obj){
			var panelClass = 'panel-default';
			if (obj.type == 'danger') panelClass = 'panel-danger';
			if (obj.type == 'warning') panelClass = 'panel-warning';
			var panel        = newElement({element: 'div', class: ['panel', panelClass]});
			var panelHeading = newElement({element: 'div', class: ['panel-heading']});
			var panelTitle   = newElement({element: 'h3', class: ['panel-title'], content: obj.title});
			var panelBody    = newElement({element: 'div', class: ['panel-body']});
			panelBody.appendChild(obj.content);
			panelHeading.appendChild(panelTitle);
			panel.appendChild(panelHeading);
			panel.appendChild(panelBody);
			return panel;
		},
		newModal: function(obj){
			var modalClass = 'modal fade';
			if (obj.type == 'danger') modalClass = 'panel-danger';
			if (obj.type == 'warning') modalClass = 'panel-warning';
			var panel        = newElement({element: 'div', class: ['panel', modalClass]});
			var panelHeading = newElement({element: 'div', class: ['panel-heading']});
			var panelTitle   = newElement({element: 'h3', class: ['panel-title'], content: obj.title});
			var panelBody    = newElement({element: 'div', class: ['panel-body']});
			panelBody.appendChild(obj.content);
			panelHeading.appendChild(panelTitle);
			panel.appendChild(panelHeading);
			panel.appendChild(panelBody);
			return panel;
		},
		show: function(){
			// form.classList.add("blur");
			statusEl.classList.remove("hidden");
			statusEl.classList.add("in");
		},
		remove: function(){
			var panelParent = status.getPanel().parentNode;
			while (panelParent.firstChild) {
				panelParent.removeChild(panelParent.firstChild);
			}
			// status.getContainer().parentNode.classList.add("hidden");
			status.getContainer().parentNode.classList.remove("in");
			// form.classList.remove("blur");
		},
		sending: function(){

			var title   = statusMessages.sending.title;
			var message = statusMessages.sending.message;

			var container   = newElement({element: 'div'});
			var content     = newElement({element: 'p', content: message});
			var progress    = newElement({element: 'div', class: ['progress'] });
			var progressBar = newElement({element: 'div', class: ['progress-bar','progress-bar-striped','active'],attr: {"role": "progressbar","style": "width: 100%" }});
			progress.appendChild(progressBar);
			container.appendChild(content);
			container.appendChild(progress);

			var panel = status.newPanel({title: title, content: container});
			status.getContainer().appendChild(panel);
			status.show();
		},
		done: function(statusObj){

			var stat = statusObj.status;
			var message = statusMessages[stat].message;
			var title = statusMessages[stat].title;

			if (stat == 'error' || stat == 'warning')
			{
				var errLoc = statusObj.errorLocations;
				var error = "Error in response: " + errLoc.join(', ');
				log(error, "error");
			}
			var confirm = statusMessages.confirm;

			var btnDynClass = 'btn-success';
			if (stat == 'danger') btnDynClass = 'btn-danger';
			else if (stat == 'warning') btnDynClass = 'btn-warning';

			var container = newElement({element: 'div'});
			var content = newElement({element: 'p', content: message});
			var button = newElement({element: 'button', type: 'button', class: ['btn', btnDynClass, 'center-block'], content: confirm});

			button.addEventListener('click', function(event){
				event.preventDefault();
				status.remove();
				if (stat == 'success')
					storageFunctions.reset();
			});

			container.appendChild(content);
			container.appendChild(button);
			var panel = status.newPanel({title: title, content: container, type: stat});
			if (status.getPanel())
				status.getContainer().replaceChild(panel, status.getPanel());
			else
				status.getContainer().appendChild(panel);

			status.show();
		},
		error: function() {
			status.done({ status: 'danger' });
		},

	};

	log("Initializing form functions");
	var formContainer = document.querySelector(formSelector);
	var form = formContainer.querySelector('form');
	var statusEl = formContainer.querySelector('.status-message');

	// Check if form variables is set, else populate with default values.
	var defaultStatusMessages = {
		sending: {
			title: 'Sending',
			message: 'Processing your message.',
		},
		success: {
			title: 'Success!',
			message: 'Your message was sent!',
			status: 'success'
		},
		danger: {
			title: 'Error!',
			message: 'Your message could not be sent. Please try again later.',
			status: 'error'
		},
		warning: {
			title: 'Warning!',
			message: 'There were some errors when processing your message. Please try again later.',
			status: 'warning',
		},
		confirm: 'OK',
	};

	if (!statusMessages) statusMessages = defaultStatusMessages;
	addListenersToForm();
	storageFunctions.restore();
}
<commit_msg>added ajaxRequest variable + moved code<commit_after>// Load and store all data from selected form to Session Storage.
// All different types of input fields need to have unique names
// or else the script won't work.

function easyContactForm(formSelector, statusMessages){

	function log(str) {
		var logPrefix = "Simple contact form app:";
		if (str)
			console.log( logPrefix + ' ' + str );
		else
			console.log( logPrefix );
	}

	function getFormData(){
		var formData = formContainer.querySelectorAll("*");
		var data = {};

		for ( var i = 0, len = formData.length; i < len; i ++ ){
			var item  = formData[i];
			var name  = item.name;
			var value = item.value;
			if (name !== "" && name !== undefined)
				data[name] = value;
		}
		return data;
	}

	function initForm(){

		// Store status of form.ajaxRequest to prevent triggering of multiple requests
		form.ajaxRequest = false;

		// Restore form data
		storageFunctions.restore();

		//Add listeners

		// Submit
		form.addEventListener('submit', function(event){
			event.preventDefault();
			if (this.checkValidity()) {
				ajaxRequest();
			}
		});

		// Reset - is this one needed?
		clearButton = form.querySelector('button[type="reset"]');
		if ( clearButton )
		{
			clearButton.addEventListener('click', function(event){
				event.preventDefault();
				if (confirm("Are you sure?")) {
					storageFunctions.reset(formContainer);
				}
			});
		}

		// Input
		form.addEventListener('input', function(event){
			storageFunctions.store();
		});
	}

	var storageFunctions = {
		// Functions to use with sessionStorage when data is entered to the form
		restore: function(){
			function restoreFormData(obj){
				var element = formContainer.querySelector('*[name="'+ obj.name +'"]');
				element.value = obj.value;
			}

			var loadedFormData = sessionStorage.getItem(formContainer.id);
			if (loadedFormData != "undefined" ){
				var loadedFormObject = JSON.parse(loadedFormData);
				log("Restored form data from sessionStorage");
				for (var key in loadedFormObject) {
					var value = loadedFormObject[key];
					restoreFormData({name: key, value: value});
				}
			}
			else log("Nothing to restore from sessionStorage");
		},
		store: function(json){
			var formData = getFormData();
			var jsonString = JSON.stringify(formData);
			var storedFormData = sessionStorage.setItem(formContainer.id, jsonString);
			log("Writing form data to sessionStorage");
		},
		reset: function(json){
			form.reset();
			sessionStorage.removeItem(formContainer.id);
			log("Form reset");
		},
	};

	function ajaxRequest() {
		if (form.ajaxRequest === false)
		{
			form.ajaxRequest = true;
			var url = form.action;
			log("Preparing XMLHttpRequest");
			var xhttp    = new XMLHttpRequest();
			var formData = getFormData();
			var dataObj  = {
				data: formData,
				ajax: true
			};

			var dataObjString = JSON.stringify(dataObj);
			status.sending();
			xhttp.open("POST", url, true);
			xhttp.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
			xhttp.onreadystatechange = function() {
				if (xhttp.readyState == 4 && xhttp.status == 200) {
					var response = JSON.parse(xhttp.responseText);
					log("XMLHttpRequest: " + response.status);

					status.done(response);
				}
				else
					status.error();
				form.ajaxRequest = false;
			};
			xhttp.send(dataObjString);
		}
	}

	function newElement(obj){
		// obj = {content: "", type: "", class: [], attr: {key: attr}}

		var el = document.createElement(obj.element);
		if (obj.content) {
			var text = document.createTextNode(obj.content);
			el.appendChild(text);
		}
		if (obj.type) {
			el.type = obj.type;
		}
		if (obj.class) {
			for (var i = 0, len = obj.class.length; i < len; i ++ ) {
				var item = obj.class[i];
				el.classList.add(item);
			}
			if (obj.attr) {
				for (var key in obj.attr) {
					el.setAttribute(key, obj.attr[key]);
				}
			}
		}
		return el;
	}

	var status = {
		getContainer: function(){
			var container = statusEl.querySelector('.container');
			return container;
		},
		getPanel: function(){
			var panel = statusEl.querySelector('.panel');
			return panel;
		},
		newPanel: function(obj){
			var panelClass = 'panel-default';
			if (obj.type == 'danger') panelClass = 'panel-danger';
			if (obj.type == 'warning') panelClass = 'panel-warning';
			var panel        = newElement({element: 'div', class: ['panel', panelClass]});
			var panelHeading = newElement({element: 'div', class: ['panel-heading']});
			var panelTitle   = newElement({element: 'h3', class: ['panel-title'], content: obj.title});
			var panelBody    = newElement({element: 'div', class: ['panel-body']});
			panelBody.appendChild(obj.content);
			panelHeading.appendChild(panelTitle);
			panel.appendChild(panelHeading);
			panel.appendChild(panelBody);
			return panel;
		},
		show: function(){
			statusEl.classList.add("in");
		},
		remove: function(){
			var panelParent = status.getPanel().parentNode;
			while (panelParent.firstChild) {
				panelParent.removeChild(panelParent.firstChild);
			}
			status.getContainer().parentNode.classList.remove("in");
		},
		sending: function(){

			var title   = statusMessages.sending.title;
			var message = statusMessages.sending.message;

			var container   = newElement({element: 'div'});
			var content     = newElement({element: 'p', content: message});
			var progress    = newElement({element: 'div', class: ['progress'] });
			var progressBar = newElement({element: 'div', class: ['progress-bar','progress-bar-striped','active'],attr: {"role": "progressbar","style": "width: 100%" }});
			progress.appendChild(progressBar);
			container.appendChild(content);
			container.appendChild(progress);

			var panel = status.newPanel({title: title, content: container});
			status.getContainer().appendChild(panel);
			panel.focus();
			status.show();
		},
		done: function(statusObj){

			var stat = statusObj.status;
			var message = statusMessages[stat].message;
			var title = statusMessages[stat].title;

			if (stat == 'error' || stat == 'warning')
			{
				var errLoc = statusObj.errorLocations;
				var error = "Error in response: " + errLoc.join(', ');
				log(error, "error");
			}
			var confirm = statusMessages.confirm;

			var btnDynClass = 'btn-success';
			if (stat == 'danger') btnDynClass = 'btn-danger';
			else if (stat == 'warning') btnDynClass = 'btn-warning';

			var container = newElement({element: 'div'});
			var content = newElement({element: 'p', content: message});
			var button = newElement({element: 'button', type: 'button', class: ['btn', btnDynClass, 'center-block'], content: confirm});

			button.addEventListener('click', function(event){
				event.preventDefault();
				status.remove();
				if (stat == 'success')
					storageFunctions.reset();
			});

			container.appendChild(content);
			container.appendChild(button);
			var panel = status.newPanel({title: title, content: container, type: stat});
			if (status.getPanel())
				status.getContainer().replaceChild(panel, status.getPanel());
			else
				status.getContainer().appendChild(panel);
			button.focus();
			status.show();
		},
		error: function() {
			status.done({ status: 'danger' });
		},

	};

	log("Initializing form functions");
	var formContainer = document.querySelector(formSelector);
	var form = formContainer.querySelector('form');
	var statusEl = formContainer.querySelector('.status-message');

	// Check if form variables is set, else populate with default values.
	var defaultStatusMessages = {
		sending: {
			title: 'Sending',
			message: 'Processing your message.',
		},
		success: {
			title: 'Success!',
			message: 'Your message was sent!',
			status: 'success'
		},
		danger: {
			title: 'Error!',
			message: 'Your message could not be sent. Please try again later.',
			status: 'error'
		},
		warning: {
			title: 'Warning!',
			message: 'There were some errors when processing your message. Please try again later.',
			status: 'warning',
		},
		confirm: 'OK',
	};

	if (!statusMessages) statusMessages = defaultStatusMessages;
	initForm();
}
<|endoftext|>


<commit_before>//All working 23/2/15 - 3pm
var restify = require('restify'),
    request = require('request'),
    rand = require('csprng'),
    sha1 = require('sha1'),
    server = restify.createServer({
        name: "localr"
    })
    uuid = require('node-uuid'),
    getDetails = require("./getDetails.js"),
    checkin = require("./checkin.js"),
    register = require("./register.js"),
    del = require("./del.js");
    groups = require("./groups.js"),
    offer = require("./offers.js");
// Setting server dependancys
server.use(restify.CORS({
    origins: ['*'], // defaults to ['*']
    credentials: true, // defaults to false
    headers: ['authorization', 'content-type', 'accept', 'origin'],
    methods: ['GET', 'PUT', 'POST', 'HEAD', 'DELETE'] // sets expose-headers
}));
server.use(restify.bodyParser());
server.use(restify.queryParser());
server.use(restify.authorizationParser());
restify.CORS.ALLOW_HEADERS.push('authorization');

//Some error handling
server.on('NotFound', function (request, response, cb) {return next(new restify.NotFoundError ('Not found please try a different URL'))});              // When a client request is sent for a URL that does not exist, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 404 handler. It is expected that if you listen for this event, you respond to the client.
server.on('MethodNotAllowed', function (request, response, cb) {return next(new restify.MethodNotAllowedError('Method not allowed please consult documentation'))});      // When a client request is sent for a URL that does exist, but you have not registered a route for that HTTP verb, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 405 handler. It is expected that if you listen for this event, you respond to the client.
server.on('VersionNotAllowed', function (request, response, cb) {});     // When a client request is sent for a route that exists, but does not match the version(s) on those routes, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 400 handler. It is expected that if you listen for this event, you respond to the client.
server.on('UnsupportedMediaType', function (request, response, cb) {});  // When a client request is sent for a route that exist, but has a content-type mismatch, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 415 handler. It is expected that if you listen for this event, you respond to the client.
server.on('after', function (request, response, route, error) {});       // Emitted after a route has finished all the handlers you registered. You can use this to write audit logs, etc. The route parameter will be the Route object that ran.
server.on('uncaughtException', function (request, response, route, error) {});  // Emitted when some handler throws an uncaughtException somewhere in the chain. The default behavior is to just call res.send(error), and let the built-ins in restify handle transforming, but you can override to whatever you want here.

// Creating Server
server.listen(8080, function() {
    var users = "/users",
        business = "/business",
        offers = '/offers'
    console.log('Incoming request being handled.');
    // Get details for user
    server.get({path : users + "/get"}, function(req, res, next) {
        getDetails.getDetails(req, res, next, 'users');
    });
    // Get details for business
    server.get({path : business + "/get"}, function(req, res, next) {
        getDetails.getDetails(req, res, next, 'business');
    });
    //Register user
    server.put({path : users + "/register"}, function(req, res, next) {
        register.register(req, res, next, 'users');
    });
    // Register business.
    server.put({path : business + "/register"}, function(req, res, next){
        register.register(req, res, next, 'business');
    });
    // The way this works is by having there name in at the moment e.g DOMAIN/checkin?user=USERNAME&location=7817587295719 This will then add 10 points at the moment
    server.put({path : users + "/checkin"}, function(req, res, next) {
        checkin.checkin(req,res,next);
    });
    // Delete user
    server.del({path: users + "/delete"}, function(req, res, next){
        del.del(req, res, next, 'users');
    });
    // Delete business
    server.del({path: business + "/delete"}, function(req, res, next){
        del.del(req, res, next, 'business');
     });
    //user/creategroup?username=username&groupname=name&description=description&competition=freshers
    server.put({path: users + "/creategroup"}, function(req, res, next) {
        groups.creategroup(req,res,next);
    });
    //?username=username&groupname=test21
    server.get({path: users + "/getgroup"}, function(req, res, next) {
        groups.showgroup(req,res,next);
    });
    //?username=username&competition=freshers
    server.get({path: users + "/getgroups"}, function(req, res, next) {
        groups.showcompetitiongroup(req,res,next);
    });
    //?username=username&groupname=test21
     server.put({path: users + "/joingroup"}, function(req, res, next) {
        groups.joinGroup(req,res,next);
    });
    //Create Offer
    server.put({path : business + offers}, function(req, res, next) {
        offer.addOffer(req, res, next);
    });
    //Get all Offers
    server.get({path : business + offers}, function(req, res, next) {
        offer.getAllOffers(req, res, next);
    });
    //Get all Offers
    server.get({path : business + offers + "/:businessname"}, function(req, res, next) {
        offer.getBusinessOffers(req, res, next);
    });
});<commit_msg>Adding some server.on options<commit_after>//All working 23/2/15 - 3pm
var restify = require('restify'),
    request = require('request'),
    rand = require('csprng'),
    sha1 = require('sha1'),
    server = restify.createServer({
        name: "localr"
    })
    uuid = require('node-uuid'),
    getDetails = require("./getDetails.js"),
    checkin = require("./checkin.js"),
    register = require("./register.js"),
    del = require("./del.js");
    groups = require("./groups.js"),
    offer = require("./offers.js");
// Setting server dependancys
server.use(restify.CORS({
    origins: ['*'], // defaults to ['*']
    credentials: true, // defaults to false
    headers: ['authorization', 'content-type', 'accept', 'origin'],
    methods: ['GET', 'PUT', 'POST', 'HEAD', 'DELETE'] // sets expose-headers
}));
server.use(restify.bodyParser());
server.use(restify.queryParser());
server.use(restify.authorizationParser());
restify.CORS.ALLOW_HEADERS.push('authorization');

//Some error handling
server.on('NotFound', function (request, response, next) {return next(new restify.NotFoundError ('Not found please try a different URL'))});              // When a client request is sent for a URL that does not exist, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 404 handler. It is expected that if you listen for this event, you respond to the client.
server.on('MethodNotAllowed', function (request, response, next) {return next(new restify.MethodNotAllowedError('Method not allowed please consult documentation'))});      // When a client request is sent for a URL that does exist, but you have not registered a route for that HTTP verb, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 405 handler. It is expected that if you listen for this event, you respond to the client.
server.on('VersionNotAllowed', function (request, response, next) {});     // When a client request is sent for a route that exists, but does not match the version(s) on those routes, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 400 handler. It is expected that if you listen for this event, you respond to the client.
server.on('UnsupportedMediaType', function (request, response, next) {});  // When a client request is sent for a route that exist, but has a content-type mismatch, restify will emit this event. Note that restify checks for listeners on this event, and if there are none, responds with a default 415 handler. It is expected that if you listen for this event, you respond to the client.
server.on('after', function (request, response, route, error) {});       // Emitted after a route has finished all the handlers you registered. You can use this to write audit logs, etc. The route parameter will be the Route object that ran.
server.on('uncaughtException', function (request, response, route, error) {});  // Emitted when some handler throws an uncaughtException somewhere in the chain. The default behavior is to just call res.send(error), and let the built-ins in restify handle transforming, but you can override to whatever you want here.

// Creating Server
server.listen(8080, function() {
    var users = "/users",
        business = "/business",
        offers = '/offers'
    console.log('Incoming request being handled.');
    // Get details for user
    server.get({path : users + "/get"}, function(req, res, next) {
        getDetails.getDetails(req, res, next, 'users');
    });
    // Get details for business
    server.get({path : business + "/get"}, function(req, res, next) {
        getDetails.getDetails(req, res, next, 'business');
    });
    //Register user
    server.put({path : users + "/register"}, function(req, res, next) {
        register.register(req, res, next, 'users');
    });
    // Register business.
    server.put({path : business + "/register"}, function(req, res, next){
        register.register(req, res, next, 'business');
    });
    // The way this works is by having there name in at the moment e.g DOMAIN/checkin?user=USERNAME&location=7817587295719 This will then add 10 points at the moment
    server.put({path : users + "/checkin"}, function(req, res, next) {
        checkin.checkin(req,res,next);
    });
    // Delete user
    server.del({path: users + "/delete"}, function(req, res, next){
        del.del(req, res, next, 'users');
    });
    // Delete business
    server.del({path: business + "/delete"}, function(req, res, next){
        del.del(req, res, next, 'business');
     });
    //user/creategroup?username=username&groupname=name&description=description&competition=freshers
    server.put({path: users + "/creategroup"}, function(req, res, next) {
        groups.creategroup(req,res,next);
    });
    //?username=username&groupname=test21
    server.get({path: users + "/getgroup"}, function(req, res, next) {
        groups.showgroup(req,res,next);
    });
    //?username=username&competition=freshers
    server.get({path: users + "/getgroups"}, function(req, res, next) {
        groups.showcompetitiongroup(req,res,next);
    });
    //?username=username&groupname=test21
     server.put({path: users + "/joingroup"}, function(req, res, next) {
        groups.joinGroup(req,res,next);
    });
    //Create Offer
    server.put({path : business + offers}, function(req, res, next) {
        offer.addOffer(req, res, next);
    });
    //Get all Offers
    server.get({path : business + offers}, function(req, res, next) {
        offer.getAllOffers(req, res, next);
    });
    //Get all Offers
    server.get({path : business + offers + "/:businessname"}, function(req, res, next) {
        offer.getBusinessOffers(req, res, next);
    });
});<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var request = require('request');
var bodyParser = require('body-parser');
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

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

app.get('/', (req, res) => {
  request('http://pizzapi.herokuapp.com', (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    res.render('index');
  });
});

app.get('/pizzas', (req, res) => {
  request('http://pizzapi.herokuapp.com/pizzas', (err, result, body ) => {
    if(err) return res.send(err);
    console.log(JSON.parse(body));
    res.render('pizzas-get', {data: JSON.parse(body)});
  });
});

app.get('/orders/:id', (req, res) => {
  request(`http://pizzapi.herokuapp.com/orders/${req.params.id}`, (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    res.send(body)
  });
});

app.post('/orders', (req, res) => {
  console.log('-------');
  console.log(req.body);
  console.log('-------');
  request.post({url: 'http://pizzapi.herokuapp.com/orders', body: JSON.stringify({id: parseInt(req.body.id)})}, (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    console.log('*****');
    res.send(body)
  });
});

app.listen(3000, () => {
  console.log('Listening on port 3000...')
})
<commit_msg>before merge<commit_after>var express = require('express');
var app = express();
var request = require('request');
var bodyParser = require('body-parser');
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

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

app.get('/', (req, res) => {
  request('http://pizzapi.herokuapp.com', (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    res.render('index');
  });
});

app.get('/pizzas', (req, res) => {
  request('http://pizzapi.herokuapp.com/pizzas', (err, result, body ) => {
    if(err) return res.send(err);
    console.log(JSON.parse(body));
    res.render('pizzas-get', {data: JSON.parse(body)});
  });
});

app.get('/orders/:id', (req, res) => {
  request(`http://pizzapi.herokuapp.com/orders/${req.params.id}`, (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    res.send(body)
  });
});

app.post('/orders', (req, res) => {
  request.post({url: 'http://pizzapi.herokuapp.com/orders', body: JSON.stringify({id: parseInt(req.body.id)})}, (err, result, body ) => {
    if(err) return res.send(err);
    console.log(body);
    res.render('order-get', {order: body});
  });
});

app.listen(3000, () => {
  console.log('Listening on port 3000...')
})
<|endoftext|>


<commit_before>// server.js
require('pmx').init();
var http = require('http'),
    https = require('https');

var pmx = require('pmx');
// set up ======================================================================
// get all the tools we need
var express = require('express');
var app = express();

var cloudflare = require('cloudflare-express');
app.use(cloudflare.restore());

var port = process.env.PORT || 1235;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');

var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');

var MongoStore = require('connect-mongo')({ session: session });

// Multer setting ==============================================================
var multer = require('multer');
app.use(multer())

// Algolia-search configuration ================================================
var HttpsAgent = require('agentkeepalive').HttpsAgent;
var Algolia = require('algolia-search');
var keepaliveAgent = new HttpsAgent({
    maxSockets: 1,
    maxKeepAliveRequests: 0, // no limit on max requests per keepalive socket
    maxKeepAliveTime: 30000 // keepalive for 30 seconds
});
var client = new Algolia('HE2Q1PU3UW', '4f2265ba4170d6768a91dc75452343e8', keepaliveAgent);

// configuration ===============================================================
var configDB = require('./config/database.js');
mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users

// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({
    extended: true
}));

app.set('view engine', 'ejs'); // set up ejs for templating

// Session settings ============================================================
var hour = 3600000;
var day = hour * 24;
var week = day * 7;

app.use(session({
    resave: true,
    saveUninitialized: true,
    secret: 'madewithlovebycolorblindlabs',
    store: new MongoStore({
        url: configDB.url,
        autoReconnect: true
    }),
    cookie: {
        maxAge: day
    } // 1 week
}));

app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session

// Load our main route =========================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport

// seeding (development stage ONLY!!!) =========================================
//require('./app/seeding/job.js'); // loads sample job
require('./app/seeding/user.js'); // loads sample user

// PMX Monitoring setting ======================================================
app.use(pmx.expressErrorHandler());

// launch ======================================================================
app.listen(port);
console.log('JOBSY is running on port ' + port + ' on development mode...');
<commit_msg>pulling<commit_after>// server.js
require('pmx').init();
var http = require('http'),
    https = require('https');

var pmx = require('pmx');
// set up ======================================================================
// get all the tools we need
var express = require('express');
var app = express();

var cloudflare = require('cloudflare-express');
app.use(cloudflare.restore());

var port = process.env.PORT || 2000;
var mongoose = require('mongoose');
var passport = require('passport');
var flash = require('connect-flash');

var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');

var MongoStore = require('connect-mongo')({ session: session });

// Multer setting ==============================================================
var multer = require('multer');
app.use(multer())

// Algolia-search configuration ================================================
var HttpsAgent = require('agentkeepalive').HttpsAgent;
var Algolia = require('algolia-search');
var keepaliveAgent = new HttpsAgent({
    maxSockets: 1,
    maxKeepAliveRequests: 0, // no limit on max requests per keepalive socket
    maxKeepAliveTime: 30000 // keepalive for 30 seconds
});
var client = new Algolia('HE2Q1PU3UW', '4f2265ba4170d6768a91dc75452343e8', keepaliveAgent);

// configuration ===============================================================
var configDB = require('./config/database.js');
mongoose.connect(configDB.url); // connect to our database
require('./config/passport')(passport); // pass passport for configuration
app.use(express.static(__dirname + '/public')); // set the static files location /public/img will be /img for users

// set up our express application
app.use(morgan('dev')); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json()); // get information from html forms
app.use(bodyParser.urlencoded({
    extended: true
}));

app.set('view engine', 'ejs'); // set up ejs for templating

// Session settings ============================================================
var hour = 3600000;
var day = hour * 24;
var week = day * 7;

app.use(session({
    resave: true,
    saveUninitialized: true,
    secret: 'madewithlovebycolorblindlabs',
    store: new MongoStore({
        url: configDB.url,
        autoReconnect: true
    }),
    cookie: {
        maxAge: day
    } // 1 week
}));

app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session

// Load our main route =========================================================
require('./app/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport

// seeding (development stage ONLY!!!) =========================================
//require('./app/seeding/job.js'); // loads sample job
require('./app/seeding/user.js'); // loads sample user

// PMX Monitoring setting ======================================================
app.use(pmx.expressErrorHandler());

// launch ======================================================================
app.listen(port);
console.log('JOBSY is running on port ' + port + ' on development mode...');
<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

moduleForComponent('cloudinary-image', 'Integration | Component | cloudinary image', {
  integration: true,

  beforeEach() {
    Ember.$.cloudinary.config({ cloud_name: 'cloud-name', api_key: '12345', secure: true });
  }
});

test('it renders an image', function(assert) {
  this.render(hbs`{{cloudinary-image 'test'}}`);

  assert.equal(this.$('img').attr('src'), 'https://res.cloudinary.com/cloud-name/image/upload/test');
});

test('it renders an image with width and height options', function(assert) {
  this.render(hbs`{{cloudinary-image 'test' (hash width=100 height=100)}}`);

  assert.equal(this.$('img').attr('src'), 'https://res.cloudinary.com/cloud-name/image/upload/test');
  assert.equal(this.$('img').attr('width'), 100);
  assert.equal(this.$('img').attr('height'), 100);
});
<commit_msg>Adding one more test<commit_after>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';

moduleForComponent('cloudinary-image', 'Integration | Component | cloudinary image', {
  integration: true,

  beforeEach() {
    Ember.$.cloudinary.config({ cloud_name: 'cloud-name', api_key: '12345', secure: true });
  }
});

test('it renders an image', function(assert) {
  this.render(hbs`{{cloudinary-image 'test'}}`);

  assert.equal(this.$('img').attr('src'), 'https://res.cloudinary.com/cloud-name/image/upload/test');
});

test('it renders an image with width and height options', function(assert) {
  this.render(hbs`{{cloudinary-image 'test' (hash width=100 height=100)}}`);

  assert.equal(this.$('img').attr('src'), 'https://res.cloudinary.com/cloud-name/image/upload/test');
  assert.equal(this.$('img').attr('width'), 100);
  assert.equal(this.$('img').attr('height'), 100);
});

test('it renders an image with width and height options in url and attributes', function(assert) {
  this.render(hbs`{{cloudinary-image 'test' (hash width=100 height=100 crop='fill')}}`);

  assert.equal(this.$('img').attr('src'), 'https://res.cloudinary.com/cloud-name/image/upload/c_fill,h_100,w_100/test');
  assert.equal(this.$('img').attr('width'), 100);
  assert.equal(this.$('img').attr('height'), 100);
});
<|endoftext|>


<commit_before>import Tool from '../../core/tool.js';
export class Connector extends Tool {
  constructor(SL, config, Belt) {
    super(SL, config, Belt);
    this.eventHandlers = {};
    this.UI = {
      Targets: []
    };
    this.initialized = true;
    this.configure();
  }
  configure(config) {
    if (!this.initialized) {
      return;
    }
    config = super.configure(config);
    this.configureUI(config.ui);
    this.initEventHandlers();
    this.registerSnappers();
    return config;
  }
  configureUI(config={}) {
    this.config.ui = config;
    if (!config.color) {
      config.color = '#0066AA';
    }
    if (!config.target)  {
      config.target = {};
    }
    if (!config.target.default) {
      config.target.default = {};
    }
    if (!config.target.default.type) {
      config.target.default.type = paper.Shape.Ellipse;
    }
    if (config.target.default.radius == undefined) {
      config.target.default.radius = 10;
    }

    if (config.target.hitScale == undefined) {
      config.target.hitScale = { x: 1.0, y: 1.0 };
    }

    if (!config.target.style) {
      config.target.style = {};
    }
    if (config.target.style.strokeColor == undefined) {
      config.target.style.strokeColor = config.color;
    }
    if (config.target.style.strokeWidth == undefined) {
      config.target.style.strokeWidth = 2;
    }
    if (config.target.style.strokeJoin == undefined) {
      config.target.style.strokeJoin = 'round';
    }
    if (config.target.style.fillColor == undefined) {
      config.target.style.fillColor = '#FFFFFF';
    }
    if (config.target.style.opacity == undefined) {
      config.target.style.opacity = 0.9;
    }
    if (config.target.style.cornerRadius == undefined) {
      config.target.style.cornerRadius = 10;
    }
  }

  reset() {
    super.reset();
    this.resetEventHandlers();
    this.unregisterSnappers();
    this.resetUI();
  }

  initEventHandlers() {
    if (!this.initialized) {
      return;
    }
  }
  resetEventHandlers() {
    if (!this.initialized) {
      return;
    }
  }

  registerSnappers() {
    let Snap = this.SL.Utils.get('Snap');
    if (Snap) {
      if (!this.Snappers) {
        this.Snappers = {};
      }
    }
  }
  unregisterSnappers() {
    let Snap = this.SL.Utils.get('Snap');
    if (!Snap || !this.Snappers) {
      return;
    }
  }

  resetUI() {
    this.resetUITargets();
  }
  resetUITargets() {
    for (let targetUI of this.UI.Targets) {
      this.SL.Paper.destroyPaperItem(targetUI);
    }
    this.UI.Targets.length = 0;
  }

  showTargets() {
    this.resetUITargets();
    this.SL.Paper.Item.forEachOfClass('Content', (item, args) => {
      this.drawItemTargets(item);
    });
  }
  hideTargets() {
    this.resetUITargets();
  }

  drawTargetShape(target, targetPoint, sourceBounds) {
    let targetShape = this.config.ui.target.default.type;
    let targetConfig = {
      position: targetPoint,
      width: this.config.ui.target.default.radius*2.0,
      height: this.config.ui.target.default.radius*2.0
    };

    // handle custom widths and heights
    if (target.width > 0 || target.height > 0) {
      targetShape = paper.Shape.Rectangle;
      if (target.width > 0) {
        if (target.width <= 1.0 && sourceBounds) {
          targetConfig.width = sourceBounds.width * target.width;
        }
        else {
          targetConfig.width = target.width;
        }
      }
      if (target.height > 0) {
        if (target.height <= 1.0 && sourceBounds) {
          targetConfig.height = sourceBounds.height * target.height;
        }
        else {
          targetConfig.height = target.height;
        }
      }
    }

    // configure the target style
    let targetStyle = $.extend({}, this.config.ui.target.style, target.style);
    if (targetStyle.cornerRadius) {
      if (targetShape == paper.Shape.Rectangle) {
        targetStyle.radius = targetStyle.cornerRadius;
      }
      targetStyle.cornerRadius = undefined;
      delete targetStyle.cornerRadius;
    }

    // create the target point
    let targetUI = this.SL.Paper.generatePaperItem({Source: this, Class:'UI', Layer:this.SL.Paper.Layers['UI_FG']+5}, targetShape, targetConfig);
    this.SL.Paper.applyStyle(targetUI, targetStyle);

    // the cornerRadius style would mess up width and height, so fix it here
    if (targetShape == paper.Shape.Rectangle && targetStyle.radius) {
      if (targetConfig.width) {
        targetUI.bounds.width = targetConfig.width;
      }
      if (targetConfig.height) {
        targetUI.bounds.height = targetConfig.height;
      }
      // make sure it stays at position
      targetUI.bounds.center = targetPoint;
    }

    // local rotation offset for the target point
    if (target.angle) {
      targetUI.rotate(target.angle);
    }

    return targetUI;
  }

  drawRectangleItemTarget(item, target) {
    // calculate the actual target point
    return this.drawTargetShape(target, this.globalTargetPoint(target, item), item.bounds);
  }
  drawLineItemTarget(item, target) {
    if (!item || !item.segments || !item.segments.length) {
      return;
    }
    let Geo = this.SL.Utils.get('Geo');

    // normalize start and end positions on line
    let start = target.start;
    let end = target.end;
    if ((start == null || end == null) && target.length) {
      if (end == null && start != null) {
        end = start + target.length;
      }
      else if (start == null && end != null) {
        start = end - target.length;
      }
      else if (target.position) {
        let halfLength = target.length/2.0;
        start = target.position - halfLength;
        end = target.position + halfLength;
      }
    }

    // calculate distance from line
    let distance = target.distance;
    if (distance > 0) {
      distance += item.strokeWidth / 2.0;
    }
    else if (distance < 0) {
      distance -= item.strokeWidth / 2.0;
    }

    if (start != null && end != null && start != end) {
      if (end < start) {
        let temp = start;
        start = end;
        end = temp;
      }

      // determine the segments the target covers
      let startAt = Geo.Normalize.pointOnLine(item, start, true);
      let endAt = Geo.Normalize.pointOnLine(item, end, true);

      // TODO: draw the target as a thick line with a styled outline
      let targetShape = paper.Path.Line;
      let targetStyle = {
        strokeColor: '#00AA66',
        strokeWidth: 1
      };
      let targetUI;

      // determine start and end points of the target
      let p1 = this.globalTargetPoint({
        position: start,
        distance: target.distance
      }, item);
      let p2 = this.globalTargetPoint({
        position: end,
        distance: target.distance
      }, item);
      if (startAt.segment == endAt.segment) {
        targetUI = this.SL.Paper.generatePaperItem({Source: this, Class:'UI', Layer:this.SL.Paper.Layers['UI_FG']+5}, targetShape, p1, p2);
      }
      else {
        // trace each segment between start and end points
        let p3;
        for (let i=startAt.segment+1; i < item.segments.length && i <= endAt.segment+1; i++) {
          if (i == endAt.segment+1 || i == item.segments.length-1) {
            p3 = p2;
          }
          else {
            p3 = item.segments[i].point.clone();
            if (distance) {
              // calculate the angle between segments
              let angle1 = p3.subtract(item.segments[i-1].point).angle;
              let angle2 = item.segments[i+1].point.subtract(p3).angle;
              if (angle1 < 0) {
                angle1 += 360.0;
              }
              if (angle2 < 0) {
                angle2 += 360.0;
              }
              let angleDiff = (angle2-angle1) % 360.0;
              if (angleDiff > 180.0) {
                angleDiff -= 360.0;
              }
              else if (angleDiff < -180.0) {
                angleDiff += 360.0;
              }

              // move the point out by distance and perpendicular to half the angle
              let vector = new paper.Point();
              vector.length = distance;
              vector.angle = angle1 + angleDiff/2.0;
              if (distance < 0) {
                vector.angle -= 90.0;
              }
              else {
                vector.angle += 90.0;
              }
              p3 = p3.add(vector);
            }
          }
          // create or append to the target
          if (!targetUI) {
            // two points are needed to create the path item
            targetUI = this.SL.Paper.generatePaperItem({Source: this, Class:'UI', Layer:this.SL.Paper.Layers['UI_FG']+5}, targetShape, p1, p3);
          }
          else {
            targetUI.add(p3);
          }
        }
      }

      if (targetUI) {
        this.SL.Paper.applyStyle(targetUI, targetStyle);
        targetUI.data.item = item;
        targetUI.data.target = target;
        this.UI.Targets.push(targetUI);
      }

      // TODO: remove the target point markers
      let targetUI1 = this.drawTargetShape({style: target.style}, p1);
      // link it to the item and track it
      targetUI1.data.item = item;
      targetUI1.data.target = target;
      this.UI.Targets.push(targetUI1);

      targetUI1 = this.drawTargetShape({style: $.extend({}, target.style, {radius: 5})}, p2);
      targetUI1.data.item = item;
      targetUI1.data.target = target;
      this.UI.Targets.push(targetUI1);
    }
    else  {
      let position;
      if (target.position != null) {
        position = target.position;
      }
      else if (start != null) {
        position = start;
      }
      else if (end != null) {
        position = end;
      }

      if (position) {
        let point = this.globalTargetPoint({
          position: start,
          distance: target.distance
        }, item);
        let targetUI = this.drawTargetShape({style: target.style}, point);
        // link it to the item and track it
        targetUI.data.item = item;
        targetUI.data.target = target;
        this.UI.Targets.push(targetUI);
      }
    }
  }
  drawItemTarget(item, target) {
    // temporarily straighten item for calculations
    let rotation = item.rotation;
    let rotationPoint = item.bounds.center;
    if (rotation) {
      item.rotate(-rotation, rotationPoint);
    }

    // draw the target depending on which type of item it is
    let targetUI;
    if (item.data && item.data.Type == 'Line') {
      targetUI = this.drawLineItemTarget(item, target);
    }
    else {
      targetUI = this.drawRectangleItemTarget(item, target);
    }

    // rotate item back
    if (rotation) {
      item.rotate(rotation, rotationPoint);
    }

    if (targetUI) {
      // rotate the target point with the item
      if (item.rotation) {
        targetUI.rotate(item.rotation, item.bounds.center);
      }

      // link it to the item and track it
      targetUI.data.item = item;
      targetUI.data.target = target;
      this.UI.Targets.push(targetUI);
    }
  }
  globalTargetPoint(target, item, offset) {
    let Geo = this.SL.Utils.get('Geo');
    if (item && target && Geo) {
      let rotation = item.rotation;
      let rotationPoint = item.bounds.center;
      if (rotation) {
        item.rotate(-rotation, rotationPoint);
      }
      let point, pivotPoint;
      if (item.data && item.data.Type == 'Line') {
        let position = target.position || 0.5;
        let distance = target.distance || 0;
        if (offset) {
          if (offset.x) {
            position += offset.x;
          }
          if (offset.y) {
            distance += offset.y;
          }
        }
        let pointOnLine = Geo.Normalize.pointOnLine(item, position, true);
        point = pointOnLine.point;
        if (distance) {
          // move the point out perpendicularly by distance
          pointOnLine.vector.length = distance;
          pointOnLine.vector.angle += 90;
          point = point.add(pointOnLine.vector);
        }
      }
      else {
        point = Geo.Normalize.pointFromRectangle(target.point, item.bounds);
        pivotPoint = point.clone();
        if (offset && (offset.x || offset.y)) {
          let targetRect = {
            width: this.config.ui.target.default.radius*2.0,
            height: this.config.ui.target.default.radius*2.0
          };
          if (target.width > 0.0) {
            if (target.width <= 1.0) {
              targetRect.width = target.width * item.bounds.width;
            }
            else {
              targetRect.width = target.width;
            }
          }
          if (target.height > 0.0) {
            if (target.height <= 1.0) {
              targetRect.height = target.height * item.bounds.height;
            }
            else {
              targetRect.height = target.height;
            }
          }
          if (offset.x) {
            point.x += (targetRect.width * offset.x * 0.5);
          }
          if (offset.y) {
            point.y += (targetRect.height * offset.y * 0.5);
          }
        }
      }
      if (target.angle) {
        point.set(point.rotate(target.angle, pivotPoint));
      }
      if (rotation) {
        item.rotate(rotation, rotationPoint);
        point.set(point.rotate(rotation, rotationPoint));
      }
      return point;
    }
  }
  isTargetHit(target, point, hitScaling=true) {
    let checkTarget = target.clone({insert:false});
    if (hitScaling) {
      let hitScale = this.config.ui.target.hitScale;
      if (checkTarget.data && checkTarget.data.hitScale) {
        hitScale = checkTarget.data.hitScale;
      }
      checkTarget.scale(hitScale.x, hitScale.y);
    }
    return checkTarget.hitTest(point);
  }
}
<commit_msg>Draw the line target as a stylized path<commit_after>import Tool from '../../core/tool.js';
export class Connector extends Tool {
  constructor(SL, config, Belt) {
    super(SL, config, Belt);
    this.eventHandlers = {};
    this.UI = {
      Targets: []
    };
    this.initialized = true;
    this.configure();
  }
  configure(config) {
    if (!this.initialized) {
      return;
    }
    config = super.configure(config);
    this.configureUI(config.ui);
    this.initEventHandlers();
    this.registerSnappers();
    return config;
  }
  configureUI(config={}) {
    this.config.ui = config;
    if (!config.color) {
      config.color = '#0066AA';
    }
    if (!config.target)  {
      config.target = {};
    }
    if (!config.target.default) {
      config.target.default = {};
    }
    if (!config.target.default.type) {
      config.target.default.type = paper.Shape.Ellipse;
    }
    if (config.target.default.radius == undefined) {
      config.target.default.radius = 10;
    }

    if (config.target.hitScale == undefined) {
      config.target.hitScale = { x: 1.0, y: 1.0 };
    }

    if (!config.target.style) {
      config.target.style = {};
    }
    if (config.target.style.strokeColor == undefined) {
      config.target.style.strokeColor = config.color;
    }
    if (config.target.style.strokeWidth == undefined) {
      config.target.style.strokeWidth = 2;
    }
    if (config.target.style.strokeJoin == undefined) {
      config.target.style.strokeJoin = 'round';
    }
    if (config.target.style.fillColor == undefined) {
      config.target.style.fillColor = '#FFFFFF';
    }
    if (config.target.style.opacity == undefined) {
      config.target.style.opacity = 0.9;
    }
    if (config.target.style.cornerRadius == undefined) {
      config.target.style.cornerRadius = 10;
    }
  }

  reset() {
    super.reset();
    this.resetEventHandlers();
    this.unregisterSnappers();
    this.resetUI();
  }

  initEventHandlers() {
    if (!this.initialized) {
      return;
    }
  }
  resetEventHandlers() {
    if (!this.initialized) {
      return;
    }
  }

  registerSnappers() {
    let Snap = this.SL.Utils.get('Snap');
    if (Snap) {
      if (!this.Snappers) {
        this.Snappers = {};
      }
    }
  }
  unregisterSnappers() {
    let Snap = this.SL.Utils.get('Snap');
    if (!Snap || !this.Snappers) {
      return;
    }
  }

  resetUI() {
    this.resetUITargets();
  }
  resetUITargets() {
    for (let targetUI of this.UI.Targets) {
      this.SL.Paper.destroyPaperItem(targetUI);
    }
    this.UI.Targets.length = 0;
  }

  showTargets() {
    this.resetUITargets();
    this.SL.Paper.Item.forEachOfClass('Content', (item, args) => {
      this.drawItemTargets(item);
    });
  }
  hideTargets() {
    this.resetUITargets();
  }

  drawTargetShape(target, targetPoint, sourceBounds) {
    let targetShape = this.config.ui.target.default.type;
    let targetConfig = {
      position: targetPoint,
      width: this.config.ui.target.default.radius*2.0,
      height: this.config.ui.target.default.radius*2.0
    };

    // handle custom widths and heights
    if (target.width > 0 || target.height > 0) {
      targetShape = paper.Shape.Rectangle;
      if (target.width > 0) {
        if (target.width <= 1.0 && sourceBounds) {
          targetConfig.width = sourceBounds.width * target.width;
        }
        else {
          targetConfig.width = target.width;
        }
      }
      if (target.height > 0) {
        if (target.height <= 1.0 && sourceBounds) {
          targetConfig.height = sourceBounds.height * target.height;
        }
        else {
          targetConfig.height = target.height;
        }
      }
    }

    // configure the target style
    let targetStyle = $.extend({}, this.config.ui.target.style, target.style);
    if (targetStyle.cornerRadius) {
      if (targetShape == paper.Shape.Rectangle) {
        targetStyle.radius = targetStyle.cornerRadius;
      }
      targetStyle.cornerRadius = undefined;
      delete targetStyle.cornerRadius;
    }

    // create the target point
    let targetUI = this.SL.Paper.generatePaperItem({Source: this, Class:'UI', Layer:this.SL.Paper.Layers['UI_FG']+5}, targetShape, targetConfig);
    this.SL.Paper.applyStyle(targetUI, targetStyle);

    // the cornerRadius style would mess up width and height, so fix it here
    if (targetShape == paper.Shape.Rectangle && targetStyle.radius) {
      if (targetConfig.width) {
        targetUI.bounds.width = targetConfig.width;
      }
      if (targetConfig.height) {
        targetUI.bounds.height = targetConfig.height;
      }
      // make sure it stays at position
      targetUI.bounds.center = targetPoint;
    }

    // local rotation offset for the target point
    if (target.angle) {
      targetUI.rotate(target.angle);
    }

    return targetUI;
  }

  drawRectangleItemTarget(item, target) {
    // calculate the actual target point
    return this.drawTargetShape(target, this.globalTargetPoint(target, item), item.bounds);
  }
  drawLineItemTarget(item, target) {
    if (!item || !item.segments || !item.segments.length) {
      return;
    }
    let Geo = this.SL.Utils.get('Geo');

    // normalize start and end positions on line
    let start = target.start;
    let end = target.end;
    if ((start == null || end == null) && target.length) {
      if (end == null && start != null) {
        end = start + target.length;
      }
      else if (start == null && end != null) {
        start = end - target.length;
      }
      else if (target.position) {
        let halfLength = target.length/2.0;
        start = target.position - halfLength;
        end = target.position + halfLength;
      }
    }

    // calculate distance from line
    let distance = target.distance;
    let flipSide = (distance < 0);
    let normalAngle = (flipSide ? -90.0 : 90.0);
    distance = Math.abs(distance);
    if (distance) {
      distance += (item.strokeWidth / 2.0);
    }

    if (start != null && end != null && start != end) {
      if (end < start) {
        let temp = start;
        start = end;
        end = temp;
      }

      // determine the segments the target covers
      let startAt = Geo.Normalize.pointOnLine(item, start, true);
      let endAt = Geo.Normalize.pointOnLine(item, end, true);
      let targetUI;
      let targetRadius = (target.width/2.0) || this.config.ui.target.default.radius;

      // build two sets of points
      let pointsA = []; // start -> end @ distance + targetRadius
      let pointsB = []; // end -> start @ distance - targetRadius

      let point, vector;

      // add the start point
      vector = startAt.vector.clone();
      vector.angle += normalAngle;
      vector.length = distance + targetRadius;
      pointsA.push(startAt.point.add(vector));
      vector.length = distance - targetRadius;
      pointsB.unshift(startAt.point.add(vector));

      // add all points in between start and end segments
      for (let i=startAt.segment+1; i < item.segments.length-1 && i <= endAt.segment; i++) {
        point = item.segments[i].point.clone();

        // calculate the angle between segments
        let angle1 = point.subtract(item.segments[i-1].point).angle;
        let angle2 = item.segments[i+1].point.subtract(point).angle;
        if (angle1 < 0) {
          angle1 += 360.0;
        }
        if (angle2 < 0) {
          angle2 += 360.0;
        }
        let angleDiff = (angle2-angle1) % 360.0;
        if (angleDiff > 180.0) {
          angleDiff -= 360.0;
        }
        else if (angleDiff < -180.0) {
          angleDiff += 360.0;
        }

        // move the point out by distance and perpendicular to half the angle
        vector = new paper.Point();
        vector.angle = angle1 + (angleDiff/2.0) + normalAngle;
        vector.length = distance + targetRadius;
        pointsA.push(point.add(vector));
        vector.length = distance - targetRadius;
        pointsB.unshift(point.add(vector));
      }

      // add the end point
      vector = endAt.vector.clone();
      vector.angle += normalAngle;
      vector.length = distance + targetRadius;
      pointsA.push(endAt.point.add(vector));
      vector.length = distance - targetRadius;
      pointsB.unshift(endAt.point.add(vector));

      targetUI = this.SL.Paper.generatePaperItem({Source: this, Class:'UI', Layer:this.SL.Paper.Layers['UI_FG']+5}, paper.Path, pointsA.concat(pointsB));
      targetUI.closed = true;

      if (targetUI) {
        this.SL.Paper.applyStyle(targetUI, $.extend({}, this.config.ui.target.style, target.style));
        targetUI.data.item = item;
        targetUI.data.target = target;
        this.UI.Targets.push(targetUI);
      }
    }
    else  {
      // a single connection point
      let position;
      if (target.position != null) {
        position = target.position;
      }
      else if (start != null) {
        position = start;
      }
      else if (end != null) {
        position = end;
      }

      if (position) {
        let point = this.globalTargetPoint({
          position: start,
          distance: target.distance
        }, item);
        let targetUI = this.drawTargetShape({style: target.style}, point);
        // link it to the item and track it
        targetUI.data.item = item;
        targetUI.data.target = target;
        this.UI.Targets.push(targetUI);
      }
    }
  }
  drawItemTarget(item, target) {
    // temporarily straighten item for calculations
    let rotation = item.rotation;
    let rotationPoint = item.bounds.center;
    if (rotation) {
      item.rotate(-rotation, rotationPoint);
    }

    // draw the target depending on which type of item it is
    let targetUI;
    if (item.data && item.data.Type == 'Line') {
      targetUI = this.drawLineItemTarget(item, target);
    }
    else {
      targetUI = this.drawRectangleItemTarget(item, target);
    }

    // rotate item back
    if (rotation) {
      item.rotate(rotation, rotationPoint);
    }

    if (targetUI) {
      // rotate the target point with the item
      if (item.rotation) {
        targetUI.rotate(item.rotation, item.bounds.center);
      }

      // link it to the item and track it
      targetUI.data.item = item;
      targetUI.data.target = target;
      this.UI.Targets.push(targetUI);
    }
  }
  globalTargetPoint(target, item, offset) {
    let Geo = this.SL.Utils.get('Geo');
    if (item && target && Geo) {
      let rotation = item.rotation;
      let rotationPoint = item.bounds.center;
      if (rotation) {
        item.rotate(-rotation, rotationPoint);
      }
      let point, pivotPoint;
      if (item.data && item.data.Type == 'Line') {
        let position = target.position || 0.5;
        let distance = target.distance || 0;
        if (offset) {
          if (offset.x) {
            position += offset.x;
          }
          if (offset.y) {
            distance += offset.y;
          }
        }
        let pointOnLine = Geo.Normalize.pointOnLine(item, position, true);
        point = pointOnLine.point;
        if (distance) {
          // move the point out perpendicularly by distance
          pointOnLine.vector.length = distance;
          pointOnLine.vector.angle += 90;
          point = point.add(pointOnLine.vector);
        }
      }
      else {
        point = Geo.Normalize.pointFromRectangle(target.point, item.bounds);
        pivotPoint = point.clone();
        if (offset && (offset.x || offset.y)) {
          let targetRect = {
            width: this.config.ui.target.default.radius*2.0,
            height: this.config.ui.target.default.radius*2.0
          };
          if (target.width > 0.0) {
            if (target.width <= 1.0) {
              targetRect.width = target.width * item.bounds.width;
            }
            else {
              targetRect.width = target.width;
            }
          }
          if (target.height > 0.0) {
            if (target.height <= 1.0) {
              targetRect.height = target.height * item.bounds.height;
            }
            else {
              targetRect.height = target.height;
            }
          }
          if (offset.x) {
            point.x += (targetRect.width * offset.x * 0.5);
          }
          if (offset.y) {
            point.y += (targetRect.height * offset.y * 0.5);
          }
        }
      }
      if (target.angle) {
        point.set(point.rotate(target.angle, pivotPoint));
      }
      if (rotation) {
        item.rotate(rotation, rotationPoint);
        point.set(point.rotate(rotation, rotationPoint));
      }
      return point;
    }
  }
  isTargetHit(target, point, hitScaling=true) {
    let checkTarget = target.clone({insert:false});
    if (hitScaling) {
      let hitScale = this.config.ui.target.hitScale;
      if (checkTarget.data && checkTarget.data.hitScale) {
        hitScale = checkTarget.data.hitScale;
      }
      checkTarget.scale(hitScale.x, hitScale.y);
    }
    return checkTarget.hitTest(point);
  }
}
<|endoftext|>