<commit_before>// import botkit
var Botkit = require('botkit');

// get authorization tokens from environment file
require('./env.js');

if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
  console.log('Error: Specify clientId clientSecret and port in environment');
  process.exit(1);
}

if (!process.env.wit) {
  console.log('Error: Specify wit server token in environment')
}

// top level controller object used to spawn bots and respond to events
var controller = Botkit.slackbot({
  json_file_store: './db_advisor_bot/',
}).configureSlackApp(
  {
    clientId: process.env.clientId,
    clientSecret: process.env.clientSecret,
    scopes: ['bot'],
  }
);

// import witai middleware
var wit = require('botkit-middleware-witai')({
  token: process.env.wit
});

// setup webserver and webhook endpoints
controller.setupWebserver(process.env.port,function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);

  webserver.get('/', function(req,res) {
    res.sendFile('index.html', {root: __dirname});
  });

// create Oauth endpoints
controller.createOauthEndpoints(controller.webserver,function(err,req,res) {
  if (err) {
    res.status(500).send('ERROR: ' + err);
  } else {
    res.send('Success!');
  }
 });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};
function trackBot(bot) {
  _bots[bot.config.token] = bot;
}

// bot creation event
controller.on('create_bot',function(bot,config) {

  if (_bots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM(function(err) {

      if (!err) {
        trackBot(bot);
      }

      bot.startPrivateConversation({user: config.createdBy},function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          convo.say('I am a bot that has just joined your team');
          convo.say('You must now /invite me to a channel so that I can be of use!');
        }
      });
    });
  }
});

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
  console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
  console.log('** The RTM api just closed');
  // you may want to attempt to re-open
});

// enable witai mmiddleware
controller.middleware.receive.use(wit.receive);

// use middleware to process message and infer intent
controller.hears(['hello'], 'direct_message', wit.hears, function(bot, message) {
  bot.reply(message,"Hello! How can I help?");
});

// end conversation
// controller.hears(['goodbye'],'direct_message', wit.hears, function(bot,message) {
//   bot.reply(message,'Goodbye');
//   bot.rtm.close();
// });

controller.hears(['goodbye'],'direct_message', function(bot,message) {
  bot.reply(message,'Goodbye');
  bot.rtm.close();
});

// begin a conversation
controller.hears(['question'], 'direct_message', wit.hears, function(bot, message) {
  bot.reply(message,'Happy to help. What is your question?');
});

// entry point for question 1
controller.hears(['rules'], 'direct_message', wit.hears, function(bot, message){
  bot.reply(message, "Good question!");
  bot.startConversation(message, askContext);
});

// entry point for question 2
controller.hears(['transfer student'], 'direct_message', wit.hears, function(bot, message){
  bot.reply(message, "I am glad you asked!");
  bot.reply(message, "1. Make sure to attend one of the campus Transfer Orientation sessions, as described in the material sent to you by the university upon your acceptance.");
  bot.reply(message, "2. If you still have queries, make an appointment to resolve any unresolved issues with the appropriate advisor, as listed on the CS department advising web site. [http://www.sjsu.edu/cs/practicalities/cs-advising/]");
  bot.reply(message, "3. Determine, preferably in consultation with an advisor, whether you are ready to start upper-division coursework in Computer Science.");
  bot.startConversation(message, askQ4);
});

askQ4 = function(response, convo){
  convo.ask("4. Have you successfully completed equivalents of CS 46A and CS 46B that use the Java language?", function(response, convo) {
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*ye*./) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("Then you have the computing background needed for CS 146 (Data Structures and Algorithms) and CS 151 (Object-Oriented Design).  You should consider taking these courses in your first semester at SJSU if you have met their mathematics prerequisites.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      askQ5(response, convo);
      convo.next();
    }
  });
}

askQ5 = function(response, convo){
  convo.ask("5. Have you successfully completed equivalents of CS 46A and CS 46B that use a language other than Java?", function(response, convo){
    convo.say("In that case...");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("You should enroll in CS 49J (Programming in Java) as soon as possible. After completing CS 49J successfully, you should take CS 146 and CS 151 as soon as you can.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      askQ6(response, convo);
      convo.next();
    }
  });
}

askQ6 = function(response, convo){
  convo.ask("6. Have you successfully completed a CS 46A equivalent but not a CS 46B equivalent?", function(response, convo){
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("Then if the CS 46A equivalent used Java you may enroll in CS 46B.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      convo.say("If you have not successfully completed a CS 46A equivalent, then you should enroll in CS 46A.");
      convo.say("You may instead, take a 46B equivalent at the same institution where you took your 46A equivalent, and then follow the instructions above for students whose 46A and 46B equivalencies didn't use Java.");
      convo.say("A third possibility is to simply take CS 46A at SJSU.  Your 46A will almost certainly count as equivalent to CS 49C if it used C or C++, and will likely count as an elective for the CS majo");
      askQ7(response, convo);
      convo.next();
    }
  });
}

askQ7 = function(response, convo){
  convo.say("7. Determine which General Education (GE) and Physical Education requirements apply to you.");
  convo.ask("Are you a second baccalaureate student?", function(response, convo){
    convo.say("Got it.");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*am[^n][^n]/)){
      convo.say("Then you need not complete any of the Core GE or physical education requirements.");
      convo.say("However, you are subject to the American Institutions requirement in US and California history and government, if you haven't completed it already.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }

    else {
      askQ7b(response, convo);
      convo.next();
    }
  });
}

askQ7b = function(response, convo){
  convo.ask("Do you have a baccalaureate from a regionally accredited US university?", function(response, convo){

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*do[^n][^n]/)){
      convo.say("Then you needn't complete any of the SJSU Studies requirements, except for CS 100W and Phil 134, which are required courses in the BSCS.");
      convo.say("You will need to complete the 'additional science' course requirement of the BSCS, which many students complete with an SJSU studies course.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }

    else{
      convo.say("If you are not a second baccalaureate student, then you are responsible for all of the General Education requirements.");
      convo.say("The Registrar's office will determine which of your transfer courses will count as SJSU GE courses.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }
  });
}

askContext = function(response, convo) {
  convo.ask("Is this issue related to Computer Science or something else?", function(response, convo) {
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*compu.*/) || response.text.toLowerCase().match(/.*cs.*/)){

      convo.say("For most CS issues, you should start with the appropriate advisor, as given on the CS advising web page. Link: http://www.sjsu.edu/cs/practicalities/cs-advising/");
      convo.say("For more complicated CS issues, or if you have problems with your graduation, you may consult the undergraduate coordinator. Link: http://www.sjsu.edu/cs/community/index.html.");
      convo.say("If you have problems with your graduation, you should see the undergraduate coordinator.");
      convo.say("Appointments may be made with the department chair through the CS department office in MH 208. ");

      askIfDone(response, convo);
      convo.next();
    }
    else{

      convo.say("For other assistance");
      convo.say('');
      convo.say('Many university facilities also stand ready to assist you. These include:');
      convo.say('1. The College of Science Advising Center [http://www.sjsu.edu/cosac/]');
      convo.say('2. Peer Connections (for tutoring)[http://peerconnections.sjsu.edu/]');
      convo.say('3. The SJSU Writing Center [http://www.sjsu.edu/writingcenter/]');
      convo.say('4. The SJSU Advising Hub [http://www.sjsu.edu/advising/]');
      convo.say('5. Academic Advising & Retention Services [http://www.sjsu.edu/aars/]');
      convo.say('6. SJSU Counseling and Psychological Services [http://www.sjsu.edu/counseling/]');
      convo.say('7. The SJSU Career Center [http://www.sjsu.edu/careercenter/index.html]');
      convo.say('8. The Division of Student Affairs [http://www.sjsu.edu/studentaffairs/]');

      askIfDone(response, convo);
      convo.next();

    }
  });
}

askIfDone = function(response, convo) {
  convo.ask("Does that answer your question?", function(response, convo) {

    if (response.text.toLowerCase().match(/.*ye.*/)
      || response.text.toLowerCase().match(/.*sure.*/)
      || response.text.toLowerCase().match(/.*thank.*/)
      || response.text.toLowerCase().match(/.*does.*/)){

      convo.say("Glad I could help!");
      convo.say("See you.");
      convo.next();
    }
    else{

      convo.say("No problem! Let's try again.")
      convo.say("Do you need help or have a question?");
      convo.next();

      controller.hears(['question'], 'direct_message', wit.hears, function(bot, message) {
      bot.reply(message,'Happy to help. What is your question?');
      });
    }
  });
}

controller.storage.teams.all(function(err,teams) {

  if (err) {
    throw new Error(err);
  }

  // connect all teams with bots up to slack!
  for (var t  in teams) {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM(function(err, bot) {
        if (err) {
          console.log('Error connecting bot to Slack:',err);
        } else {
          trackBot(bot);
        }
      });
    }
  }

});
<commit_msg>use wit.hears for messges<commit_after>// import botkit
var Botkit = require('botkit');

// get authorization tokens from environment file
require('./env.js');

if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
  console.log('Error: Specify clientId clientSecret and port in environment');
  process.exit(1);
}

if (!process.env.wit) {
  console.log('Error: Specify wit server token in environment')
}

// top level controller object used to spawn bots and respond to events
var controller = Botkit.slackbot({
  json_file_store: './db_advisor_bot/',
}).configureSlackApp(
  {
    clientId: process.env.clientId,
    clientSecret: process.env.clientSecret,
    scopes: ['bot'],
  }
);

// import witai middleware
var wit = require('botkit-middleware-witai')({
  token: process.env.wit
});

// setup webserver and webhook endpoints
controller.setupWebserver(process.env.port,function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);

  webserver.get('/', function(req,res) {
    res.sendFile('index.html', {root: __dirname});
  });

// create Oauth endpoints
controller.createOauthEndpoints(controller.webserver,function(err,req,res) {
  if (err) {
    res.status(500).send('ERROR: ' + err);
  } else {
    res.send('Success!');
  }
 });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};
function trackBot(bot) {
  _bots[bot.config.token] = bot;
}

// bot creation event
controller.on('create_bot',function(bot,config) {

  if (_bots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM(function(err) {

      if (!err) {
        trackBot(bot);
      }

      bot.startPrivateConversation({user: config.createdBy},function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          convo.say('I am a bot that has just joined your team');
          convo.say('You must now /invite me to a channel so that I can be of use!');
        }
      });
    });
  }
});

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
  console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
  console.log('** The RTM api just closed');
  // you may want to attempt to re-open
});

