<commit_before>const crypto = require('crypto')

/**
 * @module Util
 */
module.exports = {
  /**
   * Performs string formatting for things like custom nicknames.
   *
   * @param {string} formatString The string to format (contains replacement strings)
   * @param {object} data The data to replace the string replacements with
   * @param {GuildMember} member The guild member this string is being formatted for
   * @returns {string} The processed string
   */
  formatDataString (formatString, data, member) {
    let replacements = {
      '%USERNAME%': data.robloxUsername,
      '%USERID%': data.robloxId,
      '%DISCORDNAME%': data.discordName || '',
      '%DISCORDID%': data.discordId || ''
    }

    if (member != null) {
      replacements['%DISCORDNAME%'] = member.user.username
      replacements['%DISCORDID%'] = member.id
      replacements['%SERVER%'] = member.guild.name
    }

    return formatString.replace(/%\w+%/g, (all) => {
      return replacements[all] || all
    })
  },

  /**
   * Returns a promise that resolves after the given time in milliseconds.
   * @param {int} sleepTime The amount of time until the promise resolves
   * @returns Promise
   */
  async sleep (sleepTime) {
    return new Promise(resolve => {
      setTimeout(resolve, sleepTime)
    })
  },

  /**
   * Takes an array of numbers and simplifies it into a string containing ranges, e.g.:
   * [0, 1, 2, 4, 6, 7, 8] --> "0-2, 4, 6-8"
   * @param {array} numbers The input numbers
   * @returns {string} The output string
   */
  simplifyNumbers (numbers) {
    let output = []
    let rangeStart
    for (let i = 0; i < numbers.length; i++) {
      let number = numbers[i]
      let next = numbers[i + 1]

      if (rangeStart != null && (next - number !== 1 || next == null)) {
        output.push(`${rangeStart}-${number}`)
        rangeStart = null
      } else if (next == null || next - number !== 1) {
        output.push(`${number}`)
      } else if (rangeStart == null) {
        rangeStart = number
      }
    }

    return output.join(', ')
  },

  /**
   * Returns an md5 hash of the input string.
   * This is used for a key for a group binding. The key is the hash of the json of the rank binding.
   * @param {string} string The input string.
   * @returns {string} The md5 hash of the string.
   */
  md5 (string) {
    return crypto.createHash('md5').update(string).digest('hex')
  },

  /**
   * Gets group rank binding text for a binding.
   * Turns a rank binding object into a human-readable format.
   * @param {object} binding The binding object.
   * @param {boolean} [addCodeBlock=false] Whether or not to wrap the binding in markdown code block.
   * @returns {string} The binding in human-readable format.
   */
  getBindingText (binding, addCodeBlock = false) {
    let bindingMessage = addCodeBlock ? '```markdown\n' : ''
    for (let [index, group] of binding.groups.entries()) {
      if (index > 0) bindingMessage += '...or\n'

      if (group.id.match(/[a-z]/i)) {
        bindingMessage += `# Virtual Group ${group.id}\n`
        bindingMessage += `Argument ${group.ranks.length > 0 ? group.ranks[0] : 'none'}`
      } else {
        bindingMessage += `# Group ${group.id}\n`
        bindingMessage += `Rank${group.ranks.length === 1 ? '' : 's'} ` + module.exports.simplifyNumbers(group.ranks)
      }
      bindingMessage += '\n\n'
    }

    return addCodeBlock ? bindingMessage + '\n```' : bindingMessage
  },

  /**
   * Gets the user-facing link for the verification registry.
   * This is not used for actually checking against the registry.
   * This is for functionality in the future where we want to know where the user came from to help make sure that
   * they are on the right account on the website by making sure the user is in the guild they came from.
   * @param {Guild} guild The guild this link is being generated for.
   * @returns {string} The link to the verification site
   */
  getVerifyLink (guild) {
    return `https://verify.eryn.io` // /?from=${guild.id}`
  }
}
<commit_msg>Add bandaid fix for #91<commit_after>const crypto = require('crypto')

/**
 * @module Util
 */