// enable witai mmiddleware
controller.middleware.receive.use(wit.receive);

// use middleware to process message and infer intent
controller.hears(['hello'], 'direct_message', wit.hears, function(bot, message) {
  bot.reply(message,"Hello! How can I help?");
});

// end conversation
controller.hears(['goodbye'],'direct_message', wit.hears, function(bot,message) {
  bot.reply(message,'Goodbye');
  bot.rtm.close();
});

// begin a conversation
controller.hears(['question'], 'direct_message', wit.hears, function(bot, message) {
  bot.reply(message,'Happy to help. What is your question?');
});

// entry point for question 1
controller.hears(['rules'], 'direct_message', wit.hears, function(bot, message){
  bot.reply(message, "Good question!");
  bot.startConversation(message, askContext);
});

// entry point for question 2
controller.hears(['transfer student'], 'direct_message', wit.hears, function(bot, message){
  bot.reply(message, "I am glad you asked!");
  bot.reply(message, "1. Make sure to attend one of the campus Transfer Orientation sessions, as described in the material sent to you by the university upon your acceptance.");
  bot.reply(message, "2. If you still have queries, make an appointment to resolve any unresolved issues with the appropriate advisor, as listed on the CS department advising web site. [http://www.sjsu.edu/cs/practicalities/cs-advising/]");
  bot.reply(message, "3. Determine, preferably in consultation with an advisor, whether you are ready to start upper-division coursework in Computer Science.");
  bot.startConversation(message, askQ4);
});

askQ4 = function(response, convo){
  convo.ask("4. Have you successfully completed equivalents of CS 46A and CS 46B that use the Java language?", function(response, convo) {
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*ye*./) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("Then you have the computing background needed for CS 146 (Data Structures and Algorithms) and CS 151 (Object-Oriented Design).  You should consider taking these courses in your first semester at SJSU if you have met their mathematics prerequisites.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      askQ5(response, convo);
      convo.next();
    }
  });
}

askQ5 = function(response, convo){
  convo.ask("5. Have you successfully completed equivalents of CS 46A and CS 46B that use a language other than Java?", function(response, convo){
    convo.say("In that case...");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("You should enroll in CS 49J (Programming in Java) as soon as possible. After completing CS 49J successfully, you should take CS 146 and CS 151 as soon as you can.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      askQ6(response, convo);
      convo.next();
    }
  });
}

askQ6 = function(response, convo){
  convo.ask("6. Have you successfully completed a CS 46A equivalent but not a CS 46B equivalent?", function(response, convo){
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*have[^n][^n]/)){
      convo.say("Then if the CS 46A equivalent used Java you may enroll in CS 46B.");
      askQ7(response, convo);
      convo.next();
    }

    else{
      convo.say("If you have not successfully completed a CS 46A equivalent, then you should enroll in CS 46A.");
      convo.say("You may instead, take a 46B equivalent at the same institution where you took your 46A equivalent, and then follow the instructions above for students whose 46A and 46B equivalencies didn't use Java.");
      convo.say("A third possibility is to simply take CS 46A at SJSU.  Your 46A will almost certainly count as equivalent to CS 49C if it used C or C++, and will likely count as an elective for the CS majo");
      askQ7(response, convo);
      convo.next();
    }
  });
}

askQ7 = function(response, convo){
  convo.say("7. Determine which General Education (GE) and Physical Education requirements apply to you.");
  convo.ask("Are you a second baccalaureate student?", function(response, convo){
    convo.say("Got it.");

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*am[^n][^n]/)){
      convo.say("Then you need not complete any of the Core GE or physical education requirements.");
      convo.say("However, you are subject to the American Institutions requirement in US and California history and government, if you haven't completed it already.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }

    else {
      askQ7b(response, convo);
      convo.next();
    }
  });
}