module.exports = {
  /**
   * Performs string formatting for things like custom nicknames.
   *
   * @param {string} formatString The string to format (contains replacement strings)
   * @param {object} data The data to replace the string replacements with
   * @param {GuildMember} member The guild member this string is being formatted for
   * @returns {string} The processed string
   */
  formatDataString (formatString, data, member) {
    let replacements = {
      '%USERNAME%': data.robloxUsername,
      '%USERID%': data.robloxId,
      '%DISCORDNAME%': data.discordName || '',
      '%DISCORDID%': data.discordId || ''
    }

    if (member != null) {
      replacements['%DISCORDNAME%'] = member.user.username
      replacements['%DISCORDID%'] = member.id
      replacements['%SERVER%'] = member.guild.name
    }

    return formatString.replace(/%\w+%/g, (all) => {
      return replacements[all] || all
    })
  },

  /**
   * Returns a promise that resolves after the given time in milliseconds.
   * @param {int} sleepTime The amount of time until the promise resolves
   * @returns Promise
   */
  async sleep (sleepTime) {
    return new Promise(resolve => {
      setTimeout(resolve, sleepTime)
    })
  },

  /**
   * Takes an array of numbers and simplifies it into a string containing ranges, e.g.:
   * [0, 1, 2, 4, 6, 7, 8] --> "0-2, 4, 6-8"
   * @param {array} numbers The input numbers
   * @returns {string} The output string
   */
  simplifyNumbers (numbers) {
    let output = []
    let rangeStart
    for (let i = 0; i < numbers.length; i++) {
      let number = numbers[i]
      let next = numbers[i + 1]

      if (rangeStart != null && (next - number !== 1 || next == null)) {
        output.push(`${rangeStart}-${number}`)
        rangeStart = null
      } else if (next == null || next - number !== 1) {
        output.push(`${number}`)
      } else if (rangeStart == null) {
        rangeStart = number
      }
    }

    return output.join(', ')
  },

  /**
   * Returns an md5 hash of the input string.
   * This is used for a key for a group binding. The key is the hash of the json of the rank binding.
   * @param {string} string The input string.
   * @returns {string} The md5 hash of the string.
   */
  md5 (string) {
    return crypto.createHash('md5').update(string).digest('hex')
  },

  /**
   * Gets group rank binding text for a binding.
   * Turns a rank binding object into a human-readable format.
   * @param {object} binding The binding object.
   * @param {boolean} [addCodeBlock=false] Whether or not to wrap the binding in markdown code block.
   * @returns {string} The binding in human-readable format.
   */
  getBindingText (binding, addCodeBlock = false) {
    let bindingMessage = addCodeBlock ? '```markdown\n' : ''

    if (binding.groups == null) {
      return `\nInvalid Group Format - Unbind role ${binding.role} to fix this problem.\n`
    }

    for (let [index, group] of binding.groups.entries()) {
      if (index > 0) bindingMessage += '...or\n'

      if (group.id.match(/[a-z]/i)) {
        bindingMessage += `# Virtual Group ${group.id}\n`
        bindingMessage += `Argument ${group.ranks.length > 0 ? group.ranks[0] : 'none'}`
      } else {
        bindingMessage += `# Group ${group.id}\n`
        bindingMessage += `Rank${group.ranks.length === 1 ? '' : 's'} ` + module.exports.simplifyNumbers(group.ranks)
      }
      bindingMessage += '\n\n'
    }

    return addCodeBlock ? bindingMessage + '\n```' : bindingMessage
  },

  /**
   * Gets the user-facing link for the verification registry.
   * This is not used for actually checking against the registry.
   * This is for functionality in the future where we want to know where the user came from to help make sure that
   * they are on the right account on the website by making sure the user is in the guild they came from.
   * @param {Guild} guild The guild this link is being generated for.
   * @returns {string} The link to the verification site
   */
  getVerifyLink (guild) {
    return `https://verify.eryn.io` // /?from=${guild.id}`
  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
const spawn = require('child_process').spawn
const electron = require('electron-prebuilt')
const path = require('path')
const serverPath = path.join(__dirname, '../server.js')

var args = [ serverPath ].concat([].concat(process.argv).splice(2))
spawn(electron, args, { stdio: 'inherit' })
<commit_msg>forward Electron's exit code<commit_after>#!/usr/bin/env node
const spawn = require('child_process').spawn
const electron = require('electron-prebuilt')
const path = require('path')
const serverPath = path.join(__dirname, '../server.js')

var args = [ serverPath ].concat([].concat(process.argv).splice(2))

var proc = spawn(electron, args, { stdio: 'inherit' })
proc.on('close', function (code) {
  process.exit(code)
})
<|endoftext|>


<commit_before>"use strict";

// http://usejsdoc.org/about-plugins.html
module.exports = {

    handlers: {

        newDoclet: function (event) {
            var doclet = event.doclet,
                title = [
                    "@",
                    doclet.meta.lineno
                ];
            if (doclet.undocumented) {
                title.push("(u)");
            }
            title.push(": ", doclet.longname, " (", doclet.kind, ")");
            console.log(title.join(""));
if(doclet.meta.lineno === 50) {
    console.log(doclet);
}
            if (doclet.kind === "member" || doclet.kind === "function") {
                if (doclet.undocumented && -1 === doclet.longname.indexOf("~")) {
                    // Generate documentation on the fly for easy cases
                }
                if (!doclet.access) {
                    if (doclet.name.charAt(0) === "_") {
                        doclet.access = "protected";
                    } else {
                        doclet.access = "public";
                    }
                }
            }
        }

    }

};
<commit_msg>Adds type on members<commit_after>"use strict";

function _logDoclet (doclet) {
    var title = [
            "@",
            doclet.meta.lineno
        ];
    if (doclet.undocumented) {
        title.push("(u)");
    }
    title.push(": ", doclet.longname, " (", doclet.kind, ")");
    console.log(title.join(""));
}

function _addMemberType (doclet) {
    if (!doclet.type) {
        // type: { names: [ 'String' ] }
        var codeType = doclet.meta.code.type,
            type;
        if (codeType === "Literal") {
            type = typeof doclet.meta.code.value;
            type = type.charAt(0).toUpperCase() + type.substr(1);
        } else if (codeType === "ArrayExpression") {
            type = "Array";
        } else if (codeType === "ObjectExpression") {
            type = "Object";
        }
        doclet.type = {
            names: [
                type
            ]
        }
    }
}

function _checkAccess (doclet) {
    if (!doclet.access) {
        if (doclet.name.charAt(0) === "_") {
            doclet.access = "protected";
        } else {
            doclet.access = "public";
        }
    }
}

// http://usejsdoc.org/about-plugins.html
module.exports = {

    handlers: {

        newDoclet: function (event) {
            var doclet = event.doclet,
                kind = doclet.kind;
            _logDoclet(doclet);
            if (kind === "member") {
                _addMemberType(doclet);
                _checkAccess(doclet);
            } else if (kind === "function") {
                _checkAccess(doclet);
            }
            // if(doclet.meta.lineno === 54 || doclet.meta.lineno === 82) {
            //     console.log(doclet);
            // }
        }

    }

};
<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.
//
//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.
//
//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
// __END_LICENSE__

sse = {}; //namespace

$.extend(sse, {
	initialize: function() {
		sse.heartbeat();
	},
	heartbeat: function() {
		setInterval(sse.checkHeartbeat, 11000);
		sse.subscribe('heartbeat', sse.connectedCallback, 'sse');
	},
	checkHeartbeat: function() {
		var connected = false
		if (sse.lastHeartbeat != undefined){
			var diff = moment.duration(moment().diff(sse.lastHeartbeat));
			if (diff.asSeconds() <= 10) {
				connected = true;
			}
		}
		if (!connected){
			sse.disconnectedCallback();
		}
	},
	connectedCallback: function(event){
		try {
			sse.lastHeartbeat = moment(JSON.parse(event.data).timestamp);
			var cdiv = $("#connected_div");
			if (cdiv.hasClass('alert-danger')){
				cdiv.removeClass('alert-danger');
				cdiv.addClass('alert-success');
				var c = $("#connected");
				c.removeClass('fa-bolt');
				c.addClass('fa-plug');
			}
		} catch(err){
			// in case there is no such page
		}
	},
	disconnectedCallback: function(event){
		try {
			var cdiv = $("#connected_div");
			if (cdiv.hasClass('alert-success')){
				cdiv.removeClass('alert-success');
				cdiv.addClass('alert-danger');
				var c = $("#connected");
				c.addClass('fa-bolt');
				c.removeClass('fa-plug');
			}
		} catch(err){
			// in case there is no such page
		}
	},
	allChannels: function(theFunction, channels){
		for (var i=0; i<channels.length; i++){
			var channel = channels[i];
			if (channel != 'sse') {
				theFunction(channel);
			}
		}
	},
	activeChannels: undefined,
	getChannelsUrl: '/xgds_core/sseActiveChannels',
	getChannels: function(url) {
		// get the active channels over AJAX
		if (sse.activeChannels === undefined){
			$.ajax({
	            url: sse.getChannelsUrl,
	            dataType: 'json',
	            async: false,
	            success: $.proxy(function(data) {
	                sse.activeChannels = data;
	            }, this)
	          });
		}
		return sse.activeChannels;
	},
	parseEventChannel: function(event){
		return sse.parseChannel(event.target.url);
	},
	parseChannel: function(fullUrl){
		var splits = fullUrl.split('=');
		if (splits.length > 1){
			return splits[splits.length-1];
		}
		return 'sse';
	},
	subscribe: function(event_type, callback, channels) {
		if (channels != undefined) {
			if (!_.isArray(channels)) {
				channels = [channels];
			}
			for (var i=0; i<channels.length; i++){
				var channel = channels[i];
				console.log('SUBSCRIBING TO ' + channel + ':' + event_type);

                var source = new EventSource("/sse/stream?channel=" + channel);
                source.addEventListener(event_type, callback, false);
            }
		} else {
			var allChannels = sse.getChannels();
			for (var i=0; i<allChannels.length; i++){
				var channel = allChannels[i];
				console.log('SUBSCRIBING TO ' + channel + ':' + event_type);
				var source = new EventSource("/sse/stream?channel=" + channel);
				source.addEventListener(event_type, callback, false);
			}
		}
	}
});<commit_msg>remove commetns for sse<commit_after>// __BEGIN_LICENSE__
//Copyright (c) 2015, United States Government, as represented by the
//Administrator of the National Aeronautics and Space Administration.
//All rights reserved.
//
//The xGDS platform is licensed under the Apache License, Version 2.0
//(the "License"); you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//http://www.apache.org/licenses/LICENSE-2.0.
//
//Unless required by applicable law or agreed to in writing, software distributed
//under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
//CONDITIONS OF ANY KIND, either express or implied. See the License for the
//specific language governing permissions and limitations under the License.
// __END_LICENSE__

sse = {}; //namespace

$.extend(sse, {
	initialize: function() {
		sse.heartbeat();
	},
	heartbeat: function() {
		setInterval(sse.checkHeartbeat, 11000);
		sse.subscribe('heartbeat', sse.connectedCallback, 'sse');
	},
	checkHeartbeat: function() {
		var connected = false
		if (sse.lastHeartbeat != undefined){
			var diff = moment.duration(moment().diff(sse.lastHeartbeat));
			if (diff.asSeconds() <= 10) {
				connected = true;
			}
		}
		if (!connected){
			sse.disconnectedCallback();
		}
	},
	connectedCallback: function(event){
		try {
			sse.lastHeartbeat = moment(JSON.parse(event.data).timestamp);
			var cdiv = $("#connected_div");
			if (cdiv.hasClass('alert-danger')){
				cdiv.removeClass('alert-danger');
				cdiv.addClass('alert-success');
				var c = $("#connected");
				c.removeClass('fa-bolt');
				c.addClass('fa-plug');
			}
		} catch(err){
			// in case there is no such page
		}
	},
	disconnectedCallback: function(event){
		try {
			var cdiv = $("#connected_div");
			if (cdiv.hasClass('alert-success')){
				cdiv.removeClass('alert-success');
				cdiv.addClass('alert-danger');
				var c = $("#connected");
				c.addClass('fa-bolt');
				c.removeClass('fa-plug');
			}
		} catch(err){
			// in case there is no such page
		}
	},
	allChannels: function(theFunction, channels){
		for (var i=0; i<channels.length; i++){
			var channel = channels[i];
			if (channel != 'sse') {
				theFunction(channel);
			}
		}
	},
	activeChannels: undefined,
	getChannelsUrl: '/xgds_core/sseActiveChannels',
	getChannels: function(url) {
		// get the active channels over AJAX
		if (sse.activeChannels === undefined){
			$.ajax({
	            url: sse.getChannelsUrl,
	            dataType: 'json',
	            async: false,
	            success: $.proxy(function(data) {
	                sse.activeChannels = data;
	            }, this)
	          });
		}
		return sse.activeChannels;
	},
	parseEventChannel: function(event){
		return sse.parseChannel(event.target.url);
	},
	parseChannel: function(fullUrl){
		var splits = fullUrl.split('=');
		if (splits.length > 1){
			return splits[splits.length-1];
		}
		return 'sse';
	},
	subscribe: function(event_type, callback, channels) {
		if (channels != undefined) {
			if (!_.isArray(channels)) {
				channels = [channels];
			}
			for (var i=0; i<channels.length; i++){
				var channel = channels[i];
				//console.log('SUBSCRIBING TO ' + channel + ':' + event_type);

                var source = new EventSource("/sse/stream?channel=" + channel);
                source.addEventListener(event_type, callback, false);
            }
		} else {
			var allChannels = sse.getChannels();
			for (var i=0; i<allChannels.length; i++){
				var channel = allChannels[i];
				//console.log('SUBSCRIBING TO ' + channel + ':' + event_type);
				var source = new EventSource("/sse/stream?channel=" + channel);
				source.addEventListener(event_type, callback, false);
			}
		}
	}
});<|endoftext|>


<commit_before>const express = require('express');
const basicAuth = require('basic-auth');
const models = require('../models');
const Errors = require('../lib/errors');

const Ticket = models.Ticket;
const Service = models.Service;
const router = express.Router({ mergeParams: true });

/**
 * @api {get} /tickets Get all tickets
 * @apiName GetTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiSuccess  {Object[]}  tickets
 * @apiSuccess  {String}    tickets.service
 * @apiSuccess  {Date}      tickets.created
 * @apiSuccess  {Date}      tickets.updated
 * @apiSuccess  {Date}      tickets.completed
 * @apiSuccess  {Object}    tickets.details
 * @apiSuccess  {Object[]}  tickets.comments
 * @apiSuccess  {String}    tickets.comments.author
 * @apiSuccess  {String}    tickets.comments.body
 * @apiSuccess  {Date}      tickets.comments.created
 * @apiSuccess  {Date}      tickets.comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    const options = {};
    if (req.body.service_id) {
      options.service = req.body.service_id;
    }

    Ticket.find(options)
      .then((tickets) => {
        res.body = tickets;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId Get specific ticket
 * @apiName GetTicketById
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam    {String}    ticketId
 *
 * @apiSuccess  {String}    service
 * @apiSuccess  {Date}      created
 * @apiSuccess  {Date}      updated
 * @apiSuccess  {Date}      completed
 * @apiSuccess  {Object}    details
 * @apiSuccess  {Object[]}  comments
 * @apiSuccess  {String}    comments.author
 * @apiSuccess  {String}    comments.body
 * @apiSuccess  {Date}      comments.created
 * @apiSuccess  {Date}      comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets Create a ticket
 * @apiName PostTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 * @apiDescription Registered services will post errors information to this resource. All
 * parameters passed into the request body will be recorded.
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission service
 */
router.post('/', (req, res, next) => {
  const credentials = basicAuth(req);
  if (!credentials || !credentials.name || !credentials.pass) {
    next(new Errors.Unauthorized());
  } else {
    Service.findById(credentials.name)
      .then(service => service.authorize(credentials.pass))
      .then(() => {
        const currentTime = new Date();
        const ticket = new Ticket({
          created: currentTime,
          updated: currentTime,
          service: credentials.name,
          details: req.body,
        });
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId Close a ticket
 * @apiName PostTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.completed = new Date();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId Delete a ticket
 * @apiName DeleteTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then(ticket => ticket.remove())
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments Get all ticket comments
 * @apiName GetTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiSuccess  {Object[]}
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 */
router.get('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .populate(['comments.author'])
      .then((ticket) => {
        res.body = ticket.comments;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId/comments Comment on a ticket
 * @apiName PostTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else if (!req.body.message) {
    next(new Errors.Generic('Message required', 400));
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = {
          author: req.session.user._id,
          body: req.body.message,
        };
        ticket.comments.push(comment);
        return ticket.save()
          .then(() => Promise.resolve(ticket.comments.pop()));
      })
      .then((comment) => {
        res.body = comment;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {put} /tickets/:ticketId/comments/:commentId Update a ticket comment
 * @apiName PutTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.put('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = ticket.comments.id(req.params.commentId);
        if (!comment || comment.author.toString() !== req.session.user._id) {
          return Promise.reject(new Errors.Unauthorized());
        }
        comment.body = req.body.message;
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId/comments/:commentId Delete a ticket comment
 * @apiName DeleteTicketsIdCommentId
 * @apiGroup Ticket
 * @apiVersion 0.1.0
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */

module.exports = router;

<commit_msg>get comments by id<commit_after>const express = require('express');
const basicAuth = require('basic-auth');
const models = require('../models');
const Errors = require('../lib/errors');

const Ticket = models.Ticket;
const Service = models.Service;
const router = express.Router({ mergeParams: true });

/**
 * @api {get} /tickets Get all tickets
 * @apiName GetTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiSuccess  {Object[]}  tickets
 * @apiSuccess  {String}    tickets.service
 * @apiSuccess  {Date}      tickets.created
 * @apiSuccess  {Date}      tickets.updated
 * @apiSuccess  {Date}      tickets.completed
 * @apiSuccess  {Object}    tickets.details
 * @apiSuccess  {Object[]}  tickets.comments
 * @apiSuccess  {String}    tickets.comments.author
 * @apiSuccess  {String}    tickets.comments.body
 * @apiSuccess  {Date}      tickets.comments.created
 * @apiSuccess  {Date}      tickets.comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    const options = {};
    if (req.body.service_id) {
      options.service = req.body.service_id;
    }

    Ticket.find(options)
      .then((tickets) => {
        res.body = tickets;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId Get specific ticket
 * @apiName GetTicketById
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam    {String}    ticketId
 *
 * @apiSuccess  {String}    service
 * @apiSuccess  {Date}      created
 * @apiSuccess  {Date}      updated
 * @apiSuccess  {Date}      completed
 * @apiSuccess  {Object}    details
 * @apiSuccess  {Object[]}  comments
 * @apiSuccess  {String}    comments.author
 * @apiSuccess  {String}    comments.body
 * @apiSuccess  {Date}      comments.created
 * @apiSuccess  {Date}      comments.updated
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets Create a ticket
 * @apiName PostTickets
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 * @apiDescription Registered services will post errors information to this resource. All
 * parameters passed into the request body will be recorded.
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission service
 */
router.post('/', (req, res, next) => {
  const credentials = basicAuth(req);
  if (!credentials || !credentials.name || !credentials.pass) {
    next(new Errors.Unauthorized());
  } else {
    Service.findById(credentials.name)
      .then(service => service.authorize(credentials.pass))
      .then(() => {
        const currentTime = new Date();
        const ticket = new Ticket({
          created: currentTime,
          updated: currentTime,
          service: credentials.name,
          details: req.body,
        });
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId Close a ticket
 * @apiName PostTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        ticket.completed = new Date();
        return ticket.save();
      })
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId Delete a ticket
 * @apiName DeleteTicketsId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.delete('/:ticketId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then(ticket => ticket.remove())
      .then(() => {
        res.status(204);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments Get all ticket comments
 * @apiName GetTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId
 *
 * @apiSuccess  {Object[]}
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 */
router.get('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .populate(['comments.author'])
      .then((ticket) => {
        res.body = ticket.comments;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {post} /tickets/:ticketId/comments Comment on a ticket
 * @apiName PostTicketsIdComment
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiSuccess  {String}  _id     Comment ID
 * @apiSuccess  {String}  body    Comment contents
 * @apiSuccess  {Date}    created Creation time
 * @apiSuccess  {Date}    updated Update time
 * @apiSuccess  {String}  author  Comment's author
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.post('/:ticketId/comments', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else if (!req.body.message) {
    next(new Errors.Generic('Message required', 400));
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = {
          author: req.session.user._id,
          body: req.body.message,
        };
        ticket.comments.push(comment);
        return ticket.save()
          .then(() => Promise.resolve(ticket.comments.pop()));
      })
      .then((comment) => {
        res.body = comment;
        next();
      })
      .catch(next);
  }
});

/**
 * @api {get} /tickets/:ticketId/comments/:commentId Get a ticket comment
 * @apiName GetTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 *
 * @apiSuccess  {Object}  author          Author data
 * @apiSuccess  {String}  author._id      ID of author
 * @apiSuccess  {String}  author.name     Name of author
 * @apiSuccess  {String}  author.picture  Picture of author
 * @apiSuccess  {String}  _id             ID of comment
 * @apiSuccess  {String}  body            Body content of comment
 * @apiSuccess  {Date}    created         Comment creation date
 * @apiSuccess  {Date}    updated         Comment update date
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.get('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {put} /tickets/:ticketId/comments/:commentId Update a ticket comment
 * @apiName PutTicketsIdCommentId
 * @apiGroup Tickets
 * @apiVersion 0.1.0
 *
 * @apiParam  {String}  ticketId  ID of the ticket
 * @apiParam  {String}  commentId ID of the comment
 * @apiParam  {String}  message   Message contents for comment
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */
router.put('/:ticketId/comments/:commentId', (req, res, next) => {
  if (!req.session.user) {
    next(new Errors.Unauthorized());
  } else {
    Ticket.findById(req.params.ticketId)
      .then((ticket) => {
        const comment = ticket.comments.id(req.params.commentId);
        if (!comment || comment.author.toString() !== req.session.user._id) {
          return Promise.reject(new Errors.Unauthorized());
        }
        comment.body = req.body.message;
        comment.updated = new Date();
        return ticket.save();
      })
      .then((ticket) => {
        res.body = ticket.comments.id(req.params.commentId);
        next();
      })
      .catch(next);
  }
});

/**
 * @api {delete} /tickets/:ticketId/comments/:commentId Delete a ticket comment
 * @apiName DeleteTicketsIdCommentId
 * @apiGroup Ticket
 * @apiVersion 0.1.0
 *
 * @apiUse UnauthorizedError
 *
 * @apiPermission user
 */

module.exports = router;

<|endoftext|>


<commit_before>requirejs.config({
    paths: {
        'text': '../Scripts/text',
        'durandal': '../Scripts/durandal',
        'plugins': '../Scripts/durandal/plugins',
        'transitions': '../Scripts/durandal/transitions'
    }
});

define('jquery', function () { return jQuery; });
define('knockout', ko);
define('moment', moment);

define(['durandal/system', 'durandal/app', 'durandal/viewLocator'], function (system, app, viewLocator) {
    system.debug(true);

    app.title = 'Brenton & Alana';

    app.configurePlugins({
        router: true,
        dialog: true,
        widget: true
    });

    app.start().then(function () {
        viewLocator.useConvention();
        app.setRoot('viewmodels/shell', 'entrance');
    });
})<commit_msg>File path change<commit_after>requirejs.config({
    paths: {
        'text': '../scripts/text',
        'durandal': '../scripts/durandal',
        'plugins': '../scripts/durandal/plugins',
        'transitions': '../scripts/durandal/transitions'
    }
});

define('jquery', function () { return jQuery; });
define('knockout', ko);
define('moment', moment);

define(['durandal/system', 'durandal/app', 'durandal/viewLocator'], function (system, app, viewLocator) {
    system.debug(true);

    app.title = 'Brenton & Alana';

    app.configurePlugins({
        router: true,
        dialog: true,
        widget: true
    });

    app.start().then(function () {
        viewLocator.useConvention();
        app.setRoot('viewmodels/shell', 'entrance');
    });
})<|endoftext|>


<commit_before>(function() {
  'use strict';

  var gulp = require('gulp');
  var fs   = require('fs');
  var git  = require('git-rev');
  var rjs  = require('requirejs');

  var overtake = require('overtake');

  var rimraf = require('gulp-rimraf');
  var less   = require('gulp-less');
  var mincss = require('gulp-minify-css');
  var rename = require('gulp-rename');

  var nodemon = require('gulp-nodemon');

  var minified = { suffix: '.min' };

  /**
   * Clean existing build files
   */
  gulp.task('clean', function() {
    return gulp.src([
      './build.json',
      './public/css',
      './public/js',
      './frame/css/*.min.css'
    ], { read: false })
    .pipe(rimraf());
  });

  /**
   * Optimize scripts from AMD modules and build a single compressed JS file
   */
  gulp.task('js', function(done) {
    rjs.optimize({
      baseUrl: './public/src/js',
      mainConfigFile: './public/src/js/require.config.js',

      name: 'main',
      out: './public/js/debuggerio.min.js',

      pragmas: { bustCache: true },
      findNestedDependencies: true,
      preserveLicenseComments: true,
      useStrict: true,

      exclude: ['promise']
    }, function() { done(); });
  });

  /**
   * Build LESS files for each theme into a single compressed CSS file
   */
  gulp.task('less', function() {
    return gulp.src('./public/src/less/debuggerio.*.less')
      .pipe(less())
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./public/css'));
  });

  /**
   * Compress static CSS files
   */
  gulp.task('css', function() {
    return gulp.src(['./frame/css/*.css', '!./**/*.min.css'])
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./frame/css'));
  });

  /**
   * Write a `build.json` file with build information
   */
  gulp.task('rev', function(done) {
    git.short(function(rev) {
      fs.writeFile('./build.json', JSON.stringify({
        date: (new Date()).toISOString(),
        rev: rev
      }), function() { done(); });
    });
  });

  /**
   * Watch for changes in JS and LESS files
   */
  gulp.task('watch', ['default'], function() {
    gulp.watch('./public/src/js/*.js', ['js']);
    gulp.watch('./public/src/less/*.less', ['less']);
  });

  /**
   * Keep watching and restart
   */
  gulp.task('dev', ['watch'], function(done) {
    nodemon({ script: 'debuggerio.js', ext: 'js json less' }).on('quit', done);
  });

  /**
   * Check for CDN updates
   */
  gulp.task('cdn', function(done) {
    var filenames = [];
    overtake.opts.update_msg = '  [%s] [%s] [%s] %s => %s';

    gulp.src([
      '!node_modules/**',
      '!./**/*.min.*',
      './**/*.js',
      './**/*.less',
      './**/*.html'
    ], { read: false })
    .on('data', function(file) {
      filenames.push(file.path);
    }).on('end', function() {
      overtake.check_files(filenames, true).done(function() {
        done();
      });
    });
  });

  /**
   * Default task
   */
  gulp.task('default', ['clean'], function() {
    return gulp.start('js', 'less', 'css', 'rev');
  });

})();
<commit_msg>Pretty-print build.json<commit_after>(function() {
  'use strict';

  var gulp = require('gulp');
  var fs   = require('fs');
  var git  = require('git-rev');
  var rjs  = require('requirejs');

  var overtake = require('overtake');

  var rimraf = require('gulp-rimraf');
  var less   = require('gulp-less');
  var mincss = require('gulp-minify-css');
  var rename = require('gulp-rename');

  var nodemon = require('gulp-nodemon');

  var minified = { suffix: '.min' };

  /**
   * Clean existing build files
   */
  gulp.task('clean', function() {
    return gulp.src([
      './build.json',
      './public/css',
      './public/js',
      './frame/css/*.min.css'
    ], { read: false })
    .pipe(rimraf());
  });

  /**
   * Optimize scripts from AMD modules and build a single compressed JS file
   */
  gulp.task('js', function(done) {
    rjs.optimize({
      baseUrl: './public/src/js',
      mainConfigFile: './public/src/js/require.config.js',

      name: 'main',
      out: './public/js/debuggerio.min.js',

      pragmas: { bustCache: true },
      findNestedDependencies: true,
      preserveLicenseComments: true,
      useStrict: true,

      exclude: ['promise']
    }, function() { done(); });
  });

  /**
   * Build LESS files for each theme into a single compressed CSS file
   */
  gulp.task('less', function() {
    return gulp.src('./public/src/less/debuggerio.*.less')
      .pipe(less())
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./public/css'));
  });

  /**
   * Compress static CSS files
   */
  gulp.task('css', function() {
    return gulp.src(['./frame/css/*.css', '!./**/*.min.css'])
      .pipe(mincss())
      .pipe(rename(minified))
      .pipe(gulp.dest('./frame/css'));
  });

  /**
   * Write a `build.json` file with build information
   */
  gulp.task('rev', function(done) {
    git.short(function(rev) {
      fs.writeFile('./build.json', JSON.stringify({
        date: (new Date()).toISOString(),
        rev: rev
      }, null, 2) + '\n', function() { done(); });
    });
  });

  /**
   * Watch for changes in JS and LESS files
   */
  gulp.task('watch', ['default'], function() {
    gulp.watch('./public/src/js/*.js', ['js']);
    gulp.watch('./public/src/less/*.less', ['less']);
  });

  /**
   * Keep watching and restart
   */
  gulp.task('dev', ['watch'], function(done) {
    nodemon({ script: 'debuggerio.js', ext: 'js json less' }).on('quit', done);
  });

  /**
   * Check for CDN updates
   */
  gulp.task('cdn', function(done) {
    var filenames = [];
    overtake.opts.update_msg = '  [%s] [%s] [%s] %s => %s';

    gulp.src([
      '!node_modules/**',
      '!./**/*.min.*',
      './**/*.js',
      './**/*.less',
      './**/*.html'
    ], { read: false })
    .on('data', function(file) {
      filenames.push(file.path);
    }).on('end', function() {
      overtake.check_files(filenames, true).done(function() {
        done();
      });
    });
  });

  /**
   * Default task
   */
  gulp.task('default', ['clean'], function() {
    return gulp.start('js', 'less', 'css', 'rev');
  });

})();
<|endoftext|>


<commit_before>#! /usr/bin/env node
'use strict'

const pkg     = require('../package.json'),
      got     = require('got'),
      money   = require('money'),
      colors  = require('colors'),
      API     = 'https://api.fixer.io/latest'

// arguments
let argv   = process.argv.slice(2),
    amount = argv[0],
    from   = argv[1]

// version
if(argv.indexOf('--version') !== -1 || argv.indexOf('-v') !== -1) {
  console.log(pkg.version)
  return
}

// help
if(argv.indexOf('--help') !== -1 || argv.indexOf('-h') !== -1 || argv.length == 0) {
  console.log(`
    Usage
      $ moeda <amount> <currency>

    Some currency
      [ usd, eur, gbp, brl... ]

    Examples
      $ moeda 1 usd

    Result
                 Euro: 0.92
      Libra Esterlina: 0.82
       Real Brazilian: 3.15

      Conversion of USD 1
`)
  return
}

// default
console.log()
got(API, { json: true }).then(response => {
  money.base = response.body.base
  money.rates = response.body.rates

  let rates = ['USD', 'EUR', 'GBP', 'BRL'],
      names = [
        '      Dollar EUA:',
        '            Euro:',
        ' Libra Esterlina:',
        '  Real Brazilian:'
      ]

  rates.map((item, index) => {
    if(item != from.toUpperCase()) {

      console.log(` ${names[index].gray.italic} ${money.convert(amount, {
        from: from.toUpperCase(),
        to: item
      }).toFixed(2).green.bold} `)

    }
  })

  console.log(`
    Conversion of ${from.toUpperCase()} ${amount}
    `.italic.gray)
})
<commit_msg>check if has internet connection<commit_after>#! /usr/bin/env node
'use strict'

const pkg     = require('../package.json'),
      got     = require('got'),
      money   = require('money'),
      colors  = require('colors'),
      API     = 'https://api.fixer.io/latest'

// arguments
let argv   = process.argv.slice(2),
    amount = argv[0],
    from   = argv[1]

// version
if(argv.indexOf('--version') !== -1 || argv.indexOf('-v') !== -1) {
  console.log(pkg.version)
  process.exit(1)

}

// help
if(argv.indexOf('--help') !== -1 || argv.indexOf('-h') !== -1 || argv.length == 0) {
  console.log(`
    Usage
      $ moeda <amount> <currency>

    Some currency
      [ usd, eur, gbp, brl... ]

    Examples
      $ moeda 1 usd

    Result
                 Euro: 0.92
      Libra Esterlina: 0.82
       Real Brazilian: 3.15

      Conversion of USD 1
  `)
  process.exit(1)

}

// default
console.log()
got(API, { json: true }).then(response => {
  money.base = response.body.base
  money.rates = response.body.rates

  let rates = ['USD', 'EUR', 'GBP', 'BRL'],
      names = [
        '      Dollar EUA:',
        '            Euro:',
        ' Libra Esterlina:',
        '  Real Brazilian:'
      ]

  rates.map((item, index) => {
    if(item != from.toUpperCase()) {
      console.log(` ${names[index].gray.italic} ${money.convert(amount, {
        from: from.toUpperCase(),
        to: item
      }).toFixed(2).green.bold} `)

    }
  })

  console.log(`
    Conversion of ${from.toUpperCase()} ${amount}
    `.italic.gray)
  process.exit(1)

}).catch(error => {
  if(error.code === 'ENOTFOUND') {
    console.log('   Please check your internet connection.\n'.red)

  }else {
    console.log('   Internal server error... \n'.red)

  }
  process.exit(1)

})
<|endoftext|>


<commit_before>import React, { Component } from "react";
import moment from "moment";
import reactMixin from "react-mixin";
import ReactFire from "reactfire";
import classNames from "classnames";

import Day from "./Day";

class Week extends Component {
  constructor(props) {
    super(props);

    this.state = {
      focusedDay: null,
      todos: [],
    };

    this.bindFirebase = this.bindFirebase.bind(this);
    this.renderDays = this.renderDays.bind(this);
    this.focusDay = this.focusDay.bind(this);
    this.scrollToDay = this.scrollToDay.bind(this);
  }

  componentWillMount() {
    this.bindFirebase(this.props.firebaseRef, this.props.targetDay);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.targetDay !== this.props.targetDay) {
      this.unbind("todos");
      this.bindFirebase(nextProps.firebaseRef, nextProps.targetDay);
    }
  }

  componentDidMount() {
    this.scrollToDay(this.props.targetDay);
  }

  componentDidUpdate() {
    this.scrollToDay(this.props.targetDay);
  }

  bindFirebase(firebaseRef, targetDay) {
    // console.log("current target is " + moment(targetDay).format("ddd DD MM HH:mm"));
    // console.log("new target is " + moment(this.props.targetDay).format("ddd DD MM HH:mm"));
    // console.log("Start at " + moment(targetDay).startOf("isoweek").subtract(7, "days").format("ddd DD MM HH:mm"));
    // console.log("Stop at " + moment(targetDay).startOf("isoweek").add(14, "days").format("ddd DD MM HH:mm"));

    this.bindAsArray(
      firebaseRef
        .orderByChild("date")
        .startAt(moment(targetDay).startOf("isoweek").subtract(7, "days").valueOf())
        .endAt(moment(targetDay).startOf("isoweek").add(15, "days").valueOf()),
      "todos",
      function(error) {
        console.log("Firebase subscription cancelled:")
        console.log(error);
        this.setState({todos: []})
      }.bind(this)
    );
  }

  renderDays(week, today, number) {
    return week.map(function(day) {
      let firebaseKey;
      let text = "";

      this.state.todos.forEach(function(todo) {
        if (todo.date === day.valueOf()) {
          firebaseKey = todo[".key"];
          text = todo.text;
        }
      });

      return (
        <Day
          ref={(c) => this[day.valueOf()] = c}
          key={day.valueOf()}
          day={day}
          firebaseKey={firebaseKey}
          text={text}
          today={today}
          colorNumber={number}
          isFirstWeek={number === 1}
          isToday={day.valueOf() === this.props.today.valueOf()}
          isTargetDay={day.valueOf() === this.props.targetDay.valueOf()}
          aDayIsFocused={this.state.focusedDay}
          isFocusedWeekDay={this.state.focusedDay && moment(this.state.focusedDay).day() === day.day()}
          isFocusedDay={moment(this.state.focusedDay).isSame(day)}
          focusDay={this.focusDay}
          saveTodo={this.props.saveTodo}
        />
      );
    }.bind(this));
  }

  focusDay(day) {
    this.setState({focusedDay: day});
    if (day) {
      this.scrollToDay(day);
    }
  }

  scrollToDay(day) {
    const scroller = this.weekScroller;
    const textarea = this[day.valueOf()].textarea.getBoundingClientRect();
    const windowWidth = window.innerWidth;
    scroller.scrollLeft += textarea.left - (windowWidth * 0.5) + (textarea.width * 0.5);
  }

  render() {
    const targetDay = this.props.targetDay;
    const firstOfThisWeek = moment(targetDay).startOf("isoweek");
    const firstOfLastWeek = moment(firstOfThisWeek).subtract(7, "days");
    const firstOfNextWeek = moment(firstOfThisWeek).add(7, "days");

    let lastWeek = {number: 1, days: []};
    let thisWeek = {number: 2, days: []};
    let nextWeek = {number: 3, days: []};

    for (let i = 0; i < 7; i++) {
      lastWeek.days.push(moment(firstOfLastWeek).add(i, "days"));
    }

    for (let i = 0; i < 7; i++) {
      thisWeek.days.push(moment(firstOfThisWeek).add(i, "days"));
    }

    for (let i = 0; i < 7; i++) {
      nextWeek.days.push(moment(firstOfNextWeek).add(i, "days"));
    }

    const weeks = [lastWeek, thisWeek, nextWeek].map(function(week) {
      const isFocusedWeek = (
        this.state.focusedDay &&
        moment(this.state.focusedDay).isBetween(week.days[0], week.days[6], null, "[]")
      );

      return (
        <div
          key={week.number}
          className={classNames({
            "week flex even-children": true,
            "focused-week": isFocusedWeek,
            "unfocused-week": this.state.focusedDay && !isFocusedWeek,
            "this-week": week.number === 2,
          })}
        >
          {this.renderDays(week.days, this.props.today, week.number)}
        </div>
      );
    }.bind(this));

    return (
      <div
        ref={(c) => this.weekScroller = c}
        className="week-scroller faint-bottom-border grow flex vertical even-children overflow-auto"
      >
        {weeks}
      </div>
    );
  }
}

reactMixin(Week.prototype, ReactFire);
export default Week;
<commit_msg>Fixes focusToDay<commit_after>import React, { Component } from "react";
import moment from "moment";
import reactMixin from "react-mixin";
import ReactFire from "reactfire";
import classNames from "classnames";

import Day from "./Day";

class Week extends Component {
  constructor(props) {
    super(props);

    this.state = {
      focusedDay: null,
      todos: [],
    };

    this.bindFirebase = this.bindFirebase.bind(this);
    this.renderDays = this.renderDays.bind(this);
    this.focusDay = this.focusDay.bind(this);
    this.scrollToDay = this.scrollToDay.bind(this);
  }

  componentWillMount() {
    this.bindFirebase(this.props.firebaseRef, this.props.targetDay);
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.targetDay !== this.props.targetDay) {
      this.unbind("todos");
      this.bindFirebase(nextProps.firebaseRef, nextProps.targetDay);
      this.setState({focusedDay: null});
    }
  }

  componentDidMount() {
    this.scrollToDay(this.props.targetDay);
  }

  componentDidUpdate() {
    if (this.state.focusedDay) {
      this.scrollToDay(this.state.focusedDay);
    }
    else {
      this.scrollToDay(this.props.targetDay);
    }
  }

  bindFirebase(firebaseRef, targetDay) {
    // console.log("current target is " + moment(targetDay).format("ddd DD MM HH:mm"));
    // console.log("new target is " + moment(this.props.targetDay).format("ddd DD MM HH:mm"));
    // console.log("Start at " + moment(targetDay).startOf("isoweek").subtract(7, "days").format("ddd DD MM HH:mm"));
    // console.log("Stop at " + moment(targetDay).startOf("isoweek").add(14, "days").format("ddd DD MM HH:mm"));

    this.bindAsArray(
      firebaseRef
        .orderByChild("date")
        .startAt(moment(targetDay).startOf("isoweek").subtract(7, "days").valueOf())
        .endAt(moment(targetDay).startOf("isoweek").add(15, "days").valueOf()),
      "todos",
      function(error) {
        console.log("Firebase subscription cancelled:")
        console.log(error);
        this.setState({todos: []})
      }.bind(this)
    );
  }

  renderDays(week, today, number) {
    return week.map(function(day) {
      let firebaseKey;
      let text = "";

      this.state.todos.forEach(function(todo) {
        if (todo.date === day.valueOf()) {
          firebaseKey = todo[".key"];
          text = todo.text;
        }
      });

      return (
        <Day
          ref={(c) => this[day.valueOf()] = c}
          key={day.valueOf()}
          day={day}
          firebaseKey={firebaseKey}
          text={text}
          today={today}
          colorNumber={number}
          isFirstWeek={number === 1}
          isToday={day.valueOf() === this.props.today.valueOf()}
          isTargetDay={day.valueOf() === this.props.targetDay.valueOf()}
          aDayIsFocused={this.state.focusedDay}
          isFocusedWeekDay={this.state.focusedDay && moment(this.state.focusedDay).day() === day.day()}
          isFocusedDay={moment(this.state.focusedDay).isSame(day)}
          focusDay={this.focusDay}
          saveTodo={this.props.saveTodo}
        />
      );
    }.bind(this));
  }

  focusDay(day) {
    this.setState({focusedDay: day});
  }

  scrollToDay(day) {
    const scroller = this.weekScroller;
    const textarea = this[day.valueOf()].textarea.getBoundingClientRect();
    const windowWidth = window.innerWidth;
    scroller.scrollLeft += textarea.left - (windowWidth * 0.5) + (textarea.width * 0.5);
  }

  render() {
    const targetDay = this.props.targetDay;
    const firstOfThisWeek = moment(targetDay).startOf("isoweek");
    const firstOfLastWeek = moment(firstOfThisWeek).subtract(7, "days");
    const firstOfNextWeek = moment(firstOfThisWeek).add(7, "days");

    let lastWeek = {number: 1, days: []};
    let thisWeek = {number: 2, days: []};
    let nextWeek = {number: 3, days: []};

    for (let i = 0; i < 7; i++) {
      lastWeek.days.push(moment(firstOfLastWeek).add(i, "days"));
    }

    for (let i = 0; i < 7; i++) {
      thisWeek.days.push(moment(firstOfThisWeek).add(i, "days"));
    }

    for (let i = 0; i < 7; i++) {
      nextWeek.days.push(moment(firstOfNextWeek).add(i, "days"));
    }

    const weeks = [lastWeek, thisWeek, nextWeek].map(function(week) {
      const isFocusedWeek = (
        this.state.focusedDay &&
        moment(this.state.focusedDay).isBetween(week.days[0], week.days[6], null, "[]")
      );

      return (
        <div
          key={week.number}
          className={classNames({
            "week flex even-children": true,
            "focused-week": isFocusedWeek,
            "unfocused-week": this.state.focusedDay && !isFocusedWeek,
            "this-week": week.number === 2,
          })}
        >
          {this.renderDays(week.days, this.props.today, week.number)}
        </div>
      );
    }.bind(this));

    return (
      <div
        ref={(c) => this.weekScroller = c}
        className="week-scroller faint-bottom-border grow flex vertical even-children overflow-auto"
      >
        {weeks}
      </div>
    );
  }
}

reactMixin(Week.prototype, ReactFire);
export default Week;
<|endoftext|>


<commit_before>var parallel = require('run-parallel');
var path = require('path');
var readJSON = require('../read-json');
var jsonDiff = require('json-diff');
var colorize = require('json-diff/lib/colorize');
var exec = require('child_process').exec;
var jsonParse = require('safe-json-parse');

/*jshint camelcase: false*/
function purgeDeps(opts, diff, meta) {
    if (!diff) {
        return;
    }

    var depsKey = 'dependencies' in diff ?
        'dependencies' : 'dependencies__deleted' in diff ?
        'dependencies__deleted' : 'dependencies__added' in diff ?
        'dependencies__added' : null;
    if (!depsKey) {
        return diff;
    }

    var deps = diff[depsKey];

    diff[depsKey] = Object.keys(deps).reduce(function (acc, key) {
        var deleted = meta.deleted ? meta.deleted :
            (key.indexOf('__deleted') !== -1 ||
            depsKey === 'dependencies__deleted');
        var added = meta.added ? meta.added :
            (key.indexOf('__added') !== -1 ||
            depsKey === 'dependencies__added');
        if (deleted || added) {
            if (meta.depth >= opts.depth) {
                if (!opts.short) {
                    deps[key].dependencies = '[NestedObject]';
                    acc[key] = deps[key];
                } else {
                    acc[key] = (deleted ? '[Deleted' : '[Added') +
                        '@' + deps[key].version + ']';
                }
            } else {
                acc[key] = purgeDeps(opts, deps[key], {
                    depth: meta.depth + 1,
                    added: added,
                    deleted: deleted
                });
            }
        } else {
            acc[key] = purgeDeps(opts, deps[key], {
                depth: meta.depth + 1
            });
        }

        return acc;
    }, {});

    return diff;
}

function diffContent(oldContent, newContent, opts) {
    var diff = jsonDiff.diff(oldContent, newContent);

    diff = purgeDeps(opts, diff, {
        depth: 0
    });

    return colorize.colorize(diff, {
        color: opts.color
    });
}

function gitShow(sha, cwd, callback) {
    function ongit(err, stdout, stderr) {
        if (stderr) {
            console.error(stderr);
        }

        if (err && err.message.indexOf('not in \'HEAD\'') !== -1) {
            return callback(null, {});
        }

        if (err) {
            return callback(err);
        }

        jsonParse(stdout, callback);
    }

    exec('git show ' + sha + ':npm-shrinkwrap.json', {
        cwd: cwd || process.cwd(),
        maxBuffer: 10000 * 1024
    }, ongit);
}

function isFile(fileName) {
    var index = fileName.indexOf('.json');

    return index !== -1 && index === fileName.length - 5;
}

function main(opts, callback) {
    var fileA = opts._[0];
    var fileB = opts._[1];

    if (!fileB) {
        fileB = 'npm-shrinkwrap.json';
    }

    if (!fileA) {
        fileA = 'HEAD';
    }

    if (!("color" in opts)) {
        opts.color = process.stdout.isTTY;
    } else if (opts.color === "false") {
        opts.color = false;
    }

    if (!("short" in opts)) {
        opts.short = true;
    }

    opts.depth = 'depth' in opts ? opts.depth : 0;
    var cwd = opts.dirname || process.cwd();

    parallel([
        isFile(fileA) ?
            readJSON.bind(null, path.resolved(cwd, fileA)) :
            gitShow.bind(null, fileA, cwd),
        isFile(fileB) ?
            readJSON.bind(null, path.resolve(cwd, fileB)) :
            gitShow.bind(null, fileB, cwd)
    ], function (err, files) {
        if (err) {
            return callback(err);
        }

        callback(null, diffContent(files[0], files[1], opts));
    });
}

module.exports = main;
<commit_msg>Fixed npm-shrinkwrap diff when file not at repository root.<commit_after>var parallel = require('run-parallel');
var path = require('path');
var readJSON = require('../read-json');
var jsonDiff = require('json-diff');
var colorize = require('json-diff/lib/colorize');
var exec = require('child_process').exec;
var jsonParse = require('safe-json-parse');

/*jshint camelcase: false*/
function purgeDeps(opts, diff, meta) {
    if (!diff) {
        return;
    }

    var depsKey = 'dependencies' in diff ?
        'dependencies' : 'dependencies__deleted' in diff ?
        'dependencies__deleted' : 'dependencies__added' in diff ?
        'dependencies__added' : null;
    if (!depsKey) {
        return diff;
    }

    var deps = diff[depsKey];

    diff[depsKey] = Object.keys(deps).reduce(function (acc, key) {
        var deleted = meta.deleted ? meta.deleted :
            (key.indexOf('__deleted') !== -1 ||
            depsKey === 'dependencies__deleted');
        var added = meta.added ? meta.added :
            (key.indexOf('__added') !== -1 ||
            depsKey === 'dependencies__added');
        if (deleted || added) {
            if (meta.depth >= opts.depth) {
                if (!opts.short) {
                    deps[key].dependencies = '[NestedObject]';
                    acc[key] = deps[key];
                } else {
                    acc[key] = (deleted ? '[Deleted' : '[Added') +
                        '@' + deps[key].version + ']';
                }
            } else {
                acc[key] = purgeDeps(opts, deps[key], {
                    depth: meta.depth + 1,
                    added: added,
                    deleted: deleted
                });
            }
        } else {
            acc[key] = purgeDeps(opts, deps[key], {
                depth: meta.depth + 1
            });
        }

        return acc;
    }, {});

    return diff;
}

function diffContent(oldContent, newContent, opts) {
    var diff = jsonDiff.diff(oldContent, newContent);

    diff = purgeDeps(opts, diff, {
        depth: 0
    });

    return colorize.colorize(diff, {
        color: opts.color
    });
}

function gitShow(sha, cwd, callback) {
    function ongit(err, stdout, stderr) {
        if (stderr) {
            console.error(stderr);
        }

        if (err && err.message.indexOf('not in \'HEAD\'') !== -1) {
            return callback(null, {});
        }

        if (err) {
            return callback(err);
        }

        jsonParse(stdout, callback);
    }

    exec('git show ' + sha + ':./npm-shrinkwrap.json', {
        cwd: cwd || process.cwd(),
        maxBuffer: 10000 * 1024
    }, ongit);
}

function isFile(fileName) {
    var index = fileName.indexOf('.json');

    return index !== -1 && index === fileName.length - 5;
}

function main(opts, callback) {
    var fileA = opts._[0];
    var fileB = opts._[1];

    if (!fileB) {
        fileB = 'npm-shrinkwrap.json';
    }

    if (!fileA) {
        fileA = 'HEAD';
    }

    if (!("color" in opts)) {
        opts.color = process.stdout.isTTY;
    } else if (opts.color === "false") {
        opts.color = false;
    }

    if (!("short" in opts)) {
        opts.short = true;
    }

    opts.depth = 'depth' in opts ? opts.depth : 0;
    var cwd = opts.dirname || process.cwd();

    parallel([
        isFile(fileA) ?
            readJSON.bind(null, path.resolved(cwd, fileA)) :
            gitShow.bind(null, fileA, cwd),
        isFile(fileB) ?
            readJSON.bind(null, path.resolve(cwd, fileB)) :
            gitShow.bind(null, fileB, cwd)
    ], function (err, files) {
        if (err) {
            return callback(err);
        }

        callback(null, diffContent(files[0], files[1], opts));
    });
}

module.exports = main;
<|endoftext|>


<commit_before>/**
 * Created by azu on 2014/06/10.
 * LICENSE : MIT
 */
"use strict";
function windowOnload() {
    require("./console-editor").initilize();
    require("./sync-toc").initilize();
    require("./bug-report").initilize();
}
window.addEventListener("DOMContentLoaded", windowOnload);<commit_msg>chore(js): fix onload<commit_after>/**
 * Created by azu on 2014/06/10.
 * LICENSE : MIT
 */
"use strict";
function windowOnload() {
    require("./console-editor").initilize();
    require("./sync-toc").initilize();
    require("./bug-report").initilize();
}
var readyState = document.readyState;
if (readyState == "interactive" || readyState === 'complete') {
    windowOnload();
} else {
    window.addEventListener("DOMContentLoaded", windowOnload);
}<|endoftext|>


<commit_before>// node modules
var Path = nodeRequire('path');
var fs = nodeRequire('fs');
var os = nodeRequire('os');
var chokidar = nodeRequire('chokidar');
var rimdir = nodeRequire('rimraf');

// front-end modules
var Vue = require('vue');
var $ = require('jquery');
var _ = require('underscore');
var keybindings = require('./keybindings');
var Files = require('./files');
var menu = require('./menu');
var windowstate = require('./windowstate');
var updater = require('./updater');
var settings = require('./settings');
var modes = {
  p5: require('./modes/p5/p5-mode')
};

var appConfig = {

  el: '#app',

  mode: modes.p5,

  components: {
    editor: require('./editor/index'),
    sidebar: require('./sidebar/index'),
    settings: require('./settings/index'),
    debug: require('./debug/index'),
    tabs: require('./tabs/index')
  },

  data: {
    title: 'Untitled',
    projectPath: window.PATH,
    unsaved: window.UNSAVED ? true : false,
    windowURL: window.location.href,
    temp: true,
    running: false,
    focused: false,
    settings: {},
    showSettings: false,
    files: [],
    tabs: [],
    justSaved: false,
    askReload: false,
    fileTypes: ['txt', 'html', 'css', 'js', 'json', 'scss', 'xml', 'csv', 'less'],
    outX: 50,
    outY: 50
  },

  computed: {
    projectName: function() {
      return Path.basename(this.projectPath);
    },

    orientation: function(){
     var orientation = this.settings.consoleOrientation;
     return orientation;
    }
  },

  ready: function() {
    windowstate.incrementWindows();
    // this.modeFunction('update');
    updater.check();
    keybindings.setup(this);
    menu.setup(this);

    this.setupFileListener();
    this.setupCloseHandler();
    this.setupDragListener();
    this.setupSettings();

    if (this.projectPath) {
      if (!this.unsaved) this.temp = false;
      var filename = null;

      if (fs.lstatSync(this.projectPath).isFile()) {
        // keep the name of the file to be opened
        filename = this.projectPath;

        // set the projectPath to the enclosing folder
        this.projectPath = Path.dirname(this.projectPath);
      }

      if (window.FILEPATH && fs.lstatSync(window.FILEPATH).isFile()) {
        filename = window.FILEPATH;
      }

      // load the project and open the selected file
      var self = this;
      this.loadProject(this.projectPath, function(){
        if (filename) {
          if (Path.dirname(filename) === self.projectPath) {
            self.openFile(filename);
          } else {
            self.$broadcast('open-nested-file', filename);
          }
        }
        gui.Window.get().show();
      });

      menu.updateRecentFiles(this, this.projectPath);
    } else {
      // if we don't have a project path global, create a new project
      this.modeFunction('newProject');
      menu.updateRecentFiles(this);
    }
    var win = gui.Window.get();
    win.setMinimumSize(400,400);
    if(($(window).width() + 100 >= screen.width) || ($(window).height() + 100 >= screen.height)){
      win.resizeTo((screen.width * 4)/5,(screen.height * 7)/8);
    }
  },

  methods: {
    //runs a function named func in the mode file currently being used
    modeFunction: function(func, args) {
      var mode = this.$options.mode;
      if (typeof mode[func] === 'function') {
        // make args an array if it isn't already
        // typeof args won't work because it returns 'object'
        if (Object.prototype.toString.call(args) !== '[object Array]') {
          args = [args];
        }
        mode[func].apply(this, args);
      }
    },

    setupSettings: function() {
      this.settings = settings.load();
      this.$watch('settings', function(value){
        this.$broadcast('settings-changed', value);
        settings.save(value);
      });
    },

    // use jquery to handle file changes
    // (I should move this over to vuejs but it wasn't dealing
    // with the html file element properly)
    setupFileListener: function() {
      $('#openFile').change(this.open.bind(this));
      $('#saveFile').change(this.saveAs.bind(this));
      $('#saveProject').change(this.saveProjectAs.bind(this));
    },

    setupCloseHandler: function() {
      var self = this;
      var win = gui.Window.get();
      win.on('close', function(closeEvent){
        // check to see if there are unsaved files
        var shouldClose = true;
        if (_.any(self.files, function(f) { return f.contents != f.lastSavedContents; })) {
          shouldClose = confirm('You have unsaved files. Quit and lose changes?');
        }
        if (shouldClose) {
          // clean up output window
          if (self.outputWindow) {
            self.outputWindow.close(true);
            self.outputWindow = null;
          }

          // save window state if the user quit the program
          if (closeEvent === 'quit') {
            windowstate.save(self, win);
          }

          windowstate.decrementWindows();
          if (windowstate.totalWindows() === 0) {
            gui.App.quit();
          }

          // close this window
          this.close(true);
          win = null;
        }
      });

      win.on('focus', function(){
        self.focused = true;
        self.resetMenu();
        if (self.askReload) {
          self.askReload = false;
          var shouldRefresh = confirm(self.currentFile.path + ' was edited on the disk. Reload? You will lose any changes.');
          if (shouldRefresh) {

            var win = self.newWindow(self.windowURL);
            windowstate.decrementWindows();

            win.on('document-start', function(){
              win.window.PATH = self.currentFile.path;
              gui.Window.get().close(true);
            });
          }
        }
      });

      win.on('blur', function(){
        self.focused = false;
      });
    },

    // todo: setup drag and drop
    setupDragListener: function() {
      var self = this;
      window.ondragover = function(e) { e.preventDefault(); return false };
      window.ondrop = function(e) {
        e.preventDefault();
        if (e.dataTransfer.files[0]) {
          var path = e.dataTransfer.files[0].path;
          self.openProject(path);
        }
        return false
      };
    },

    // create a new window 50px below current window
    newWindow: function(url, options) {
      var currentWindow = gui.Window.get();

      var win = gui.Window.open(url, _.extend({
        x: currentWindow.x + 50,
        y: currentWindow.y + 50,
        width: 1024,
        height: 768,
        toolbar: false,
        focus: true,
        show: false
      }, options));
      return win;
    },

    // open an existing project with a new window
    open: function(event) {
      var path = event.target.files[0].path;
      this.openProject(path);
      // reset value in case the user wants to open the same file more than once
      $('#openFile').val('');
    },

    openProject: function(path, temp) {
      // create the new window
      var win = this.newWindow(this.windowURL);

      // set the project path of the new window
      win.on('document-start', function(){
        if (fs.lstatSync(path).isDirectory()) {
          var sketchPath = Path.join(path, 'sketch.js');
          if (fs.existsSync(sketchPath)) path = sketchPath;
        }
        win.window.PATH = path;
        if (typeof temp === 'boolean' && temp === true) {
          win.window.UNSAVED = true;
        }
      });
      return win;
    },

    // load project files
    loadProject: function(path, callback) {
      var self = this;
      Files.list(path, function(files){
        self.files = files;
        self.watch(path);
        if (typeof callback === 'function') callback();
      });
    },

    resetMenu: function(){
      menu.resetMenu();
    },

    // watch the project file tree for changes
    watch: function(path) {
      var self = this;

      // don't watch recursively
      var watcher = chokidar.watch(path, {
        ignoreInitial: true,
        ignored: function(filepath) {
          var regex = new RegExp(path + '\/.*\/.+');
          return regex.test(filepath);
        }
      });

      watcher.on('add', function(path) {
        var f = Files.setup(path);
        Files.addToTree(f, self.files, self.projectPath);
      }).on('addDir', function(path) {
        var f = Files.setup(path, {type: 'folder', children: []});
        Files.addToTree(f, self.files, self.projectPath);
      }).on('unlink', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('unlinkDir', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('change', function(path) {
        if (self.justSaved) {
          self.justSaved = false;
        } else {
          if (!self.temp) self.askReload = true;
        }
      });
    },

    // close the window, checking for unsaved file changes
    closeProject: function() {
      if (this.focused) {
        if (this.outputWindow) {
          this.outputWindow.close(true);
          this.outputWindow = null;
        }
        gui.Window.get().close();
      } else {
        if (this.outputWindow) {
          this.toggleRun();
        }
      }
    },

    // save all open files
    saveAll: function() {
      _.where(this.files, {type: 'file', open: true}).forEach(function(file) {
        if (file.lastSavedContents != file.contents) {
          fs.writeFileSync(file.path, file.contents, "utf8");
          file.lastSavedContents = file.contents;
        }
      });
      menu.updateRecentFiles(this, this.projectPath);
      this.justSaved = true;
    },

    saveAs: function(event) {
      // capture the filename selected by the user
      var file = event.target.files[0].path;

      if (this.temp) {
        // mode specific action
        this.modeFunction('saveAs', file);
        menu.updateRecentFiles(this, this.projectPath);
      } else {
        // save a file
        // if the we are saving inside the project path just open the new file
        // otherwise open a new window
        fs.writeFileSync(file, this.currentFile.contents, "utf8");
        if ((Path.dirname(file) + '/').indexOf(this.projectPath + '/') > -1) {
          var f = Files.setup(file);
          Files.addToTree(f, this.files, this.projectPath);
          this.openFile(file);
        } else {
          this.openProject(file);
        }
      }

      // reset value in case the user wants to save the same filename more than once
      $('#saveFile').val('');
    },

    saveProjectAs: function(event) {
      var path = event.target.files[0].path;
      var self = this;
      fs.exists(path, function(existing){
        if (existing) {
          rimdir(path, function(error){
            if (error) throw error;
            self.modeFunction('saveAs', path);
          });
        } else {
          self.modeFunction('saveAs', path);
        }
      });
    },

    saveFile: function() {
      // if this is a new project then trigger a save-as
      if (this.temp) {
        $('#saveFile').trigger('click');
      } else {
        // otherwise just write the current file
        this.writeFile();
      }
    },

    saveFileAs: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Save file as:', originalName);
      if (!newName || newName === originalName) return false;

      var self = this;
      var filename = Path.join(Path.dirname(path), newName);
      fs.writeFile(filename, this.currentFile.contents, 'utf8', function(err){
        var f = Files.setup(filename);
        self.openFile(filename);
      });
    },

    writeFile: function() {
      var self = this;

      self.justSaved = true;

      fs.writeFileSync(this.currentFile.path, this.currentFile.contents, "utf8");
      this.currentFile.lastSavedContents = this.currentFile.contents;

    },

    // open up a file - read its contents if it's not already opened
    openFile: function(path, callback) {
      var self = this;
      var re = /(?:\.([^.]+))?$/;
      var ext = re.exec(path)[1];

      var file = Files.find(this.files, path);
      if (!file) return false;
      if (self.fileTypes.indexOf(ext) < 0) {
        window.alert("Unsupported file type. Types we can edit:\n" + self.fileTypes.toString());
      } else {
        if (file.open) {
          this.title = file.name;
          this.currentFile = file;
          this.$broadcast('open-file', this.currentFile);
        } else {
          fs.readFile(path, 'utf8', function(err, fileContents) {
            if (err) throw err;
            file.contents = file.originalContents = file.lastSavedContents = fileContents;
            file.open = true;
            self.title = file.name;
            self.currentFile = file;
            self.$broadcast('open-file', self.currentFile);
            self.$broadcast('add-tab', self.currentFile,self.tabs);

            if (typeof callback === 'function') callback(file);
          });
        }
      }
    },

    closeFile: function(path){
        // check to see if there are unsaved files
         var file = Files.find(this.files, path);
        if (!file) return false;

        if(this.tabs.length==1){
            var win = gui.Window.get();
            win.close();
        }
        var shouldClose = true;
        var win = gui.Window.get();
        if (file.contents != file.lastSavedContents){
          shouldClose = confirm('You have unsaved changes. Close file and lose changes?');
        }
        if (shouldClose) {
          file.open = false;
          file.contents = file.lastSavedContents;
          this.$broadcast('close-file', file);
          return true;
        }
        return false;
    },

    // create a new file and save it in the project path
    newFile: function(basepath) {
      var title = prompt('Choose a file name and type: \nSupported types: ' + this.fileTypes.toString()).replace(/ /g,'');
      var dotSplit = title.split(".");
      var re = /(?:\.([^.]+))?$/;

      if (!title) return false;

      if (this.fileTypes.indexOf(re.exec(title)[1]) < 0 || (dotSplit.length > 2)){
        window.alert("unsupported/improper file type selected.\nAutomaticallly adding a .js extension");
        title = dotSplit[0] + '.js';
      }

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.writeFile(filename, '', 'utf8', function(err){
        var f = Files.setup(filename);
        Files.addToTree(f, self.files, self.projectPath);
        self.openFile(filename);
      });
    },

    newFolder: function(basepath) {
      var title = prompt('Folder name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.mkdir(filename);
    },

    renameFile: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Rename ' + originalName + ' to:', originalName);
      if (!newName) return false;

      fs.rename(path, Path.join(Path.dirname(path), newName));
    },

    run: function() {
      $('#debug').html('');
      this.modeFunction('run');
    },

    toggleRun: function() {
      if (this.running) {
        this.modeFunction('stop');
      } else {
        $('#debug').html('');
        this.modeFunction('run');
      }
    },

    changeFontSize: function(sz) {
      this.settings.fontSize = parseInt(this.settings.fontSize) + sz;
    },

    toggleSettingsPane: function() {
      this.showSettings = !this.showSettings;
    },

    toggleSidebar: function() {
      this.settings.showSidebar = !this.settings.showSidebar;
    },

    showHelp: function() {
      gui.Shell.openExternal(this.$options.mode.referenceURL);
    }

  }

};

var app = new Vue(appConfig);
<commit_msg>closes #153<commit_after>// node modules
var Path = nodeRequire('path');
var fs = nodeRequire('fs');
var os = nodeRequire('os');
var chokidar = nodeRequire('chokidar');
var rimdir = nodeRequire('rimraf');

// front-end modules
var Vue = require('vue');
var $ = require('jquery');
var _ = require('underscore');
var keybindings = require('./keybindings');
var Files = require('./files');
var menu = require('./menu');
var windowstate = require('./windowstate');
var updater = require('./updater');
var settings = require('./settings');
var modes = {
  p5: require('./modes/p5/p5-mode')
};

var appConfig = {

  el: '#app',

  mode: modes.p5,

  components: {
    editor: require('./editor/index'),
    sidebar: require('./sidebar/index'),
    settings: require('./settings/index'),
    debug: require('./debug/index'),
    tabs: require('./tabs/index')
  },

  data: {
    title: 'Untitled',
    projectPath: window.PATH,
    unsaved: window.UNSAVED ? true : false,
    windowURL: window.location.href,
    temp: true,
    running: false,
    focused: false,
    settings: {},
    showSettings: false,
    files: [],
    tabs: [],
    justSaved: false,
    askReload: false,
    fileTypes: ['txt', 'html', 'css', 'js', 'json', 'scss', 'xml', 'csv', 'less'],
    outX: 50,
    outY: 50
  },

  computed: {
    projectName: function() {
      return Path.basename(this.projectPath);
    },

    orientation: function(){
     var orientation = this.settings.consoleOrientation;
     return orientation;
    }
  },

  ready: function() {
    windowstate.incrementWindows();
    // this.modeFunction('update');
    updater.check();
    keybindings.setup(this);
    menu.setup(this);

    this.setupFileListener();
    this.setupCloseHandler();
    this.setupDragListener();
    this.setupSettings();

    if (this.projectPath) {
      if (!this.unsaved) this.temp = false;
      var filename = null;

      if (fs.lstatSync(this.projectPath).isFile()) {
        // keep the name of the file to be opened
        filename = this.projectPath;

        // set the projectPath to the enclosing folder
        this.projectPath = Path.dirname(this.projectPath);
      }

      if (window.FILEPATH && fs.lstatSync(window.FILEPATH).isFile()) {
        filename = window.FILEPATH;
      }

      // load the project and open the selected file
      var self = this;
      this.loadProject(this.projectPath, function(){
        if (filename) {
          if (Path.dirname(filename) === self.projectPath) {
            self.openFile(filename);
          } else {
            self.$broadcast('open-nested-file', filename);
          }
        }
        gui.Window.get().show();
      });

      menu.updateRecentFiles(this, this.projectPath);
    } else {
      // if we don't have a project path global, create a new project
      this.modeFunction('newProject');
      menu.updateRecentFiles(this);
    }
    var win = gui.Window.get();
    win.setMinimumSize(400,400);
    if(($(window).width() + 100 >= screen.width) || ($(window).height() + 100 >= screen.height)){
      win.resizeTo((screen.width * 4)/5,(screen.height * 7)/8);
    }
  },

  methods: {
    //runs a function named func in the mode file currently being used
    modeFunction: function(func, args) {
      var mode = this.$options.mode;
      if (typeof mode[func] === 'function') {
        // make args an array if it isn't already
        // typeof args won't work because it returns 'object'
        if (Object.prototype.toString.call(args) !== '[object Array]') {
          args = [args];
        }
        mode[func].apply(this, args);
      }
    },

    setupSettings: function() {
      this.settings = settings.load();
      this.$watch('settings', function(value){
        this.$broadcast('settings-changed', value);
        settings.save(value);
      });
    },

    // use jquery to handle file changes
    // (I should move this over to vuejs but it wasn't dealing
    // with the html file element properly)
    setupFileListener: function() {
      $('#openFile').change(this.open.bind(this));
      $('#saveFile').change(this.saveAs.bind(this));
      $('#saveProject').change(this.saveProjectAs.bind(this));
    },

    setupCloseHandler: function() {
      var self = this;
      var win = gui.Window.get();
      win.on('close', function(closeEvent){
        // check to see if there are unsaved files
        var shouldClose = true;
        if (_.any(self.files, function(f) { return f.contents != f.lastSavedContents; })) {
          shouldClose = confirm('You have unsaved files. Quit and lose changes?');
        }
        if (shouldClose) {
          // clean up output window
          if (self.outputWindow) {
            self.outputWindow.close(true);
            self.outputWindow = null;
          }

          // save window state if the user quit the program
          if (closeEvent === 'quit') {
            windowstate.save(self, win);
          }

          windowstate.decrementWindows();
          if (windowstate.totalWindows() < 1) {
            gui.App.quit();
          }

          // close this window
          this.close(true);
          win = null;
        }
      });

      win.on('focus', function(){
        self.focused = true;
        self.resetMenu();
        if (self.askReload) {
          self.askReload = false;
          var shouldRefresh = confirm(self.currentFile.path + ' was edited on the disk. Reload? You will lose any changes.');
          if (shouldRefresh) {

            var win = self.newWindow(self.windowURL);
            windowstate.decrementWindows();

            win.on('document-start', function(){
              win.window.PATH = self.currentFile.path;
              gui.Window.get().close(true);
            });
          }
        }
      });

      win.on('blur', function(){
        self.focused = false;
      });
    },

    // todo: setup drag and drop
    setupDragListener: function() {
      var self = this;
      window.ondragover = function(e) { e.preventDefault(); return false };
      window.ondrop = function(e) {
        e.preventDefault();
        if (e.dataTransfer.files[0]) {
          var path = e.dataTransfer.files[0].path;
          self.openProject(path);
        }
        return false
      };
    },

    // create a new window 50px below current window
    newWindow: function(url, options) {
      var currentWindow = gui.Window.get();

      var win = gui.Window.open(url, _.extend({
        x: currentWindow.x + 50,
        y: currentWindow.y + 50,
        width: 1024,
        height: 768,
        toolbar: false,
        focus: true,
        show: false
      }, options));
      return win;
    },

    // open an existing project with a new window
    open: function(event) {
      var path = event.target.files[0].path;
      this.openProject(path);
      // reset value in case the user wants to open the same file more than once
      $('#openFile').val('');
    },

    openProject: function(path, temp) {
      // create the new window
      var win = this.newWindow(this.windowURL);

      // set the project path of the new window
      win.on('document-start', function(){
        if (fs.lstatSync(path).isDirectory()) {
          var sketchPath = Path.join(path, 'sketch.js');
          if (fs.existsSync(sketchPath)) path = sketchPath;
        }
        win.window.PATH = path;
        if (typeof temp === 'boolean' && temp === true) {
          win.window.UNSAVED = true;
        }
      });
      return win;
    },

    // load project files
    loadProject: function(path, callback) {
      var self = this;
      Files.list(path, function(files){
        self.files = files;
        self.watch(path);
        if (typeof callback === 'function') callback();
      });
    },

    resetMenu: function(){
      menu.resetMenu();
    },

    // watch the project file tree for changes
    watch: function(path) {
      var self = this;

      // don't watch recursively
      var watcher = chokidar.watch(path, {
        ignoreInitial: true,
        ignored: function(filepath) {
          var regex = new RegExp(path + '\/.*\/.+');
          return regex.test(filepath);
        }
      });

      watcher.on('add', function(path) {
        var f = Files.setup(path);
        Files.addToTree(f, self.files, self.projectPath);
      }).on('addDir', function(path) {
        var f = Files.setup(path, {type: 'folder', children: []});
        Files.addToTree(f, self.files, self.projectPath);
      }).on('unlink', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('unlinkDir', function(path) {
        Files.removeFromTree(path, self.files);
      }).on('change', function(path) {
        if (self.justSaved) {
          self.justSaved = false;
        } else {
          if (!self.temp) self.askReload = true;
        }
      });
    },

    // close the window, checking for unsaved file changes
    closeProject: function() {
      if (this.focused) {
        if (this.outputWindow) {
          this.outputWindow.close(true);
          this.outputWindow = null;
        }
        gui.Window.get().close();
      } else {
        if (this.outputWindow) {
          this.toggleRun();
        }
      }
    },

    // save all open files
    saveAll: function() {
      _.where(this.files, {type: 'file', open: true}).forEach(function(file) {
        if (file.lastSavedContents != file.contents) {
          fs.writeFileSync(file.path, file.contents, "utf8");
          file.lastSavedContents = file.contents;
        }
      });
      menu.updateRecentFiles(this, this.projectPath);
      this.justSaved = true;
    },

    saveAs: function(event) {
      // capture the filename selected by the user
      var file = event.target.files[0].path;

      if (this.temp) {
        // mode specific action
        this.modeFunction('saveAs', file);
        menu.updateRecentFiles(this, this.projectPath);
      } else {
        // save a file
        // if the we are saving inside the project path just open the new file
        // otherwise open a new window
        fs.writeFileSync(file, this.currentFile.contents, "utf8");
        if ((Path.dirname(file) + '/').indexOf(this.projectPath + '/') > -1) {
          var f = Files.setup(file);
          Files.addToTree(f, this.files, this.projectPath);
          this.openFile(file);
        } else {
          this.openProject(file);
        }
      }

      // reset value in case the user wants to save the same filename more than once
      $('#saveFile').val('');
    },

    saveProjectAs: function(event) {
      var path = event.target.files[0].path;
      var self = this;
      fs.exists(path, function(existing){
        if (existing) {
          rimdir(path, function(error){
            if (error) throw error;
            self.modeFunction('saveAs', path);
          });
        } else {
          self.modeFunction('saveAs', path);
        }
      });
    },

    saveFile: function() {
      // if this is a new project then trigger a save-as
      if (this.temp) {
        $('#saveFile').trigger('click');
      } else {
        // otherwise just write the current file
        this.writeFile();
      }
    },

    saveFileAs: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Save file as:', originalName);
      if (!newName || newName === originalName) return false;

      var self = this;
      var filename = Path.join(Path.dirname(path), newName);
      fs.writeFile(filename, this.currentFile.contents, 'utf8', function(err){
        var f = Files.setup(filename);
        self.openFile(filename);
      });
    },

    writeFile: function() {
      var self = this;

      self.justSaved = true;

      fs.writeFileSync(this.currentFile.path, this.currentFile.contents, "utf8");
      this.currentFile.lastSavedContents = this.currentFile.contents;

    },

    // open up a file - read its contents if it's not already opened
    openFile: function(path, callback) {
      var self = this;
      var re = /(?:\.([^.]+))?$/;
      var ext = re.exec(path)[1];

      var file = Files.find(this.files, path);
      if (!file) return false;
      if (self.fileTypes.indexOf(ext) < 0) {
        window.alert("Unsupported file type. Types we can edit:\n" + self.fileTypes.toString());
      } else {
        if (file.open) {
          this.title = file.name;
          this.currentFile = file;
          this.$broadcast('open-file', this.currentFile);
        } else {
          fs.readFile(path, 'utf8', function(err, fileContents) {
            if (err) throw err;
            file.contents = file.originalContents = file.lastSavedContents = fileContents;
            file.open = true;
            self.title = file.name;
            self.currentFile = file;
            self.$broadcast('open-file', self.currentFile);
            self.$broadcast('add-tab', self.currentFile,self.tabs);

            if (typeof callback === 'function') callback(file);
          });
        }
      }
    },

    closeFile: function(path){
        // check to see if there are unsaved files
         var file = Files.find(this.files, path);
        if (!file) return false;

        if(this.tabs.length==1){
            var win = gui.Window.get();
            win.close();
        }
        var shouldClose = true;
        var win = gui.Window.get();
        if (file.contents != file.lastSavedContents){
          shouldClose = confirm('You have unsaved changes. Close file and lose changes?');
        }
        if (shouldClose) {
          file.open = false;
          file.contents = file.lastSavedContents;
          this.$broadcast('close-file', file);
          return true;
        }
        return false;
    },

    // create a new file and save it in the project path
    newFile: function(basepath) {
      var title = prompt('Choose a file name and type: \nSupported types: ' + this.fileTypes.toString()).replace(/ /g,'');
      var dotSplit = title.split(".");
      var re = /(?:\.([^.]+))?$/;

      if (!title) return false;

      if (this.fileTypes.indexOf(re.exec(title)[1]) < 0 || (dotSplit.length > 2)){
        window.alert("unsupported/improper file type selected.\nAutomaticallly adding a .js extension");
        title = dotSplit[0] + '.js';
      }

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.writeFile(filename, '', 'utf8', function(err){
        var f = Files.setup(filename);
        Files.addToTree(f, self.files, self.projectPath);
        self.openFile(filename);
      });
    },

    newFolder: function(basepath) {
      var title = prompt('Folder name:');
      if (!title) return false;

      if (typeof basepath === 'undefined') {
        basepath = this.projectPath;
      }

      var filename = Path.join(basepath, title);

      var self = this;
      fs.mkdir(filename);
    },

    renameFile: function(path) {
      var originalName = Path.basename(path);
      var newName = prompt('Rename ' + originalName + ' to:', originalName);
      if (!newName) return false;

      fs.rename(path, Path.join(Path.dirname(path), newName));
    },

    run: function() {
      $('#debug').html('');
      this.modeFunction('run');
    },

    toggleRun: function() {
      if (this.running) {
        this.modeFunction('stop');
      } else {
        $('#debug').html('');
        this.modeFunction('run');
      }
    },

    changeFontSize: function(sz) {
      this.settings.fontSize = parseInt(this.settings.fontSize) + sz;
    },

    toggleSettingsPane: function() {
      this.showSettings = !this.showSettings;
    },

    toggleSidebar: function() {
      this.settings.showSidebar = !this.settings.showSidebar;
    },

    showHelp: function() {
      gui.Shell.openExternal(this.$options.mode.referenceURL);
    }

  }

};

var app = new Vue(appConfig);
<|endoftext|>