askQ7b = function(response, convo){
  convo.ask("Do you have a baccalaureate from a regionally accredited US university?", function(response, convo){

    if (response.text.toLowerCase().match(/.*ye.*/) || response.text.toLowerCase().match(/.*do[^n][^n]/)){
      convo.say("Then you needn't complete any of the SJSU Studies requirements, except for CS 100W and Phil 134, which are required courses in the BSCS.");
      convo.say("You will need to complete the 'additional science' course requirement of the BSCS, which many students complete with an SJSU studies course.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }

    else{
      convo.say("If you are not a second baccalaureate student, then you are responsible for all of the General Education requirements.");
      convo.say("The Registrar's office will determine which of your transfer courses will count as SJSU GE courses.");
      convo.say("For more information about General Education requirements, check the Schedule of Classes. Select the appropriate semester's Policies and Procedures, and then follow the General Education (GE) link. [http://info.sjsu.edu/home/schedules.html]");
      askIfDone(response, convo);
      convo.next();
    }
  });
}

askContext = function(response, convo) {
  convo.ask("Is this issue related to Computer Science or something else?", function(response, convo) {
    convo.say("Ok.");

    if (response.text.toLowerCase().match(/.*compu.*/) || response.text.toLowerCase().match(/.*cs.*/)){

      convo.say("For most CS issues, you should start with the appropriate advisor, as given on the CS advising web page. Link: http://www.sjsu.edu/cs/practicalities/cs-advising/");
      convo.say("For more complicated CS issues, or if you have problems with your graduation, you may consult the undergraduate coordinator. Link: http://www.sjsu.edu/cs/community/index.html.");
      convo.say("If you have problems with your graduation, you should see the undergraduate coordinator.");
      convo.say("Appointments may be made with the department chair through the CS department office in MH 208. ");

      askIfDone(response, convo);
      convo.next();
    }
    else{

      convo.say("For other assistance");
      convo.say('');
      convo.say('Many university facilities also stand ready to assist you. These include:');
      convo.say('1. The College of Science Advising Center [http://www.sjsu.edu/cosac/]');
      convo.say('2. Peer Connections (for tutoring)[http://peerconnections.sjsu.edu/]');
      convo.say('3. The SJSU Writing Center [http://www.sjsu.edu/writingcenter/]');
      convo.say('4. The SJSU Advising Hub [http://www.sjsu.edu/advising/]');
      convo.say('5. Academic Advising & Retention Services [http://www.sjsu.edu/aars/]');
      convo.say('6. SJSU Counseling and Psychological Services [http://www.sjsu.edu/counseling/]');
      convo.say('7. The SJSU Career Center [http://www.sjsu.edu/careercenter/index.html]');
      convo.say('8. The Division of Student Affairs [http://www.sjsu.edu/studentaffairs/]');

      askIfDone(response, convo);
      convo.next();

    }
  });
}

askIfDone = function(response, convo) {
  convo.ask("Does that answer your question?", function(response, convo) {

    if (response.text.toLowerCase().match(/.*ye.*/)
      || response.text.toLowerCase().match(/.*sure.*/)
      || response.text.toLowerCase().match(/.*thank.*/)
      || response.text.toLowerCase().match(/.*does.*/)){

      convo.say("Glad I could help!");
      convo.say("See you.");
      convo.next();
    }
    else{

      convo.say("No problem! Let's try again.")
      convo.say("Do you need help or have a question?");
      convo.next();

      controller.hears(['question'], 'direct_message', wit.hears, function(bot, message) {
      bot.reply(message,'Happy to help. What is your question?');
      });
    }
  });
}

controller.storage.teams.all(function(err,teams) {

  if (err) {
    throw new Error(err);
  }

  // connect all teams with bots up to slack!
  for (var t  in teams) {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM(function(err, bot) {
        if (err) {
          console.log('Error connecting bot to Slack:',err);
        } else {
          trackBot(bot);
        }
      });
    }
  }

});
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Map display

var map, markers = [], resultMarkers = [];
var initialize = function (element, centroid, zoom, features) {
    map = L.map(element, {
        scrollWheelZoom: true,
        doubleClickZoom: false,
        boxZoom: false,
        touchZoom: false
    }).setView(new L.LatLng(centroid[0], centroid[1]), zoom);
    var Stamen_Watercolor = L.tileLayer('http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png', {
        attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
        subdomains: 'abcd',
        minZoom: 1,
        maxZoom: 16,
        opacity: 0.5
    });
    Stamen_Watercolor.addTo(map);

    map.on("dblclick", function (e) {
        var newEventLink = $('')
            .text('Create event here!')
            .attr('href', '#')
            .on('click', function () {
                openCreateDialog(e.latlng)
            })[0];
        var popup = L.popup()
            .setLatLng(e.latlng)
            .setContent(newEventLink)
            .openOn(map);
    });
};

var addMarker = function(marker) {
    map.addLayer(marker);
    markers[marker.options._id] = marker;
};

var removeMarker = function(_id) {
    var marker = markers[_id];
    if (map.hasLayer(marker)) map.removeLayer(marker);
};

var openCreateDialog = function (latlng) {
    if (! Meteor.userId())
        throw new Meteor.Error(403, "You must be logged in");
    Session.set("createCoords", latlng);
    $("#newEvent").modal("show");
};

function createPopup(event) {
    return "" + event.title +
        "
" + event.description +
        "
Where: " + event.location +
        "
What to bring: " + event.artifact +
        "
 Organised by: " + event.organiser;
}

function createMarker(event) {
    var marker = new L.Marker(event.latlng, {
        _id: event._id,
        icon: L.divIcon({
            iconSize: [10, 10],
            className: 'leaflet-div-icon'
        }),
        riseOnHover: true
    })
        .bindPopup(createPopup(event))
        .on('click', function (e) {
            Session.set("selected", event._id);
        });
    return marker;
}
Template.map.created = function() {
    Events.find({}).observe({
        added: function(event) {
            var marker = createMarker(event);
            addMarker(marker);
            marker.valueOf()._icon.style.backgroundColor = event.category.color;
        },
        changed: function(event) {
            var marker = markers[event._id];
            if (marker) marker = createMarker(event)//marker.setIcon(createIcon(event));
        },
        removed: function(event) {
            removeMarker(event._id);
        }
    });

};

Template.map.rendered = function () {
    // basic housekeeping
    $(window).resize(function () {
        var h = $(window).height(), offsetTop = 90; // Calculate the top offset
        $('#map_canvas').css('height', (h - offsetTop));
    }).resize();

  // initialize map events
    if (!map) {
        initialize($("#map_canvas")[0], [48.28593, 16.30371], 4);
        var self = this;
        Tracker.autorun(function() {
          var selectedEvent = Events.findOne(Session.get("selected"));
          if (selectedEvent) {
            var line;
            if (!self.animatedMarker) {
              line = L.polyline([[selectedEvent.latlng.lat, selectedEvent.latlng.lng]]);
              self.animatedMarker = L.animatedMarker(line.getLatLngs(), {
                autoStart: false,
                distance: 10000,  // meters
                interval: 5, // milliseconds
                icon: L.divIcon({
                  iconSize: [20, 20],
                  className: 'leaflet-animated-icon'
                })
              });
              map.addLayer(self.animatedMarker);
            } else {
              // animate to here
              line = L.polyline([[self.animatedMarker.getLatLng().lat, self.animatedMarker.getLatLng().lng],
                [selectedEvent.latlng.lat, selectedEvent.latlng.lng]]);
              self.animatedMarker.setLine(line.getLatLngs());
              self.animatedMarker.start();
            }
          }
            var results = Session.get("results");

            function clearResults() {
                for (var i = 0, len = resultMarkers.length; i < len; i++) {
                    var marker = resultMarkers[i];
                    if (map.hasLayer(marker)) {
                        map.removeLayer(marker);
                    }
                }
            }

            function addResults() {
                for (var i = 0, len = results.length; i < len; i++) {
                    var result = results[i];
                    var resultMarker = new L.Marker(result.latlng, {
                        _id: result._id,
                        icon: L.divIcon({
                            iconSize: [20, 20],
                            className: 'leaflet-result-marker'
                        }),
                        zIndexOffset: -2000,
                        riseOnHover: true
                    });
                    resultMarkers[i] = resultMarker;
                    map.addLayer(resultMarker);
                }
            }
            function collectBounds() {
                var latlngArr = [];
                var i;
                for (i = 0, len = results.length; i < len; i++) {
                    latlngArr.push(results[i].latlng);
                }
                return L.latLngBounds(latlngArr);
            }
            function fitBoundsToResultSet() {
                var bounds = collectBounds();
                map.fitBounds(bounds, {maxZoom: 6});
            }

            if (!!results && results.length!=0){
                fitBoundsToResultSet();
                clearResults();
                addResults();
            }
        })
    }
    else {
        $('#map_container').html(map.getContainer());
    }

};

<commit_msg>refactorization<commit_after>///////////////////////////////////////////////////////////////////////////////
// Map display

var map, markers = [], resultMarkers = [];
var initialize = function (element, centroid, zoom, features) {
    map = L.map(element, {
        scrollWheelZoom: true,
        doubleClickZoom: false,
        boxZoom: false,
        touchZoom: false
    }).setView(new L.LatLng(centroid[0], centroid[1]), zoom);
    var Stamen_Watercolor = L.tileLayer('http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png', {
        attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
        subdomains: 'abcd',
        minZoom: 1,
        maxZoom: 16,
        opacity: 0.5
    });
    Stamen_Watercolor.addTo(map);

    map.on("dblclick", function (e) {
        var newEventLink = $('')
            .text('Create event here!')
            .attr('href', '#')
            .on('click', function () {
                openCreateDialog(e.latlng)
            })[0];
        var popup = L.popup()
            .setLatLng(e.latlng)
            .setContent(newEventLink)
            .openOn(map);
    });
};

var addMarker = function(marker, color) {
    map.addLayer(marker);
    markers[marker.options._id] = marker;
    marker.valueOf()._icon.style.backgroundColor = color;
};

var removeMarker = function(_id) {
    var marker = markers[_id];
    if (map.hasLayer(marker)) map.removeLayer(marker);
};

var openCreateDialog = function (latlng) {
    if (! Meteor.userId())
        throw new Meteor.Error(403, "You must be logged in");
    Session.set("createCoords", latlng);
    $("#newEvent").modal("show");
};

function createPopup(event) {
    return "" + event.title +
        "
" + event.description +
        "
Where: " + event.location +
        "
What to bring: " + event.artifact +
        "
 Organised by: " + event.organiser;
}

function createMarker(event) {
    return new L.Marker(event.latlng, {
        _id: event._id,
        icon: L.divIcon({
            iconSize: [10, 10],
            className: 'leaflet-div-icon'
        }),
        riseOnHover: true
    })
    .bindPopup(createPopup(event))
    .on('click', function (e) {
        Session.set("selected", event._id);
    });
}

function animateMarkers(self) {
    var selectedEvent = Events.findOne(Session.get("selected"));
    if (selectedEvent) {
        var line;
        if (!self.animatedMarker) {
            line = L.polyline([[selectedEvent.latlng.lat, selectedEvent.latlng.lng]]);
            self.animatedMarker = L.animatedMarker(line.getLatLngs(), {
                autoStart: false,
                distance: 10000,  // meters
                interval: 5, // milliseconds
                icon: L.divIcon({
                    iconSize: [20, 20],
                    className: 'leaflet-animated-icon'
                })
            });
            map.addLayer(self.animatedMarker);
        } else {
            // animate to here
            line = L.polyline([[self.animatedMarker.getLatLng().lat, self.animatedMarker.getLatLng().lng],
                [selectedEvent.latlng.lat, selectedEvent.latlng.lng]]);
            self.animatedMarker.setLine(line.getLatLngs());
            self.animatedMarker.start();
        }
    }
}

function handleSearchResults(results) {
    function clearResults() {
        for (var i = 0, len = resultMarkers.length; i < len; i++) {
            var marker = resultMarkers[i];
            if (map.hasLayer(marker)) {
                map.removeLayer(marker);
            }
        }
    }

    function addResults() {
        for (var i = 0, len = results.length; i < len; i++) {
            var result = results[i];
            var resultMarker = new L.Marker(result.latlng, {
                _id: result._id,
                icon: L.divIcon({
                    iconSize: [20, 20],
                    className: 'leaflet-result-marker'
                }),
                zIndexOffset: -2000,
                riseOnHover: true
            });
            resultMarkers[i] = resultMarker;
            map.addLayer(resultMarker);
        }
    }
    function collectBounds() {
        var latlngArr = [];
        var i;
        for (i = 0, len = results.length; i < len; i++) {
            latlngArr.push(results[i].latlng);
        }
        return L.latLngBounds(latlngArr);
    }
    function fitBoundsToResultSet() {
        var bounds = collectBounds();
        map.fitBounds(bounds, {maxZoom: 6});
    }

    if (!!results && results.length != 0) {
        fitBoundsToResultSet();
        clearResults();
        addResults();
    }
}

Template.map.rendered = function () {
    var $mapCanvas = $('#map_canvas');
    $(window).resize(function () {
        var h = $(window).height(), offsetTop = 90; // Calculate the top offset
        $mapCanvas.css('height', (h - offsetTop));
    }).resize();

  // initialize map events
    if (!map) {
        initialize($mapCanvas[0], [48.28593, 16.30371], 4);
        var self = this;
        Tracker.autorun(function() {
            animateMarkers(self);
            var results = Session.get("results");
            handleSearchResults(results);
        })
    }
    else {
        $('#map_container').html(map.getContainer());
    }

    Events.find({}).observe({
        added: function(event) {
            var marker = createMarker(event);
            var color = event.category.color;
            addMarker(marker, color);
        },
        changed: function(event) {
            var marker = markers[event._id];
            if (marker) {
                removeMarker(event._id);
                marker = createMarker(event);
                var color = event.category.color;
                addMarker(marker, color);
            }
        },
        removed: function(event) {
            removeMarker(event._id);
        }
    });

};

<|endoftext|>


<commit_before>"use strict";

class ColorWheel {
    constructor(id, size) {
        this.radius = size / 2;
        this.ringsize = size / 10;
        this.color = 0;

        this.length = Math.sqrt(2 * Math.pow(this.radius - this.ringsize, 2));
        this.half = this.length / 2;

        this.can = $('<canvas>').attr({
            width: size,
            height: size
        });
        this.ctx = this.can.get(0).getContext('2d');

        this.outer = $('<div/>').addClass('colorwheel-outer').css({
            width: this.ringsize,
            height: this.ringsize
        });
        this.inner = $('<div/>').addClass('colorwheel-inner');

        this.holder = $('<div/>').attr('id', id).append(this.outer).append(this.inner).append(this.can);
        this.x = this.y = this.radius;

        this.setHue(this.color * (Math.PI / 180));
        this.inner.css({
            left: this.can.position().left + this.x - 5,
            top: this.can.position().top + this.y - 5
        });

        this.focusOut = false, this.focusIn = false;

        this.renderOuter();
        this.renderInner();

        var _this = this;
        $(this.holder).on('mousedown', function (evt) {
            evt.preventDefault();
            var offset = _this.getRelativePos(_this.can, evt);
            var dist = Math.sqrt(Math.pow(_this.x - offset.x, 2) + Math.pow(_this.y - offset.y, 2));
            if (dist < _this.radius && dist > _this.radius - _this.ringsize) {
                _this.focusOut = true;
                _this.updateOuter(evt);
            } else if (dist < _this.radius - _this.ringsize) {
                _this.focusIn = true;
                _this.updateInner(evt);
            }
        });
        $(document).on('mouseup', function (evt) {
            _this.focusOut = _this.focusIn = false;
        }).on('mousemove', function (evt) {
            if (_this.focusOut) {
                _this.updateOuter(evt);
            } else if (_this.focusIn) {
                _this.updateInner(evt);
            }
        });
        $('body').append(this.holder);
    }

    getRGB() {
        var x = this.inner.offset().left - this.can.offset().left + 5;
        var y = this.inner.offset().top - this.can.offset().top + 5;

        var c = this.ctx.getImageData(x, y, 1, 1).data;
        return { 'r': c[0], 'g': c[1], 'b': c[2] };
    }

    getHex() {
        var rgb = this.getRGB();
        return this.toHex(rgb.r) + this.toHex(rgb.g) + this.toHex(rgb.b);
    }

    getHSL() {
        var rgb = this.getRGB();
        return ColorConvert.RGBToHSL(rgb.r, rgb.g, rgb.b);
    }

    toHex(value) {
        var hex = value.toString(16);
        return hex.length == 1 ? '0' + hex : hex;
    }

    setHue(angle) {
        var middle = this.radius - ((this.ringsize) / 2);
        this.outer.css({
            left: Math.cos(angle) * middle + this.can.position().left + this.x - (this.ringsize / 2) - 2,
            top: Math.sin(angle) * middle + this.can.position().top + this.y - (this.ringsize / 2) - 2
        });
    }

    renderOuter() {
        for (var i = 0; i < 360; i++) {
            this.ctx.beginPath();
            this.ctx.fillStyle = 'hsl(' + i + ', 100%, 50%)';
            this.ctx.moveTo(this.x, this.y);
            this.ctx.arc(this.x, this.y, this.radius, (i - 2) * (Math.PI / 180), (i * (Math.PI / 180)), false);
            this.ctx.lineTo(this.x, this.y);
            this.ctx.fill();
        }
        //Draw white to hide the center area
        this.ctx.beginPath();
        this.ctx.fillStyle = 'white';
        this.ctx.moveTo(this.x, this.y);
        this.ctx.arc(this.x, this.y, this.radius - this.ringsize, 0, 2 * Math.PI, false);
        this.ctx.fill();
    }

    renderInner() {
        this.fillStyle = "#CFCFCF";
        this.ctx.lineWidth = 3;
        this.ctx.strokeRect(this.x - this.half + 2, this.y - this.half + 2, this.length - 2, this.length - 2);

        for(var i = 0; i < 100; i++) {
            var line = this.ctx.createLinearGradient(
                this.x - this.half,
                this.y - this.half + (i * (this.length / 100)),
                this.x + this.half,
                this.y - this.half + (i * (this.length / 100))
            );

            var stops = 15;

            for(var j = 0; j < stops; j++) {
                var hsl = ColorConvert.HSBToHSL(this.color / 60, j / stops, (100 - i) / 100);
                line.addColorStop(j / stops, 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)');
            }

                this.ctx.fillStyle = line;
                if(i < 97) {
                    this.ctx.fillRect(
                        this.x - this.half + 2,
                        (this.y - this.half) + 2 + ((i * (this.length)) / 100),
                        this.length - 2,
                        (this.length / 100) + 2
                    );
                } else {
                    this.ctx.fillRect(
                        this.x - this.half + 2,
                        (this.y - this.half) + 2 + ((i * (this.length)) / 100),
                        this.length - 2,
                        (this.length / 100))
                }
        }
    }

    updateInner(evt) {
        var offset = this.getRelativePos(this.can, evt);
        var xDiff = Math.abs(this.x - offset.x);
        var yDiff = Math.abs(this.y - offset.y);
        var dist = Math.sqrt(Math.pow(this.x - offset.x, 2) + Math.pow(this.y - offset.y, 2));
        if (dist < this.radius - this.ringsize && xDiff < this.half && yDiff < this.half) {
            this.inner.css({
                left: this.can.position().left + offset.x - 5,
                top: this.can.position().top + offset.y - 5
            });
        }
    }

    updateOuter(evt) {
        var offset = this.getRelativePos(this.can, evt);
        var rawAngle = Math.atan2(offset.y - this.y, offset.x - this.x), angle = rawAngle * 180 / Math.PI;
        if (rawAngle < 0) {
            angle = 360 - (angle * -1);
        }
        this.setHue(rawAngle);
        this.color = Math.round(angle);
        this.renderInner();
    }

    getRelativePos(element, evt) {
        var parentOffset = $(element).offset();
        var eX = evt.pageX - parentOffset.left;
        var eY = evt.pageY - parentOffset.top;
        return { x: eX, y: eY };
    }
}

class ColorConvert {
    static RGBtoHSL(R, G, B) {
        var H,S,L;

        var r = R / 255;
        var g = G / 255;
        var b = B / 255;

        var cMax = Math.max(r, g, b);
        var cMin = Math.min(r, g, b);

        var dC = cMax - cMin;

        if(dC == 0) {
            return {
                'h': 0, 's': 0, 'l': 0
            };
        }

        L = (cMax + cMin) / 2;

        if(cMax == cMin) {
            S = 0;
        } else {
            S = L < .5 ? (cMax - cMin)/(cMax + cMin) : (cMax - cMin)/(2 - cMax - cMin);
        }

        switch(cMax){
            case r: H = (g-b) / dC; break;
            case g: H = 2 + (b-r)/ dC; break;
            case b: H = 4 + (r - g) / dC; break;
        }

        return {
            'h': Math.round(H * 60),
            's': Math.round(S * 100),
            'l': Math.round(L * 100)
        };
    }

    static HSLToHSB (H,S,L) {
        var h, s, b;
        h = H;
        b = (2 * L + S*(1 - Math.abs(2*L - 1))) / 2;

        if(b == 0) {
            return {
                'h': 0,
                's': 0,
                'b': 0
            };
        }

        s = (2 * (b - L))/b;

        return {
            'h': Math.round(h * 60),
            's': Math.round(s * 100),
            'b': Math.round(b * 100)
        };
    }

    static HSBToHSL(H,S,B) {
        var h, s, l;
        h = H;

        l = .5 * B * (2 - S);

        s = (B * S) / (1 - Math.abs(2 * l - 1));

        if(S == 0 && B == 1) {
            return {
                'h': H * 60,
                's': S * 100,
                'l': B * 100
            };
        }

        return {
            'h': Math.round(h * 60),
            's': Math.round(s * 100),
            'l': Math.round(l * 100)
        };
    }

    static RGBToHSL(R, G, B) {
        var H,S,L;

        var r = R / 255;
        var g = G / 255;
        var b = B / 255;

        var cMax = Math.max(r, g, b);
        var cMin = Math.min(r, g, b);

        var dC = cMax - cMin;

        L = (cMax + cMin) / 2;

        if(cMax == cMin) {
            S = 0;
        } else {
            S = L < .5 ? (cMax - cMin)/(cMax + cMin) : (cMax - cMin)/(2 - cMax - cMin);
        }

        switch(cMax){
            case r: H = (g-b) / dC; break;
            case g: H = 2 + (b-r)/ dC; break;
            case b: H = 4 + (r - g) / dC; break;
        }

        return {
            'h': Math.round(H * 60),
            's': Math.round(S * 100),
            'l': Math.round(L * 100)
        };
    }

    //TODO: Finish
    static HSBToRGB(H,S,B) {
        var c = S * B;
        var x = c * (1 - Math.abs( H / 60) % 2 - 1)
        var m = B - c;

        var R,G,B;

    }
}<commit_msg>Remove border from inner box<commit_after>"use strict";

class ColorWheel {
    constructor(id, size) {
        this.radius = size / 2;
        this.ringsize = size / 10;
        this.color = 0;

        this.length = Math.sqrt(2 * Math.pow(this.radius - this.ringsize, 2));
        this.half = this.length / 2;

        this.can = $('<canvas>').attr({
            width: size,
            height: size
        });
        this.ctx = this.can.get(0).getContext('2d');

        this.outer = $('<div/>').addClass('colorwheel-outer').css({
            width: this.ringsize,
            height: this.ringsize
        });
        this.inner = $('<div/>').addClass('colorwheel-inner');

        this.holder = $('<div/>').attr('id', id).append(this.outer).append(this.inner).append(this.can);
        this.x = this.y = this.radius;

        this.setHue(this.color * (Math.PI / 180));
        this.inner.css({
            left: this.can.position().left + this.x - 5,
            top: this.can.position().top + this.y - 5
        });

        this.focusOut = false, this.focusIn = false;

        this.renderOuter();
        this.renderInner();

        var _this = this;
        $(this.holder).on('mousedown', function (evt) {
            evt.preventDefault();
            var offset = _this.getRelativePos(_this.can, evt);
            var dist = Math.sqrt(Math.pow(_this.x - offset.x, 2) + Math.pow(_this.y - offset.y, 2));
            if (dist < _this.radius && dist > _this.radius - _this.ringsize) {
                _this.focusOut = true;
                _this.updateOuter(evt);
            } else if (dist < _this.radius - _this.ringsize) {
                _this.focusIn = true;
                _this.updateInner(evt);
            }
        });
        $(document).on('mouseup', function (evt) {
            _this.focusOut = _this.focusIn = false;
        }).on('mousemove', function (evt) {
            if (_this.focusOut) {
                _this.updateOuter(evt);
            } else if (_this.focusIn) {
                _this.updateInner(evt);
            }
        });
        $('body').append(this.holder);
    }

    getRGB() {
        var x = this.inner.offset().left - this.can.offset().left + 5;
        var y = this.inner.offset().top - this.can.offset().top + 5;

        var c = this.ctx.getImageData(x, y, 1, 1).data;
        return { 'r': c[0], 'g': c[1], 'b': c[2] };
    }

    getHex() {
        var rgb = this.getRGB();
        return this.toHex(rgb.r) + this.toHex(rgb.g) + this.toHex(rgb.b);
    }

    getHSL() {
        var rgb = this.getRGB();
        return ColorConvert.RGBToHSL(rgb.r, rgb.g, rgb.b);
    }

    toHex(value) {
        var hex = value.toString(16);
        return hex.length == 1 ? '0' + hex : hex;
    }

    setHue(angle) {
        var middle = this.radius - ((this.ringsize) / 2);
        this.outer.css({
            left: Math.cos(angle) * middle + this.can.position().left + this.x - (this.ringsize / 2) - 2,
            top: Math.sin(angle) * middle + this.can.position().top + this.y - (this.ringsize / 2) - 2
        });
    }

    renderOuter() {
        for (var i = 0; i < 360; i++) {
            this.ctx.beginPath();
            this.ctx.fillStyle = 'hsl(' + i + ', 100%, 50%)';
            this.ctx.moveTo(this.x, this.y);
            this.ctx.arc(this.x, this.y, this.radius, (i - 2) * (Math.PI / 180), (i * (Math.PI / 180)), false);
            this.ctx.lineTo(this.x, this.y);
            this.ctx.fill();
        }
        //Draw white to hide the center area
        this.ctx.beginPath();
        this.ctx.fillStyle = 'white';
        this.ctx.moveTo(this.x, this.y);
        this.ctx.arc(this.x, this.y, this.radius - this.ringsize, 0, 2 * Math.PI, false);
        this.ctx.fill();
    }

    renderInner() {
        for(var i = 0; i < 100; i++) {
            var line = this.ctx.createLinearGradient(
                this.x - this.half,
                this.y - this.half + (i * (this.length / 100)),
                this.x + this.half,
                this.y - this.half + (i * (this.length / 100))
            );

            var stops = 15;

            for(var j = 0; j < stops; j++) {
                var hsl = ColorConvert.HSBToHSL(this.color / 60, j / stops, (100 - i) / 100);
                line.addColorStop(j / stops, 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)');
            }

                this.ctx.fillStyle = line;
                if(i < 99) {
                    this.ctx.fillRect(
                        this.x - this.half + 2,
                        (this.y - this.half) + 2 + ((i * (this.length)) / 100),
                        this.length - 2,
                        (this.length / 100) + 2
                    );
                } else {
                    this.ctx.fillRect(
                        this.x - this.half + 2,
                        (this.y - this.half) + 2 + ((i * (this.length)) / 100),
                        this.length - 2,
                        (this.length / 100))
                }
        }
    }

    updateInner(evt) {
        var offset = this.getRelativePos(this.can, evt);
        var xDiff = Math.abs(this.x - offset.x);
        var yDiff = Math.abs(this.y - offset.y);
        var dist = Math.sqrt(Math.pow(this.x - offset.x, 2) + Math.pow(this.y - offset.y, 2));
        if (dist < this.radius - this.ringsize && xDiff < this.half && yDiff < this.half) {
            this.inner.css({
                left: this.can.position().left + offset.x - 5,
                top: this.can.position().top + offset.y - 5
            });
        }
    }

    updateOuter(evt) {
        var offset = this.getRelativePos(this.can, evt);
        var rawAngle = Math.atan2(offset.y - this.y, offset.x - this.x), angle = rawAngle * 180 / Math.PI;
        if (rawAngle < 0) {
            angle = 360 - (angle * -1);
        }
        this.setHue(rawAngle);
        this.color = Math.round(angle);
        this.renderInner();
    }

    getRelativePos(element, evt) {
        var parentOffset = $(element).offset();
        var eX = evt.pageX - parentOffset.left;
        var eY = evt.pageY - parentOffset.top;
        return { x: eX, y: eY };
    }
}

class ColorConvert {
    static RGBtoHSL(R, G, B) {
        var H,S,L;

        var r = R / 255;
        var g = G / 255;
        var b = B / 255;

        var cMax = Math.max(r, g, b);
        var cMin = Math.min(r, g, b);

        var dC = cMax - cMin;

        if(dC == 0) {
            return {
                'h': 0, 's': 0, 'l': 0
            };
        }

        L = (cMax + cMin) / 2;

        if(cMax == cMin) {
            S = 0;
        } else {
            S = L < .5 ? (cMax - cMin)/(cMax + cMin) : (cMax - cMin)/(2 - cMax - cMin);
        }

        switch(cMax){
            case r: H = (g-b) / dC; break;
            case g: H = 2 + (b-r)/ dC; break;
            case b: H = 4 + (r - g) / dC; break;
        }

        return {
            'h': Math.round(H * 60),
            's': Math.round(S * 100),
            'l': Math.round(L * 100)
        };
    }

    static HSLToHSB (H,S,L) {
        var h, s, b;
        h = H;
        b = (2 * L + S*(1 - Math.abs(2*L - 1))) / 2;

        if(b == 0) {
            return {
                'h': 0,
                's': 0,
                'b': 0
            };
        }

        s = (2 * (b - L))/b;

        return {
            'h': Math.round(h * 60),
            's': Math.round(s * 100),
            'b': Math.round(b * 100)
        };
    }

    static HSBToHSL(H,S,B) {
        var h, s, l;
        h = H;

        l = .5 * B * (2 - S);

        s = (B * S) / (1 - Math.abs(2 * l - 1));

        if(S == 0 && B == 1) {
            return {
                'h': H * 60,
                's': S * 100,
                'l': B * 100
            };
        }

        return {
            'h': Math.round(h * 60),
            's': Math.round(s * 100),
            'l': Math.round(l * 100)
        };
    }

    static RGBToHSL(R, G, B) {
        var H,S,L;

        var r = R / 255;
        var g = G / 255;
        var b = B / 255;

        var cMax = Math.max(r, g, b);
        var cMin = Math.min(r, g, b);

        var dC = cMax - cMin;

        L = (cMax + cMin) / 2;

        if(cMax == cMin) {
            S = 0;
        } else {
            S = L < .5 ? (cMax - cMin)/(cMax + cMin) : (cMax - cMin)/(2 - cMax - cMin);
        }

        switch(cMax){
            case r: H = (g-b) / dC; break;
            case g: H = 2 + (b-r)/ dC; break;
            case b: H = 4 + (r - g) / dC; break;
        }

        return {
            'h': Math.round(H * 60),
            's': Math.round(S * 100),
            'l': Math.round(L * 100)
        };
    }

    //TODO: Finish
    static HSBToRGB(H,S,B) {
        var c = S * B;
        var x = c * (1 - Math.abs( H / 60) % 2 - 1)
        var m = B - c;

        var R,G,B;

    }
}<|endoftext|>


<commit_before>/* @flow */

export type globOptions = {
  types: 'all' | 'files' | 'directories',
  ignorePattern: string
}

export type Command = 'build' | 'clean' | 'graph' | 'load' | 'log' | 'save' | 'scrub'

export type Phase = 'initialize' | 'execute' | 'finalize'

export type Action = 'parse' | 'run' | 'updateDependencies'

export type RuleInfo = {
  name: string,
  description: string
}

export type FileType = {
  fileName?: RegExp,
  contents?: RegExp,
  hashSkip?: RegExp
}

export type FileCache = {
  timeStamp: Date,
  hash: string,
  type?: string,
  subType?: string,
  value?: any,
  jobNames?: Array<string>
}

export type RuleCache = {
  name: string,
  command: Command,
  phase: Phase,
  jobName?: string,
  parameters: Array<string>,
  inputs: Array<string>,
  outputs: Array<string>
}

export type Cache = {
  filePath: string,
  options: Object,
  files: { [filePath: string]: FileCache },
  rules: Array<RuleCache>
}

export type LineRange = {
  start: number,
  end: number
}

export type Reference = {
  file: string,
  range?: LineRange
}

export type Parser = {
  names: Array<string>,
  patterns: Array<RegExp>,
  evaluate: (reference: Reference, groups: Object) => void
}

export type Severity = 'info' | 'warning' | 'error'

export type Message = {
  severity: Severity,
  text: string,
  name?: string,
  category?: string,
  source?: Reference,
  log?: Reference
}

export type LogEvent = {
  type: 'log',
  severity: Severity,
  text: string,
  name?: string,
  category?: string,
  source?: Reference,
  log?: Reference
}

export type ActionEvent = {
  type: 'action',
  rule: string,
  action: string,
  triggers: Array<string>
}

export type CommandEvent = {
  type: 'command',
  rule: string,
  command: string
}

export type FileEvent = {
  type: 'fileChanged' | 'fileAdded' | 'fileDeleted' | 'fileRemoved',
  file: string,
  virtual?: boolean
}

export type InputOutputEvent = {
  type: 'inputAdded' | 'outputAdded',
  rule: string,
  file: string,
  virtual?: boolean
}

export type Event = LogEvent | ActionEvent | CommandEvent | FileEvent | InputOutputEvent

export type Option = {
  name: string,
  type: 'string' | 'strings' | 'number' | 'boolean' | 'variable',
  defaultValue?: any,
  description: string,
  values?: Array<any>,
  aliases?: Array<string>,
  commands: Array<string>,
  noInvalidate?: boolean
}

export type KillToken = {
  error?: Error,
  resolve?: Function,
  promise?: Promise<void>
}

export type ShellCall = {
  args: Array<string>,
  options: Object,
  status: string
}

export type ParsedLog = {
  inputs: Array<string>,
  outputs: Array<string>,
  messages: Array<Message>,
  calls: Array<ShellCall>
}

export type CommandOptions = {
  args: Array<string>,
  cd: string,
  severity: Severity,
  inputs?: Array<string>,
  outputs?: Array<string>,
  globbedInputs?: Array<string>,
  globbedOutputs?: Array<string>,
  stdout?: boolean | string,
  stderr?: boolean | string
}

export type LineRangeMapping = {
  input: LineRange,
  output: LineRange
}

export type SourceMap = {
  input: string,
  output: string,
  mappings: Array<LineRangeMapping>
}

export type SourceMaps = {
  maps: Array<SourceMap>
}

// START_AUTO

export interface OptionsInterface {
  [name: string]: string | Array<string>,
  bibtexEngine: 'bibtex' | 'bibtex8' | 'bibtexu' | 'pbibtex' | 'upbibtex',
  check?: Array<string>,
  cleanPatterns: Array<string>,
  copyTargetsToRoot: boolean,
  disableRules: Array<string>,
  dviToPdfEngine: 'dvipdfm' | 'xdvipdfmx' | 'dvipdfmx',
  engine: string,
  epstopdfBoundingBox: 'default' | 'exact' | 'hires',
  epstopdfOutputPath: string,
  epstopdfRestricted: boolean,
  filePath: string,
  ignoreCache: boolean,
  ignoreUserOptions: boolean,
  indexAutomaticRanges: boolean,
  indexCompressBlanks: boolean,
  indexDictionary?: string,
  indexEngine: 'makeindex' | 'mendex' | 'texindy' | 'upmendex',
  indexForceKanji: boolean,
  indexLogPath?: string,
  indexOrdering: 'word' | 'letter',
  indexOutputPath?: string,
  indexSorting: 'default' | 'german' | 'thai' | 'locale',
  indexStartPage?: string,
  indexStyle?: string,
  intermediatePostScript: boolean,
  jobName?: string,
  jobNames: Array<string>,
  kanji?: 'euc' | 'jis' | 'sjis' | 'uptex' | 'utf8',
  kanjiInternal?: 'euc' | 'sjis' | 'uptex' | 'utf8',
  knitrConcordance: boolean,
  knitrOutputPath: string,
  lhs2texStyle: 'poly' | 'math' | 'newCode' | 'code' | 'typewriter' | 'verbatim',
  literateAgdaEngine: 'agda' | 'lhs2TeX' | 'none',
  literateHaskellEngine: 'lhs2TeX' | 'none',
  outputDirectory?: string,
  outputFormat: 'dvi' | 'pdf' | 'ps' | 'svg',
  phaseCycles: number,
  pweaveCacheDirectory: string,
  pweaveDocumentationMode: boolean,
  pweaveFigureDirectory: string,
  pweaveKernel: string,
  pweaveOutputFormat: 'tex' | 'texminted' | 'texpweave' | 'texpygments',
  pweaveOutputPath: string,
  severity: 'info' | 'warning' | 'error',
  shellEscape?: 'disabled' | 'restricted' | 'enabled',
  synctex: boolean
}

export const DEFAULT_OPTIONS = { $BIBINPUTS: [ '$ROOTDIR',
  '$ROOTDIR/$OUTDIR',
  '' ],
$TEXINPUTS: [ '$ROOTDIR',
  '$ROOTDIR/$OUTDIR',
  '' ],
bibtexEngine: 'bibtex',
cleanPatterns: [ '$OUTDIR/$JOB!($OUTEXT|.synctex.gz|.tex)',
  '/$OUTDIR/_minted-$JOB/*' ],
disableRules: [ ],
dviToPdfEngine: 'xdvipdfmx',
engine: 'pdflatex',
epstopdfBoundingBox: 'default',
epstopdfOutputPath: '$DIR_0/$NAME_0.pdf',
indexAutomaticRanges: true,
indexEngine: 'makeindex',
indexOrdering: 'word',
indexSorting: 'default',
knitrConcordance: true,
knitrOutputPath: '$JOB.tex',
lhs2texStyle: 'poly',
literateAgdaEngine: 'agda',
literateHaskellEngine: 'lhs2TeX',
outputFormat: 'pdf',
phaseCycles: 20,
pweaveCacheDirectory: 'pweave-cache-for-$JOB',
pweaveFigureDirectory: 'pweave-figures-for-$JOB',
pweaveKernel: 'python3',
pweaveOutputFormat: 'tex',
pweaveOutputPath: '$JOB.tex',
severity: 'warning' }

// END_AUTO
<commit_msg>Add OptionsInterfaceMap type<commit_after>/* @flow */

export type globOptions = {
  types: 'all' | 'files' | 'directories',
  ignorePattern: string
}

export type Command = 'build' | 'clean' | 'graph' | 'load' | 'log' | 'save' | 'scrub'

export type Phase = 'initialize' | 'execute' | 'finalize'

export type Action = 'parse' | 'run' | 'updateDependencies'

export type RuleInfo = {
  name: string,
  description: string
}

export type FileType = {
  fileName?: RegExp,
  contents?: RegExp,
  hashSkip?: RegExp
}

export type FileCache = {
  timeStamp: Date,
  hash: string,
  type?: string,
  subType?: string,
  value?: any,
  jobNames?: Array<string>
}

export type RuleCache = {
  name: string,
  command: Command,
  phase: Phase,
  jobName?: string,
  parameters: Array<string>,
  inputs: Array<string>,
  outputs: Array<string>
}

export type Cache = {
  filePath: string,
  options: Object,
  files: { [filePath: string]: FileCache },
  rules: Array<RuleCache>
}

export type LineRange = {
  start: number,
  end: number
}

export type Reference = {
  file: string,
  range?: LineRange
}

export type Parser = {
  names: Array<string>,
  patterns: Array<RegExp>,
  evaluate: (reference: Reference, groups: Object) => void
}

export type Severity = 'info' | 'warning' | 'error'

export type Message = {
  severity: Severity,
  text: string,
  name?: string,
  category?: string,
  source?: Reference,
  log?: Reference
}

export type LogEvent = {
  type: 'log',
  severity: Severity,
  text: string,
  name?: string,
  category?: string,
  source?: Reference,
  log?: Reference
}

export type ActionEvent = {
  type: 'action',
  rule: string,
  action: string,
  triggers: Array<string>
}

export type CommandEvent = {
  type: 'command',
  rule: string,
  command: string
}

export type FileEvent = {
  type: 'fileChanged' | 'fileAdded' | 'fileDeleted' | 'fileRemoved',
  file: string,
  virtual?: boolean
}

export type InputOutputEvent = {
  type: 'inputAdded' | 'outputAdded',
  rule: string,
  file: string,
  virtual?: boolean
}

export type Event = LogEvent | ActionEvent | CommandEvent | FileEvent | InputOutputEvent

export type Option = {
  name: string,
  type: 'string' | 'strings' | 'number' | 'boolean' | 'variable',
  defaultValue?: any,
  description: string,
  values?: Array<any>,
  aliases?: Array<string>,
  commands: Array<string>,
  noInvalidate?: boolean
}

export type KillToken = {
  error?: Error,
  resolve?: Function,
  promise?: Promise<void>
}

export type ShellCall = {
  args: Array<string>,
  options: Object,
  status: string
}

export type ParsedLog = {
  inputs: Array<string>,
  outputs: Array<string>,
  messages: Array<Message>,
  calls: Array<ShellCall>
}

export type CommandOptions = {
  args: Array<string>,
  cd: string,
  severity: Severity,
  inputs?: Array<string>,
  outputs?: Array<string>,
  globbedInputs?: Array<string>,
  globbedOutputs?: Array<string>,
  stdout?: boolean | string,
  stderr?: boolean | string
}

export type LineRangeMapping = {
  input: LineRange,
  output: LineRange
}

export type SourceMap = {
  input: string,
  output: string,
  mappings: Array<LineRangeMapping>
}

export type SourceMaps = {
  maps: Array<SourceMap>
}

// START_AUTO

export interface OptionsInterface {
  [name: string]: string | Array<string>,
  bibtexEngine: 'bibtex' | 'bibtex8' | 'bibtexu' | 'pbibtex' | 'upbibtex',
  check?: Array<string>,
  cleanPatterns: Array<string>,
  copyTargetsToRoot: boolean,
  disableRules: Array<string>,
  dviToPdfEngine: 'dvipdfm' | 'xdvipdfmx' | 'dvipdfmx',
  engine: string,
  epstopdfBoundingBox: 'default' | 'exact' | 'hires',
  epstopdfOutputPath: string,
  epstopdfRestricted: boolean,
  filePath: string,
  ignoreCache: boolean,
  ignoreUserOptions: boolean,
  indexAutomaticRanges: boolean,
  indexCompressBlanks: boolean,
  indexDictionary?: string,
  indexEngine: 'makeindex' | 'mendex' | 'texindy' | 'upmendex',
  indexForceKanji: boolean,
  indexLogPath?: string,
  indexOrdering: 'word' | 'letter',
  indexOutputPath?: string,
  indexSorting: 'default' | 'german' | 'thai' | 'locale',
  indexStartPage?: string,
  indexStyle?: string,
  intermediatePostScript: boolean,
  jobName?: string,
  jobNames: Array<string>,
  kanji?: 'euc' | 'jis' | 'sjis' | 'uptex' | 'utf8',
  kanjiInternal?: 'euc' | 'sjis' | 'uptex' | 'utf8',
  knitrConcordance: boolean,
  knitrOutputPath: string,
  lhs2texStyle: 'poly' | 'math' | 'newCode' | 'code' | 'typewriter' | 'verbatim',
  literateAgdaEngine: 'agda' | 'lhs2TeX' | 'none',
  literateHaskellEngine: 'lhs2TeX' | 'none',
  outputDirectory?: string,
  outputFormat: 'dvi' | 'pdf' | 'ps' | 'svg',
  phaseCycles: number,
  pweaveCacheDirectory: string,
  pweaveDocumentationMode: boolean,
  pweaveFigureDirectory: string,
  pweaveKernel: string,
  pweaveOutputFormat: 'tex' | 'texminted' | 'texpweave' | 'texpygments',
  pweaveOutputPath: string,
  severity: 'info' | 'warning' | 'error',
  shellEscape?: 'disabled' | 'restricted' | 'enabled',
  synctex: boolean
}

export const DEFAULT_OPTIONS = { $BIBINPUTS: [ '$ROOTDIR',
  '$ROOTDIR/$OUTDIR',
  '' ],
$TEXINPUTS: [ '$ROOTDIR',
  '$ROOTDIR/$OUTDIR',
  '' ],
bibtexEngine: 'bibtex',
cleanPatterns: [ '$OUTDIR/$JOB!($OUTEXT|.synctex.gz|.tex)',
  '/$OUTDIR/_minted-$JOB/*' ],
disableRules: [ ],
dviToPdfEngine: 'xdvipdfmx',
engine: 'pdflatex',
epstopdfBoundingBox: 'default',
epstopdfOutputPath: '$DIR_0/$NAME_0.pdf',
indexAutomaticRanges: true,
indexEngine: 'makeindex',
indexOrdering: 'word',
indexSorting: 'default',
knitrConcordance: true,
knitrOutputPath: '$JOB.tex',
lhs2texStyle: 'poly',
literateAgdaEngine: 'agda',
literateHaskellEngine: 'lhs2TeX',
outputFormat: 'pdf',
phaseCycles: 20,
pweaveCacheDirectory: 'pweave-cache-for-$JOB',
pweaveFigureDirectory: 'pweave-figures-for-$JOB',
pweaveKernel: 'python3',
pweaveOutputFormat: 'tex',
pweaveOutputPath: '$JOB.tex',
severity: 'warning' }

// END_AUTO

export type OptionInterfaceMap = { [name: string]: OptionsInterface }
<|endoftext|>


<commit_before>var dropchop = (function(dc) {

    'use strict';

    dc = dc || {};
    dc.ops = dc.ops || {};

    dc.ops.file = {
        upload: {
            description: 'Upload from your computer (.geojson)',
            icon: '',
            execute: function() {
                // inspired from geojson.io
                // https://github.com/mapbox/geojson.io/blob/gh-pages/src/ui/file_bar.js#L390
                var $blindInput = $('<input>')
                        .attr('type', 'file')
                        .attr('multiple', 'true')
                        .css('visibility', 'hidden')
                        .css('position', 'absolute')
                        .css('height', '0')
                        .on('change', function() {
                            var files = this.files;
                            $(files).each(function(i) {
                                // var ext = dc.util.getFileExtension(files[i].name);

                                // // if it is a shapefile or zip file
                                // if (ext === 'shp' || ext === 'zip') {
                                //   // upload a shapefile and add the layer
                                //   dc.util.readShpFile
                                //   shp("files/pandr").then(function(geojson){
                                //     //do something with your geojson
                                //   });
                                // } else {
                                dc.util.readFile(files[i]);
                                // }

                            });
                            $blindInput.remove();
                        });
                $('body').append($blindInput);
                $blindInput.click();
            }
        },

        'load-url': {
            description: 'Import file from a URL',
            icon: '',
            parameters: [
                {
                    name: 'url',
                    description :'URL',
                    type: 'text',
                    default: 'http://',
                },
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-url']);
            },
            get: function(event, name, parameters) {
                var url = parameters[0];
                dc.util.xhr(url, dc.ops.file[name].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    // get filename based on the end of the url - not sure if this is sustainable
                    var name = xhr.responseURL.substring(xhr.responseURL.lastIndexOf('/')+1);
                    $(dc).trigger('file:added', [name, data]);
                } else {
                    dc.notify('error', xhr.status + ': could not retrieve Gist. Please check your URL');
                }
            }
        },

        'load-gist': {
            description: 'Import files from Gist',
            icon: '',
            parameters: [
                {
                    name: 'gist',
                    description :'Gist ID or URL',
                    type: 'text',
                    default: 'https://gist.github.com/'
                },
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-gist']);
            },
            get: function(event, name, parameters) {
                var gist = parameters[0].split('/')[parameters[0].split('/').length-1];
                var url = 'https://api.github.com/gists/' + gist;
                dc.util.xhr(url, dc.ops.file[name].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    for (var f in data.files) {
                        var name = data.files[f].filename;
                        $(dc).trigger('file:added', [name, JSON.parse(data.files[f].content)]);
                    }
                    // dc.notify('success', 'Succesfully retrieved gist')
                } else {
                    dc.notify('error', xhr.status + ': could not retrieve Gist. Please check your URL');
                }
            }
        },

        'load-arcgis': {
            description: 'Query an ArcGIS Server Feature Service',
            icon: '',
            _temp: {},
            parameters: [
                {
                    name: 'layer name',
                    description: 'Assign a name to the layer',
                    type: 'text',
                    default: 'arcgis_layer'
                },
                {
                    name: 'feature service',
                    description: 'Enter URL to ArcGIS Feature Service (ex. http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/0)',
                    type: 'text',
                    default: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/0'
                },
                {
                    name: 'where',
                    description: 'A where clause for the query filter. Any legal SQL where clause operating on the fields in the layer is allowed.',
                    type: 'text',
                    default: '1=1'
                }
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-arcgis']);
            },
            get: function(event, name, parameters) {
                dc.ops.file['load-arcgis']._temp.layerName = parameters[0];
                // build the query with bounding box
                var bbox = dc.util.getEsriBBox();
                dc.util.xhr(parameters[1] + '/query?f=json&inSR=4326&outSR=4326&geometry=' + bbox + '&where=' + parameters[2], dc.ops.file['load-arcgis'].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    var geojson = toGeoJSON(data);
                    console.log(geojson);
                    if (data.features.length<1) {
                        dc.notify('info', 'No features found in your query.');
                    } else {
                        $(dc).trigger('file:added', [dc.ops.file['load-arcgis']._temp.layerName, geojson]);
                        dc.notify('success', 'Found ' + data.features.length + ' features from the ArcGIS Service');
                    }
                } else {
                    dc.notify('error', xhr.status + ': could not query the ArcGIS Service');
                }
            }
        },

        'load-overpass': {
            description: 'Query the Overpass API',
            icon: '',
            _temp: {},
            parameters: [
                {
                    name: 'query',
                    description :'Learn more about the query language.',
                    type: 'text',
                    default: 'amenity=bar'
                },
                {
                    name: 'layer name',
                    description :'Name of the layer added if results are found.',
                    type: 'text',
                    default: 'overpass_layer_name'
                }
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-overpass']);
            },
            get: function(event, name, parameters) {
                // set layer name to _temp for later usage
                dc.ops.file['load-overpass']._temp.layerName = parameters[1]; // this is ugly

                // build the query with bounding box
                var bbox = dc.util.getBBox();
                dc.util.xhr('http://overpass-api.de/api/interpreter?[out:json];node['+parameters[0]+']('+bbox+');out;', dc.ops.file['load-overpass'].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    var geojson = osmtogeojson(data);
                    if (!data.elements.length) {
                        dc.notify('info', 'No elements found in your query.');
                    } else {
                        $(dc).trigger('file:added', [dc.ops.file['load-overpass']._temp.layerName, geojson]);
                        dc.notify('success', 'Found ' + data.elements.length + ' elements from the Overpass API');
                    }
                } else {
                    dc.notify('error', xhr.status + ': could not query the Overpass API');
                }
            }
        },

        location: {
            description: 'Add your location as a layer',
            icon: '',
            execute: function() {
                if (!navigator.geolocation) {
                    dc.notify('error', 'Geolocation is not available on this browser.', 4000);
                } else {
                    dc.map.m.locate();
                }
            }
        },

        'break1': { type: 'break' },

        'save-geojson': {
            minFeatures: 1,
            description: 'Save as GeoJSON',
            icon: '',
            createsLayer: false,
            execute: function() {
                for(var i = 0; i < dc.selection.list.length; i++) {
                    (function(file) {
                        var content = JSON.stringify(dc.selection.list[file].raw);
                        var title = 'dropchop_' + dc.selection.list[file].name + '.geojson';
                        saveAs(new Blob([content], {
                            type: 'text/plain;charset=utf-8'
                        }), title);
                    })(i);
                }
            }
        },

        'save-shapefile': {
            minFeatures: 1,
            description: 'Save as Shapefile',
            icon: '',
            execute: function() {
                $(dc.selection.list).each(function(i) {
                    try {
                        var options = {
                            folder: 'dropchop_' + dc.selection.list[i].name,
                            types: {
                                point: 'dropchop_' + dc.selection.list[i].name + '_point',
                                polygon: 'dropchop_' + dc.selection.list[i].name + '_poly',
                                line: 'dropchop_' + dc.selection.list[i].name + '_line'
                            }
                        };
                        shpwrite.download(dc.selection.list[i].raw, options);
                    } catch (err) {
                        dc.notify('Error', 'There was a problem downloading the shapefile.' + err);
                        throw err;
                    }
                });
            },
            createsLayer: false
        },

        'break2': { type: 'break' },

        extent: {
            minFeatures: 1,
            description: 'View extent of layers',
            icon: '',
            execute: function() {
                if(!dc.selection.list.length) {
                    // extent of entire layer list if nothing selected
                    dc.map.m.fitBounds(dc.map.layergroup.getBounds());
                } else {
                    // otherwise build the bounds based on selected layers
                    var bounds;
                    $(dc.selection.list).each(function(l) {
                        var fl = dc.selection.list[l].featurelayer;
                        if(!l) bounds = fl.getBounds();
                        else bounds.extend(fl.getBounds());
                    });
                    dc.map.m.fitBounds(bounds);
                }

            }
        },

        expand: {
            minFeatures: 1,
            description: 'Convert FeatureCollection into separate features',
            icon: '',
            execute: function() {
                var count = 0;
                if (dc.selection.list.length === 1) {
                    var fc = dc.selection.list[0];
                    if (fc.raw.type === 'FeatureCollection') {
                        $(fc.raw.features).each(function(f) {
                            count++;
                            $(dc).trigger('file:added', [fc.name + count + '_' + fc.raw.features[f].geometry.type, fc.raw.features[f]]);
                        });
                    } else {
                        var err = new Error('That needs to be a feature collection!');
                        dc.notify('error', 'Layer is not a FeatureCollection.');
                        throw err;
                    }
                } else {
                    dc.notify('info', 'Please select a single layer!');
                }
            }
        },

        combine: {
            minFeatures: 1,
            description: 'Combines selected features into a single FeatureCollection',
            icon: '',
            execute: function() {
                var fc = {
                    type: 'FeatureCollection',
                    features: []
                };
                if (dc.selection.list.length > 0) {
                    $(dc.selection.list).each(function(f) {
                        if (dc.selection.list[f].raw.type !== 'FeatureCollection') {
                            fc.features.push(dc.selection.list[f].raw);
                        } else {
                            dc.notify('info', dc.selection.list[f].name + ' was not added because it is already a FeatureCollection');
                        }
                    });
                    $(dc).trigger('file:added', ['new_FeatureCollection', fc]);
                } else {
                    dc.notify('info', 'No layers selected!');
                }
            }
        },

        // 'view-attributes': {
        //   minFeatures: 1,
        //   description: 'Show attribute table',
        //   icon: '',
        //   execute: function() {
        //     dc.attr.render(dc.selection.list[0]);
        //   }
        // },

        'break3': { type: 'break' },

        rename: {
            minFeatures: 1,
            description: 'Rename layer',
            icon: '',
            parameters: [
                {
                    name: 'Name',
                    type: 'text',
                    description: ''
                }
            ],
            execute: function() {
                if (dc.selection.list.length === 1) {
                    $(dc).trigger('form:file', ['rename']);
                } else {
                    dc.notify('info', 'Please select one layer.');
                }
            },
            callback: function(event, name, parameters) {
                console.log(name, parameters);
                $(dc).trigger('layer:rename', [dc.selection.list[0], parameters[0]]);
            }
        },

        remove: {
            minFeatures: 1,
            description: 'Remove selected layers',
            icon: '',
            execute: function() {
                $(dc.selection.list).each(function(i) {
                    console.log(this.stamp);
                    $(dc).trigger('layer:remove', [this.stamp]);
                });
                dc.selection.clear();
            }
        },

        info: {
            type: 'info',
            description: 'Learn more about dropchop',
            icon: '',
            execute: function() {
                window.location = '/about.html';
            }
        }
    };

    return dc;

})(dropchop || {});
<commit_msg>Query for all fields in feature service<commit_after>var dropchop = (function(dc) {

    'use strict';

    dc = dc || {};
    dc.ops = dc.ops || {};

    dc.ops.file = {
        upload: {
            description: 'Upload from your computer (.geojson)',
            icon: '',
            execute: function() {
                // inspired from geojson.io
                // https://github.com/mapbox/geojson.io/blob/gh-pages/src/ui/file_bar.js#L390
                var $blindInput = $('<input>')
                        .attr('type', 'file')
                        .attr('multiple', 'true')
                        .css('visibility', 'hidden')
                        .css('position', 'absolute')
                        .css('height', '0')
                        .on('change', function() {
                            var files = this.files;
                            $(files).each(function(i) {
                                // var ext = dc.util.getFileExtension(files[i].name);

                                // // if it is a shapefile or zip file
                                // if (ext === 'shp' || ext === 'zip') {
                                //   // upload a shapefile and add the layer
                                //   dc.util.readShpFile
                                //   shp("files/pandr").then(function(geojson){
                                //     //do something with your geojson
                                //   });
                                // } else {
                                dc.util.readFile(files[i]);
                                // }

                            });
                            $blindInput.remove();
                        });
                $('body').append($blindInput);
                $blindInput.click();
            }
        },

        'load-url': {
            description: 'Import file from a URL',
            icon: '',
            parameters: [
                {
                    name: 'url',
                    description :'URL',
                    type: 'text',
                    default: 'http://',
                },
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-url']);
            },
            get: function(event, name, parameters) {
                var url = parameters[0];
                dc.util.xhr(url, dc.ops.file[name].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    // get filename based on the end of the url - not sure if this is sustainable
                    var name = xhr.responseURL.substring(xhr.responseURL.lastIndexOf('/')+1);
                    $(dc).trigger('file:added', [name, data]);
                } else {
                    dc.notify('error', xhr.status + ': could not retrieve Gist. Please check your URL');
                }
            }
        },

        'load-gist': {
            description: 'Import files from Gist',
            icon: '',
            parameters: [
                {
                    name: 'gist',
                    description :'Gist ID or URL',
                    type: 'text',
                    default: 'https://gist.github.com/'
                },
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-gist']);
            },
            get: function(event, name, parameters) {
                var gist = parameters[0].split('/')[parameters[0].split('/').length-1];
                var url = 'https://api.github.com/gists/' + gist;
                dc.util.xhr(url, dc.ops.file[name].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    for (var f in data.files) {
                        var name = data.files[f].filename;
                        $(dc).trigger('file:added', [name, JSON.parse(data.files[f].content)]);
                    }
                    // dc.notify('success', 'Succesfully retrieved gist')
                } else {
                    dc.notify('error', xhr.status + ': could not retrieve Gist. Please check your URL');
                }
            }
        },

        'load-arcgis': {
            description: 'Query an ArcGIS Server Feature Service',
            icon: '',
            _temp: {},
            parameters: [
                {
                    name: 'layer name',
                    description: 'Assign a name to the layer',
                    type: 'text',
                    default: 'arcgis_layer'
                },
                {
                    name: 'feature service',
                    description: 'Enter URL to ArcGIS Feature Service (ex. http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/0)',
                    type: 'text',
                    default: 'http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/0'
                },
                {
                    name: 'where',
                    description: 'A where clause for the query filter. Any legal SQL where clause operating on the fields in the layer is allowed.',
                    type: 'text',
                    default: '1=1'
                }
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-arcgis']);
            },
            get: function(event, name, parameters) {
                dc.ops.file['load-arcgis']._temp.layerName = parameters[0];
                // build the query with bounding box
                var bbox = dc.util.getEsriBBox();
                dc.util.xhr(parameters[1] + '/query?f=json&inSR=4326&outSR=4326&geometry=' + bbox + '&where=' + parameters[2] + '&outfields=*', dc.ops.file['load-arcgis'].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    var geojson = toGeoJSON(data);
                    console.log(geojson);
                    if (data.features.length<1) {
                        dc.notify('info', 'No features found in your query.');
                    } else {
                        $(dc).trigger('file:added', [dc.ops.file['load-arcgis']._temp.layerName, geojson]);
                        dc.notify('success', 'Found ' + data.features.length + ' features from the ArcGIS Service');
                    }
                } else {
                    dc.notify('error', xhr.status + ': could not query the ArcGIS Service');
                }
            }
        },

        'load-overpass': {
            description: 'Query the Overpass API',
            icon: '',
            _temp: {},
            parameters: [
                {
                    name: 'query',
                    description :'Learn more about the query language.',
                    type: 'text',
                    default: 'amenity=bar'
                },
                {
                    name: 'layer name',
                    description :'Name of the layer added if results are found.',
                    type: 'text',
                    default: 'overpass_layer_name'
                }
            ],
            execute: function() {
                $(dc).trigger('form:file', ['load-overpass']);
            },
            get: function(event, name, parameters) {
                // set layer name to _temp for later usage
                dc.ops.file['load-overpass']._temp.layerName = parameters[1]; // this is ugly

                // build the query with bounding box
                var bbox = dc.util.getBBox();
                dc.util.xhr('http://overpass-api.de/api/interpreter?[out:json];node['+parameters[0]+']('+bbox+');out;', dc.ops.file['load-overpass'].callback);
            },
            callback: function(xhr, xhrEvent) {
                dropchop.util.loader(false);
                if (xhr.status === 200) {
                    var data = JSON.parse(xhr.responseText);
                    var geojson = osmtogeojson(data);
                    if (!data.elements.length) {
                        dc.notify('info', 'No elements found in your query.');
                    } else {
                        $(dc).trigger('file:added', [dc.ops.file['load-overpass']._temp.layerName, geojson]);
                        dc.notify('success', 'Found ' + data.elements.length + ' elements from the Overpass API');
                    }
                } else {
                    dc.notify('error', xhr.status + ': could not query the Overpass API');
                }
            }
        },

        location: {
            description: 'Add your location as a layer',
            icon: '',
            execute: function() {
                if (!navigator.geolocation) {
                    dc.notify('error', 'Geolocation is not available on this browser.', 4000);
                } else {
                    dc.map.m.locate();
                }
            }
        },

        'break1': { type: 'break' },

        'save-geojson': {
            minFeatures: 1,
            description: 'Save as GeoJSON',
            icon: '',
            createsLayer: false,
            execute: function() {
                for(var i = 0; i < dc.selection.list.length; i++) {
                    (function(file) {
                        var content = JSON.stringify(dc.selection.list[file].raw);
                        var title = 'dropchop_' + dc.selection.list[file].name + '.geojson';
                        saveAs(new Blob([content], {
                            type: 'text/plain;charset=utf-8'
                        }), title);
                    })(i);
                }
            }
        },

        'save-shapefile': {
            minFeatures: 1,
            description: 'Save as Shapefile',
            icon: '',
            execute: function() {
                $(dc.selection.list).each(function(i) {
                    try {
                        var options = {
                            folder: 'dropchop_' + dc.selection.list[i].name,
                            types: {
                                point: 'dropchop_' + dc.selection.list[i].name + '_point',
                                polygon: 'dropchop_' + dc.selection.list[i].name + '_poly',
                                line: 'dropchop_' + dc.selection.list[i].name + '_line'
                            }
                        };
                        shpwrite.download(dc.selection.list[i].raw, options);
                    } catch (err) {
                        dc.notify('Error', 'There was a problem downloading the shapefile.' + err);
                        throw err;
                    }
                });
            },
            createsLayer: false
        },

        'break2': { type: 'break' },

        extent: {
            minFeatures: 1,
            description: 'View extent of layers',
            icon: '',
            execute: function() {
                if(!dc.selection.list.length) {
                    // extent of entire layer list if nothing selected
                    dc.map.m.fitBounds(dc.map.layergroup.getBounds());
                } else {
                    // otherwise build the bounds based on selected layers
                    var bounds;
                    $(dc.selection.list).each(function(l) {
                        var fl = dc.selection.list[l].featurelayer;
                        if(!l) bounds = fl.getBounds();
                        else bounds.extend(fl.getBounds());
                    });
                    dc.map.m.fitBounds(bounds);
                }

            }
        },

        expand: {
            minFeatures: 1,
            description: 'Convert FeatureCollection into separate features',
            icon: '',
            execute: function() {
                var count = 0;
                if (dc.selection.list.length === 1) {
                    var fc = dc.selection.list[0];
                    if (fc.raw.type === 'FeatureCollection') {
                        $(fc.raw.features).each(function(f) {
                            count++;
                            $(dc).trigger('file:added', [fc.name + count + '_' + fc.raw.features[f].geometry.type, fc.raw.features[f]]);
                        });
                    } else {
                        var err = new Error('That needs to be a feature collection!');
                        dc.notify('error', 'Layer is not a FeatureCollection.');
                        throw err;
                    }
                } else {
                    dc.notify('info', 'Please select a single layer!');
                }
            }
        },

        combine: {
            minFeatures: 1,
            description: 'Combines selected features into a single FeatureCollection',
            icon: '',
            execute: function() {
                var fc = {
                    type: 'FeatureCollection',
                    features: []
                };
                if (dc.selection.list.length > 0) {
                    $(dc.selection.list).each(function(f) {
                        if (dc.selection.list[f].raw.type !== 'FeatureCollection') {
                            fc.features.push(dc.selection.list[f].raw);
                        } else {
                            dc.notify('info', dc.selection.list[f].name + ' was not added because it is already a FeatureCollection');
                        }
                    });
                    $(dc).trigger('file:added', ['new_FeatureCollection', fc]);
                } else {
                    dc.notify('info', 'No layers selected!');
                }
            }
        },

        // 'view-attributes': {
        //   minFeatures: 1,
        //   description: 'Show attribute table',
        //   icon: '',
        //   execute: function() {
        //     dc.attr.render(dc.selection.list[0]);
        //   }
        // },

        'break3': { type: 'break' },

        rename: {
            minFeatures: 1,
            description: 'Rename layer',
            icon: '',
            parameters: [
                {
                    name: 'Name',
                    type: 'text',
                    description: ''
                }
            ],
            execute: function() {
                if (dc.selection.list.length === 1) {
                    $(dc).trigger('form:file', ['rename']);
                } else {
                    dc.notify('info', 'Please select one layer.');
                }
            },
            callback: function(event, name, parameters) {
                console.log(name, parameters);
                $(dc).trigger('layer:rename', [dc.selection.list[0], parameters[0]]);
            }
        },

        remove: {
            minFeatures: 1,
            description: 'Remove selected layers',
            icon: '',
            execute: function() {
                $(dc.selection.list).each(function(i) {
                    console.log(this.stamp);
                    $(dc).trigger('layer:remove', [this.stamp]);
                });
                dc.selection.clear();
            }
        },

        info: {
            type: 'info',
            description: 'Learn more about dropchop',
            icon: '',
            execute: function() {
                window.location = '/about.html';
            }
        }
    };

    return dc;

})(dropchop || {});
<|endoftext|>