<commit_before>/*jslint indent: 2, nomen: true, maxlen: 120 */
/*global require */

(function () {
  'use strict';
  var Foxx = require('org/arangodb/foxx'),
    _ = require('underscore'),
    ArangoError = require('internal').ArangoError,
    JsonApiModel,
    JsonApiRepository,
    ElementTransition,
    ContainerTransition,
    ReplaceOperation,
    transitions = [];

  JsonApiRepository = Foxx.Repository.extend({
    updateByIdWithOperations: function (id, operations) {
      var model = this.byId(id);
      _.each(operations, function (operation) {
        operation.execute(model);
      });
      return this.replace(model);
    },

    allWithNeighbors: function (options) {
      var results = this.all(options);
      _.each(results, function (result) {
        this.addLinks(result);
      }, this);
      return results;
    },

    byIdWithNeighbors: function (key) {
      var result = this.byId(key);
      this.addLinks(result);
      return result;
    },

    addLinks: function (model) {
      var links = {},
        graph = this.graph,
        relationNames = this.relationNames;

      _.each(relationNames, function (relation) {
        var neighbors = graph.neighbors(model.get('_id'), {
          edgeCollectionRestriction: [relation.edgeCollectionName]
        });

        if (relation.type === 'one' && neighbors.length > 0) {
          links[relation.relationName] = neighbors[0]._key;
        } else if (relation.type === 'many') {
          links[relation.relationName] = _.pluck(neighbors, '_key');
        }
      });

      model.set('links', links);
    }
  });

  JsonApiModel = Foxx.Model.extend({
    forClient: function () {
      var attributes = Foxx.Model.prototype.forClient.call(this);
      return _.extend({ id: this.get('_key') }, attributes);
    }
  });

  ReplaceOperation = Foxx.Model.extend({
    isValid: function () {
      return (this.get('op') === 'replace');
    },

    execute: function (model) {
      model.set(this.getAttributeName(), this.get('value'));
    },

    // Fake implementation
    getAttributeName: function () {
      return this.get('path').split('/').pop();
    }
  }, {
    op: { type: 'string', required: true },
    path: { type: 'string', required: true },
    value: { type: 'string', required: true }
  });

  ElementTransition = function (graph, controller) {
    this.graph = graph;
    this.controller = controller;
  };

  _.extend(ElementTransition.prototype, {
    apply: function (from, to) {
      var collectionPath = '/' + from.name,
        perPage = 10,
        repository = from.repository,
        nameOfRootElement = from.name,
        Model = to.model,
        attributes = Model.attributes,
        BodyParam = Foxx.Model.extend({}, { attributes: attributes });

      repository.relationNames = to.relationNames;

      this.controller.get(collectionPath, function (req, res) {
        var data = {},
          page = req.params('page') || 0,
          skip = page * perPage,
          options = { skip: skip, limit: perPage };

        data[nameOfRootElement] = _.map(repository.allWithNeighbors(options), function (datum) {
          return datum.forClient();
        });
        res.json(data);
      }).queryParam('page', {
        description: 'Page of the results',
        type: 'int'
      }).summary('Get all entries')
        .notes('TODO');

      this.controller.post(collectionPath, function (req, res) {
        var data = {};
        data[nameOfRootElement] = _.map(req.params(nameOfRootElement), function (model) {
          return repository.save(model).forClient();
        });
        res.status(201);
        res.json(data);
      }).bodyParam(nameOfRootElement, 'TODO', [BodyParam])
        .summary('Post new entries')
        .notes('TODO');
    }
  });

  transitions.push({ name: 'element', Transition: ElementTransition });

  ContainerTransition = function (graph, controller) {
    this.graph = graph;
    this.controller = controller;
  };

  _.extend(ContainerTransition.prototype, {
    apply: function (from, to) {
      var entryPath = '/' + to.name + '/:id',
        repository = to.repository,
        nameOfRootElement = to.name;

      from.collectionName = to.collectionName;

      this.controller.get(entryPath, function (req, res) {
        var id = req.params('id'),
          entry = repository.byIdWithNeighbors(id),
          data = {};

        data[nameOfRootElement] = [entry.forClient()];

        res.json(data);
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).summary('Get a specific entry')
        .notes('TODO');

      // This works a little different from the standard:
      // It expects a root element, the standard does not
      this.controller.patch(entryPath, function (req, res) {
        var operations = req.params('operations'),
          id = req.params('id'),
          data = {};

        if (_.all(operations, function (x) { return x.isValid(); })) {
          data[nameOfRootElement] = repository.updateByIdWithOperations(id, operations).forClient();
          res.json(data);
        } else {
          res.json({ error: 'Only replace is supported right now' });
          res.status(405);
        }
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).bodyParam('operations', 'The operations to be executed on the document', [ReplaceOperation])
        .summary('Update an entry')
        .notes('TODO');

      this.controller.del(entryPath, function (req, res) {
        var id = req.params('id');
        repository.removeById(id);
        res.status(204);
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).errorResponse(ArangoError, 404, 'An entry with this ID could not be found')
        .summary('Remove an entry')
        .notes('TODO');
    }
  });

  transitions.push({ name: 'container', Transition: ContainerTransition });

  exports.mediaType = {
    Model: JsonApiModel,
    Repository: JsonApiRepository,
    transitions: transitions
  };
}());
<commit_msg>Create an urlFor method on states that are contained in other states<commit_after>/*jslint indent: 2, nomen: true, maxlen: 120 */
/*global require */

(function () {
  'use strict';
  var Foxx = require('org/arangodb/foxx'),
    _ = require('underscore'),
    ArangoError = require('internal').ArangoError,
    JsonApiModel,
    JsonApiRepository,
    ElementTransition,
    ContainerTransition,
    ReplaceOperation,
    transitions = [];

  JsonApiRepository = Foxx.Repository.extend({
    updateByIdWithOperations: function (id, operations) {
      var model = this.byId(id);
      _.each(operations, function (operation) {
        operation.execute(model);
      });
      return this.replace(model);
    },

    allWithNeighbors: function (options) {
      var results = this.all(options);
      _.each(results, function (result) {
        this.addLinks(result);
      }, this);
      return results;
    },

    byIdWithNeighbors: function (key) {
      var result = this.byId(key);
      this.addLinks(result);
      return result;
    },

    addLinks: function (model) {
      var links = {},
        graph = this.graph,
        relationNames = this.relationNames;

      _.each(relationNames, function (relation) {
        var neighbors = graph.neighbors(model.get('_id'), {
          edgeCollectionRestriction: [relation.edgeCollectionName]
        });

        if (relation.type === 'one' && neighbors.length > 0) {
          links[relation.relationName] = neighbors[0]._key;
        } else if (relation.type === 'many') {
          links[relation.relationName] = _.pluck(neighbors, '_key');
        }
      });

      model.set('links', links);
    }
  });

  JsonApiModel = Foxx.Model.extend({
    forClient: function () {
      var attributes = Foxx.Model.prototype.forClient.call(this);
      return _.extend({ id: this.get('_key') }, attributes);
    }
  });

  ReplaceOperation = Foxx.Model.extend({
    isValid: function () {
      return (this.get('op') === 'replace');
    },

    execute: function (model) {
      model.set(this.getAttributeName(), this.get('value'));
    },

    // Fake implementation
    getAttributeName: function () {
      return this.get('path').split('/').pop();
    }
  }, {
    op: { type: 'string', required: true },
    path: { type: 'string', required: true },
    value: { type: 'string', required: true }
  });

  ElementTransition = function (graph, controller) {
    this.graph = graph;
    this.controller = controller;
  };

  _.extend(ElementTransition.prototype, {
    apply: function (from, to) {
      var collectionPath = '/' + from.name,
        perPage = 10,
        repository = from.repository,
        nameOfRootElement = from.name,
        Model = to.model,
        attributes = Model.attributes,
        BodyParam = Foxx.Model.extend({}, { attributes: attributes });

      repository.relationNames = to.relationNames;

      this.controller.get(collectionPath, function (req, res) {
        var data = {},
          page = req.params('page') || 0,
          skip = page * perPage,
          options = { skip: skip, limit: perPage };

        data[nameOfRootElement] = _.map(repository.allWithNeighbors(options), function (datum) {
          return datum.forClient();
        });
        res.json(data);
      }).queryParam('page', {
        description: 'Page of the results',
        type: 'int'
      }).summary('Get all entries')
        .notes('TODO');

      this.controller.post(collectionPath, function (req, res) {
        var data = {};
        data[nameOfRootElement] = _.map(req.params(nameOfRootElement), function (model) {
          return repository.save(model).forClient();
        });
        res.status(201);
        res.json(data);
      }).bodyParam(nameOfRootElement, 'TODO', [BodyParam])
        .summary('Post new entries')
        .notes('TODO');
    }
  });

  transitions.push({ name: 'element', Transition: ElementTransition });

  ContainerTransition = function (graph, controller) {
    this.graph = graph;
    this.controller = controller;
  };

  _.extend(ContainerTransition.prototype, {
    apply: function (from, to) {
      var entryPath = '/' + to.name + '/:id',
        repository = to.repository,
        nameOfRootElement = to.name;

      from.urlFor = function (selector) {
        return '/' + to.name + '/{' + selector + '}';
      };

      from.collectionName = to.collectionName;

      this.controller.get(entryPath, function (req, res) {
        var id = req.params('id'),
          entry = repository.byIdWithNeighbors(id),
          data = {};

        data[nameOfRootElement] = [entry.forClient()];

        res.json(data);
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).summary('Get a specific entry')
        .notes('TODO');

      // This works a little different from the standard:
      // It expects a root element, the standard does not
      this.controller.patch(entryPath, function (req, res) {
        var operations = req.params('operations'),
          id = req.params('id'),
          data = {};

        if (_.all(operations, function (x) { return x.isValid(); })) {
          data[nameOfRootElement] = repository.updateByIdWithOperations(id, operations).forClient();
          res.json(data);
        } else {
          res.json({ error: 'Only replace is supported right now' });
          res.status(405);
        }
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).bodyParam('operations', 'The operations to be executed on the document', [ReplaceOperation])
        .summary('Update an entry')
        .notes('TODO');

      this.controller.del(entryPath, function (req, res) {
        var id = req.params('id');
        repository.removeById(id);
        res.status(204);
      }).pathParam('id', {
        description: 'ID of the document',
        type: 'string'
      }).errorResponse(ArangoError, 404, 'An entry with this ID could not be found')
        .summary('Remove an entry')
        .notes('TODO');
    }
  });

  transitions.push({ name: 'container', Transition: ContainerTransition });

  exports.mediaType = {
    Model: JsonApiModel,
    Repository: JsonApiRepository,
    transitions: transitions
  };
}());
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import { connect } from 'react-redux';
import { startChat, stopChat } from '../redux/chat/action-creator';
import socket from '../socket';
import { controls } from '../game/main';

class Chat extends Component {
  constructor(props) {
    super(props);
    this.state = { message: '' };

    this.submitMessage = this.submitMessage.bind(this);
    this.handleMessageChange = this.handleMessageChange.bind(this);
  }

  componentDidMount() {
    /*----- ENABLE CHAT START/CLOSE ON 'ENTER' -----*/
    window.addEventListener('keydown', (evt) => {

      //'ENTER' TO START
      if (evt.keyCode === 13) {
        this.props.startChat();
        controls.enabled = false;
      }

      // BACKTICK TO STOP
      if (evt.keyCode === 192) {
        this.props.stopChat();
        controls.enabled = true;
      }
    }, false)
  }

  componentDidUpdate() {
    /*----- SETS FOCUS TO CHAT BOX -----*/
    if (this.props.isChatting) { this.refs.chat.focus(); }
  }

  submitMessage(evt) {
    if (evt.keyCode === 13 && this.state.message.length > 0) {
      controls.enabled = true;
      socket.emit('new_message', {
        id: socket.id,
        message: this.state.message
      })
      this.setState({ message: '' });
      this.props.stopChat();
    }
  }

  handleMessageChange(evt) {
    this.setState({ message: evt.target.value });
  }

  render() {
    const lastFiveMessages = this.props.lastFiveMessages;
    const isChatting = this.props.isChatting;
    const handleMessageChange = this.handleMessageChange;
    const submitMessage = this.submitMessage;
    const message = this.state.message;

    const chatMessages = lastFiveMessages.map((chatMessage, idx) => {
      return (<h1 key={`${idx}`} className='chat-message'>{chatMessage}
)
    })

    return (

        {chatMessages}
        {isChatting ?
        <TextField
          id="chat"
          ref="chat"
          onChange={handleMessageChange}
          onKeyDown={submitMessage}
          value={message}
          underlineFocusStyle={{ borderColor: '#ffffff' }}
          inputStyle={{ fontSize: 15, color: '#ffffff' }}
        /> :
Press Enter to Chat / Back Tick to Exit Chat
}

    )
  }
}

const mapStateToProps = (state) => ({
  isChatting: state.chat.isChatting,
  isPlaying: state.isPlaying,
  lastFiveMessages: state.chat.lastFiveMessages
})

const mapDispatchToProps = (dispatch) => ({
  startChat: () => dispatch(startChat()),
  stopChat: () => dispatch(stopChat())
})

export default connect(mapStateToProps, mapDispatchToProps)(Chat);
<commit_msg>chore: style variable for inline style<commit_after>import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import { connect } from 'react-redux';
import { startChat, stopChat } from '../redux/chat/action-creator';
import socket from '../socket';
import { controls } from '../game/main';

const styles = {
  borderStyles: {
    borderColor: '#ffffff'
  },
  fontStyles: {
    ontSize: 15,
    color: '#ffffff'
  }
}

class Chat extends Component {
  constructor(props) {
    super(props);
    this.state = { message: '' };

    this.submitMessage = this.submitMessage.bind(this);
    this.handleMessageChange = this.handleMessageChange.bind(this);
  }

  componentDidMount() {
    /*----- ENABLE CHAT START/CLOSE ON 'ENTER' -----*/
    window.addEventListener('keydown', (evt) => {

      //'ENTER' TO START
      if (evt.keyCode === 13) {
        this.props.startChat();
        controls.enabled = false;
      }

      // BACKTICK TO STOP
      if (evt.keyCode === 192) {
        this.props.stopChat();
        controls.enabled = true;
      }
    }, false)
  }

  componentDidUpdate() {
    /*----- SETS FOCUS TO CHAT BOX -----*/
    if (this.props.isChatting) { this.refs.chat.focus(); }
  }

  submitMessage(evt) {
    if (evt.keyCode === 13 && this.state.message.length > 0) {
      controls.enabled = true;
      socket.emit('new_message', {
        id: socket.id,
        message: this.state.message
      })
      this.setState({ message: '' });
      this.props.stopChat();
    }
  }

  handleMessageChange(evt) {
    this.setState({ message: evt.target.value });
  }

  render() {
    const lastFiveMessages = this.props.lastFiveMessages;
    const isChatting = this.props.isChatting;
    const handleMessageChange = this.handleMessageChange;
    const submitMessage = this.submitMessage;
    const message = this.state.message;

    const chatMessages = lastFiveMessages.map((chatMessage, idx) => {
      return (<h1 key={`${idx}`} className='chat-message'>{chatMessage}
)
    })

    return (

        {chatMessages}
        {isChatting ?
        <TextField
          id="chat"
          ref="chat"
          onChange={handleMessageChange}
          onKeyDown={submitMessage}
          value={message}
          underlineFocusStyle={styles.borderStyles}
          inputStyle={styles.fontStyles}
        /> :
Press Enter to Chat / Back Tick to Exit Chat
}

    )
  }
}

const mapStateToProps = (state) => ({
  isChatting: state.chat.isChatting,
  isPlaying: state.isPlaying,
  lastFiveMessages: state.chat.lastFiveMessages
})

const mapDispatchToProps = (dispatch) => ({
  startChat: () => dispatch(startChat()),
  stopChat: () => dispatch(stopChat())
})

export default connect(mapStateToProps, mapDispatchToProps)(Chat);
<|endoftext|>


<commit_before>define(['jquery', 'bootstrap', "logging"], function ($) {
    var dashboard = dashboard || {};

    dashboard = {
        init: function () {
            this.navigation.init();
            this.bootstrap.init();
            this.stages.init();
            this.feedback.init();
        },

        bootstrap: {
            activeFilter: "all",
            init: function () {
                $('#usr-settings-theme').bind('change', function () {
                    $('#css').attr('href', $(this).val());
                });

                $('#search').bind('keyup', function () {
                    dashboard.stages.filter($(this).val());
                });
                $('#btn-refresh').bind('click', function () {
                    dashboard.stages.reload();
                });
                $('.modal').on('hide.bs.modal', function () {
                    $(".shell").html("");
                    dashboard.stages.reload();
                });
                $('[data-toggle="tab"]').on('click', function () {
                    var stages, filter, check;
                    stages = $("#all-stages").find("tr");
                    filter = $(this).attr("href").slice(1);
                    check = '';

                    dashboard.bootstrap.activeFilter = filter;

                    if (filter == 'all') {
                        stages.removeClass('filter');
                    } else {
                        stages.addClass('filter');
                    }

                    if (parseInt($(this).find('.badge').html()) == 0) {
                        $('#empty').removeClass('hidden');
                    } else {
                        $('#empty').addClass('hidden');
                    }

                    stages.each(function () {

                        if ($(this).attr('data-extractionurl') != null) {

                            if (filter === "trunks") {
                                check = '/trunk';
                            } else if (filter === "branches") {
                                check = '/branches/';
                            } else if (filter === "workspaces") {
                                check = '/workspaces/';
                            }

                            $(this).addClass('hidden');

                            if (filter === 'all' || filter === '') {
                                $(this).removeClass('hidden');
                            } else {
                                if ($(this).attr('data-extractionurl').indexOf(check) > -1) {
                                    $(this).removeClass('hidden');
                                }
                            }

                        }

                    });

                    dashboard.stages.filter($('#search').val());

                });

            }
        },

        navigation: {
            init: function () {
                var hash;
                hash = location.hash.slice(1);
                if (hash.indexOf(':') > -1) {
                    params = hash.split(':');
                    hash = params[0];
                }
                this.hashChange()
                this.loadContentByHash(hash != '' ? hash : 'dashboard');

            },

            hashChange: function () {
                $(window).bind('hashchange', function () {
                    var h, params;
                    h = location.hash.slice(1);
                    params = null;
                    if (h.indexOf(':') > -1) {
                        params = h.split(':');
                        h = params[0];
                    }
                    if (h != '') {
                        dashboard.navigation.loadContentByHash(h, params);
                    }
                    dashboard.navigation.checkLocationHash();
                });
            },

            loadContentByHash: function (h) {

                if ($('div').find('.content.' + h).length == 0) {

                    $('.content').addClass('hidden');
                    $('.tab').removeClass('active');
                    $('.content.error').removeClass('hidden');

                } else {

                    $('.content').each(function () {
                        $(this).addClass('hidden');
                        if ($(this).hasClass(h)) {
                            $(this).removeClass('hidden');
                            if ($(this).attr('data-title') != '') {
                                document.title = 'Stage  ' + $(this).attr('data-title');
                            }
                        }
                    });

                    $('.tab').each(function () {
                        $(this).removeClass('active');
                        if ($(this).hasClass(h)) {
                            $(this).addClass('active');
                        }
                    });

                }

            },
            checkLocationHash: function () {

                var hash, i;
                hash = window.location.hash.slice(1);

                if (hash.indexOf(':') > -1) {
                    parameters = hash.split(':');
                    for (i = 1; i < parameters.length; i += 1) {
                        parameter = parameters[i];
                        if (parameter.indexOf('=') > -1) {
                            param = parameter.split('=');
                            if (param[0] == 'search') {
                                dashboard.stages.filter(param[1]);
                            } else if (param[0] == 'stage') {
                                dashboard.navigation.updateStageBreadcrump(param[1]);
                            }
                        }
                    }
                }

            }
        },

        updateStageBreadcrump: function (stagename) {
            $('.breadcrumb .stagename').html(stagename);
        },

        info: {
            show: function (type, title, text) {
                $('#info').append($('
').attr('class', 'alert alert-dismissable alert-' + type).html('<button type="button" class="close" data-dismiss="alert">X</button>
' + title + '
' + text).fadeIn().delay(2000).fadeOut());
            },
            text: function (element, text) {
                $(element).html(text).removeClass('hidden');

            }
        },

        stages: {
            init: function () {
                dashboard.stages.reload();
                $('[data-action]').on('click', dashboard.stages.action);
                $('#loading').remove();
                dashboard.stages.silentReload();
            },

            silentReload: function () {

                dashboard.stages.reload();
                setTimeout(function () {
                    dashboard.stages.silentReload();
                }, dashboard.interval.withCounter(0));
            },

            recountTabs: function (wrapper) {
                var trunks, branches, workspaces;

                trunks = wrapper.find(".trunk").size();
                branches = wrapper.find(".branch").size()
                workspaces = wrapper.find(".workspace").size();
                $('#all-count').html(trunks + workspaces + branches);

                $('#trunks-count').html(trunks);
                $('#branches-count').html(branches);
                $('#workspaces-count').html(workspaces);
            },

            reload: function () {
                $.ajax('/stages', {
                    dataType: "html",
                    success: function (data) {
                        // TODO: handle removed stages. They currently remain in the dashboard until the user presses refresh.

                        var wrapper;
                        wrapper = $('#all-stages');

                        $(data).each(function (i, tr) {
                            var oldTr;
                            var actions;
                            var id;

                            id = tr.id;

                            if (id !== undefined) {
                                id = id.replace(/\./g, "\\.")
                            }
                            oldTr = wrapper.find('#' + id);

                            if (oldTr.length === 0) {
                                // new stage
                                $(tr).find('[data-action]').off('click', dashboard.stages.action);
                                wrapper.append(tr);
                                $(tr).find('[data-action]').on('click', dashboard.stages.action);
                            } else if (oldTr.attr("data-hash") !== $(tr).attr("data-hash")) {
                                // updated stage
                                $(tr).find('[data-action]').off('click', dashboard.stages.action);
                                oldTr.replaceWith(tr);
                                $(tr).find('[data-action]').on('click', dashboard.stages.action);
                            } else {
                                // no changes
                            }
                        });
                        $('[data-toggle="popover"]').popover();
                        dashboard.stages.recountTabs(wrapper);
                        dashboard.navigation.checkLocationHash();
                    }
                });
            },

            action: function () {
                var stage, action, options, estimate, p, i, url;
                if ($(this).parent().hasClass('disabled') && $(this).attr('data-stage') === null) {
                    return false;
                }

                stage = $(this).attr('data-stage');
                action = $(this).attr('data-action');
                options = $(this).attr('data-options');
                estimate = $(this).attr('data-estimate');
                p = $(this).parent().parent();
                i = $(p).find('.info');

                url = "/stages/" + stage + "/" + action;
                $('#' + stage + ' a.action').addClass('disabled');

                $(p).find('.fa').removeClass('hidden');
                $(p).find('.label').attr('class', 'label label-primary').html('n/a');

                //setInfo
                (i, $(this).attr('data-title'));
                //showInfo('info', stage, $(this).attr('data-title'));

                if (options != null) {
                    url = url + "/" + options;
                }

                $('.modal-title').text("Console output");
                $('.modal').modal();
                $.post(url).fail(function (r) {
                    $(p).find('.fa').addClass('hidden');
                    $(p).find('.label').attr('class', 'label label-warning').html('broken');
                    $(p).addClass('warning');
                    dashboard.info.show(i, 'dude something went wrong ... try again or send us a feedback');
                    dashboard.info.text('warning', stage, '%ERROR_MESSAGE%');
                    log.error(r);
                }).done(function (r) {
                    var spinner;
                    $('.modal-body .shell').html("").show();
                    spinner = '.fa-spinner';
                    $(spinner).show();
                    dashboard.stages.showLog('.modal-body .shell', r, 0, spinner, '.modal', estimate);

                });

                return false;

            },
            showLog: function (element, id, index, spinner, parent, estimate) {
                dashboard.stages.fetchLog(element, id, index, spinner, 0, parent);

                //dashboard.stages.progressbar.updater(".modal-footer", estimate, new Date())
            },

            progressbar: {
                updater: function (parent, estimate, start) {
                    var done, percent, text;
                    done = new Date() - start;
                    percent = Math.round(done / estimate * 100);
                    text = "ETA " + Math.round(estimate / 1000 - done / 1000) + "sec.";
                    dashboard.stages.progressbar.update(parent, percent, text)
                    if (percent <= 100) {
                        setTimeout(function () {
                            dashboard.stages.progressbar.updater(parent, estimate, start);
                        }, 200);
                    }

                },
                update: function (parent, percent, text) {
                    parent = $(parent);
                    if (parent.find(".logprogress").size() > 0) {
                        var element;
                        element = parent.find('.progress-bar');
                        element.attr('aria-valuenow', percent);
                        element.attr('style', "width:" + percent + "%");
                        element.text(text)
                    } else {
                        parent.append('
' +
                            '
' +
                            text + '
' + text + '
');
                    }
                },
            },
            fetchLog: function (element, id, index, spinner, lastSize, parent) {
                $.get("/processes/" + id + "/log", {"index": index}).done(function (data, status, response) {
                    newSize = response.getResponseHeader("X-Size");
                    if (newSize != lastSize) {
                        lastSize = newSize;
                        $(element).append(data);
                        $(parent).animate({scrollTop: $(element).height()}, 'fast');
                    }
                    if ("true" == response.getResponseHeader("X-Running")) {
                        setTimeout(function () {
                            dashboard.stages.fetchLog(element, id, index, spinner, lastSize, parent);
                        }, 1000);
                    } else {
                        $(spinner).hide();
                    }
                });

            },

            filter: function (search) {
                if (search !== '') {
                    var activeFilter;
                    activeFilter = dashboard.bootstrap.activeFilter;
                    document.location.hash = '#dashboard:search=' + search;

                    $('#all-stages').find('tr.stage').addClass('hidden');
                    $('#empty').addClass('hidden');

                    found = 0;

                    $('#all-stages').find('tr.stage' + (activeFilter == 'all' ? '' : '.' + activeFilter)).each(function () {
                        if ($(this).attr('data-extractionurl').indexOf(search) > -1 || $(this).attr('data-user').indexOf(search) > -1 || $(this).find('td.name span').html().indexOf(search) > -1) {
                            $(this).removeClass('hidden');
                            found++;
                        }
                    });

                    if (found === 0) {
                        $('#empty').removeClass('hidden');
                    }

                } else {
                    document.location.hash = '#dashboard';
                    $('#empty').addClass('hidden');
                    $('#all-stages').find('tr.stage' + (activeFilter == 'all' ? '' : '.' + activeFilter)).removeClass('hidden');
                }

            }
        },
        feedback: {
            init: function () {
                $('#feedback-submit').bind('click', function (e) {
                    e.preventDefault();
                    var text = $('#feedback-text');
                    if (text.val() != "") {
                        $.post("/feedback", { message: text.val()})
                            .done(function () {
                                text.parent().parent().prepend("
Thanks.
");
                                text.val("");
                            })
                            .fail(function () {
                                text.parent().parent()
                                    .prepend("
Opps, something wen't wrong =(.
");
                            });
                    }
                });
            }
        },

        interval: {
            withCounter: function (count) {
                return 5000;
            }
        }

    };
    dashboard.init();
});
<commit_msg>Update main.js<commit_after>define(['jquery', 'bootstrap', "logging"], function ($) {
    var dashboard = dashboard || {};

    dashboard = {
        init: function () {
            this.navigation.init();
            this.bootstrap.init();
            this.stages.init();
            this.feedback.init();
        },

        bootstrap: {
            activeFilter: "all",
            init: function () {
                $('#usr-settings-theme').bind('change', function () {
                    $('#css').attr('href', $(this).val());
                });

                $('#search').bind('keyup', function () {
                    dashboard.stages.filter($(this).val());
                });
                $('#btn-refresh').bind('click', function () {
                    dashboard.stages.reload();
                });
                $('.modal').on('hide.bs.modal', function () {
                    $(".shell").html("");
                    dashboard.stages.reload();
                });
                $('[data-toggle="tab"]').on('click', function () {
                    var stages, filter, check;
                    stages = $("#all-stages").find("tr");
                    filter = $(this).attr("href").slice(1);
                    check = '';

                    dashboard.bootstrap.activeFilter = filter;

                    if (filter == 'all') {
                        stages.removeClass('filter');
                    } else {
                        stages.addClass('filter');
                    }

                    if (parseInt($(this).find('.badge').html()) == 0) {
                        $('#empty').removeClass('hidden');
                    } else {
                        $('#empty').addClass('hidden');
                    }

                    stages.each(function () {

                        if ($(this).attr('data-extractionurl') != null) {

                            if (filter === "trunks") {
                                check = '/trunk';
                            } else if (filter === "branches") {
                                check = '/branches/';
                            } else if (filter === "workspaces") {
                                check = '/workspaces/';
                            }

                            $(this).addClass('hidden');

                            if (filter === 'all' || filter === '') {
                                $(this).removeClass('hidden');
                            } else {
                                if ($(this).attr('data-extractionurl').indexOf(check) > -1) {
                                    $(this).removeClass('hidden');
                                }
                            }

                        }

                    });

                    dashboard.stages.filter($('#search').val());

                });

            }
        },

        navigation: {
            init: function () {
                var hash;
                hash = location.hash.slice(1);
                if (hash.indexOf(':') > -1) {
                    params = hash.split(':');
                    hash = params[0];
                }
                this.hashChange()
                this.loadContentByHash(hash != '' ? hash : 'dashboard');

            },

            hashChange: function () {
                $(window).bind('hashchange', function () {
                    var h, params;
                    h = location.hash.slice(1);
                    params = null;
                    if (h.indexOf(':') > -1) {
                        params = h.split(':');
                        h = params[0];
                    }
                    if (h != '') {
                        dashboard.navigation.loadContentByHash(h, params);
                    }
                    dashboard.navigation.checkLocationHash();
                });
            },

            loadContentByHash: function (h) {

                if ($('div').find('.content.' + h).length == 0) {

                    $('.content').addClass('hidden');
                    $('.tab').removeClass('active');
                    $('.content.error').removeClass('hidden');

                } else {

                    $('.content').each(function () {
                        $(this).addClass('hidden');
                        if ($(this).hasClass(h)) {
                            $(this).removeClass('hidden');
                            if ($(this).attr('data-title') != '') {
                                document.title = 'Stage  ' + $(this).attr('data-title');
                            }
                        }
                    });

                    $('.tab').each(function () {
                        $(this).removeClass('active');
                        if ($(this).hasClass(h)) {
                            $(this).addClass('active');
                        }
                    });

                }

            },
            checkLocationHash: function () {

                var hash, i;
                hash = window.location.hash.slice(1);

                if (hash.indexOf(':') > -1) {
                    parameters = hash.split(':');
                    for (i = 1; i < parameters.length; i += 1) {
                        parameter = parameters[i];
                        if (parameter.indexOf('=') > -1) {
                            param = parameter.split('=');
                            if (param[0] == 'search') {
                                dashboard.stages.filter(param[1]);
                            } else if (param[0] == 'stage') {
                                dashboard.navigation.updateStageBreadcrump(param[1]);
                            }
                        }
                    }
                }

            }
        },

        updateStageBreadcrump: function (stagename) {
            $('.breadcrumb .stagename').html(stagename);
        },

        info: {
            show: function (type, title, text) {
                $('#info').append($('
').attr('class', 'alert alert-dismissable alert-' + type).html('<button type="button" class="close" data-dismiss="alert">X</button>
' + title + '
' + text).fadeIn().delay(2000).fadeOut());
            },
            text: function (element, text) {
                $(element).html(text).removeClass('hidden');

            }
        },

        stages: {
            init: function () {
                dashboard.stages.reload();
                $('[data-action]').on('click', dashboard.stages.action);
                $('#loading').remove();
                dashboard.stages.silentReload();
            },

            silentReload: function () {

                dashboard.stages.reload();
                setTimeout(function () {
                    dashboard.stages.silentReload();
                }, dashboard.interval.withCounter(0));
            },

            recountTabs: function (wrapper) {
                var trunks, branches, workspaces;

                trunks = wrapper.find(".trunk").size();
                branches = wrapper.find(".branch").size()
                workspaces = wrapper.find(".workspace").size();
                $('#all-count').html(trunks + workspaces + branches);

                $('#trunks-count').html(trunks);
                $('#branches-count').html(branches);
                $('#workspaces-count').html(workspaces);
            },

            reload: function () {
                $.ajax('/stages', {
                    dataType: "html",
                    success: function (data) {
                        // TODO: handle removed stages. They currently remain in the dashboard until the user presses refresh.

                        var wrapper;
                        wrapper = $('#all-stages');

                        $(data).each(function (i, tr) {
                            var oldTr;
                            var actions;
                            var id;

                            id = tr.id;

                            if (id !== undefined) {
                                id = id.replace(/\./g, "\\.");
                            }
                            oldTr = wrapper.find('#' + id);

                            if (oldTr.length === 0) {
                                // new stage
                                $(tr).find('[data-action]').off('click', dashboard.stages.action);
                                wrapper.append(tr);
                                $(tr).find('[data-action]').on('click', dashboard.stages.action);
                            } else if (oldTr.attr("data-hash") !== $(tr).attr("data-hash")) {
                                // updated stage
                                $(tr).find('[data-action]').off('click', dashboard.stages.action);
                                oldTr.replaceWith(tr);
                                $(tr).find('[data-action]').on('click', dashboard.stages.action);
                            } else {
                                // no changes
                            }
                        });
                        $('[data-toggle="popover"]').popover();
                        dashboard.stages.recountTabs(wrapper);
                        dashboard.navigation.checkLocationHash();
                    }
                });
            },

            action: function () {
                var stage, action, options, estimate, p, i, url;
                if ($(this).parent().hasClass('disabled') && $(this).attr('data-stage') === null) {
                    return false;
                }

                stage = $(this).attr('data-stage');
                action = $(this).attr('data-action');
                options = $(this).attr('data-options');
                estimate = $(this).attr('data-estimate');
                p = $(this).parent().parent();
                i = $(p).find('.info');

                url = "/stages/" + stage + "/" + action;
                $('#' + stage + ' a.action').addClass('disabled');

                $(p).find('.fa').removeClass('hidden');
                $(p).find('.label').attr('class', 'label label-primary').html('n/a');

                //setInfo(i, $(this).attr('data-title'));
                //showInfo('info', stage, $(this).attr('data-title'));

                if (options != null) {
                    url = url + "/" + options;
                }

                $('.modal-title').text("Console output");
                $('.modal').modal();
                $.post(url).fail(function (r) {
                    $(p).find('.fa').addClass('hidden');
                    $(p).find('.label').attr('class', 'label label-warning').html('broken');
                    $(p).addClass('warning');
                    dashboard.info.show(i, 'dude something went wrong ... try again or send us a feedback');
                    dashboard.info.text('warning', stage, '%ERROR_MESSAGE%');
                    log.error(r);
                }).done(function (r) {
                    var spinner;
                    $('.modal-body .shell').html("").show();
                    spinner = '.fa-spinner';
                    $(spinner).show();
                    dashboard.stages.showLog('.modal-body .shell', r, 0, spinner, '.modal', estimate);

                });

                return false;

            },
            showLog: function (element, id, index, spinner, parent, estimate) {
                dashboard.stages.fetchLog(element, id, index, spinner, 0, parent);

                //dashboard.stages.progressbar.updater(".modal-footer", estimate, new Date())
            },

            progressbar: {
                updater: function (parent, estimate, start) {
                    var done, percent, text;
                    done = new Date() - start;
                    percent = Math.round(done / estimate * 100);
                    text = "ETA " + Math.round(estimate / 1000 - done / 1000) + "sec.";
                    dashboard.stages.progressbar.update(parent, percent, text)
                    if (percent <= 100) {
                        setTimeout(function () {
                            dashboard.stages.progressbar.updater(parent, estimate, start);
                        }, 200);
                    }

                },
                update: function (parent, percent, text) {
                    parent = $(parent);
                    if (parent.find(".logprogress").size() > 0) {
                        var element;
                        element = parent.find('.progress-bar');
                        element.attr('aria-valuenow', percent);
                        element.attr('style', "width:" + percent + "%");
                        element.text(text)
                    } else {
                        parent.append('
' +
                            '
' +
                            text + '
' + text + '
');
                    }
                },
            },
            fetchLog: function (element, id, index, spinner, lastSize, parent) {
                $.get("/processes/" + id + "/log", {"index": index}).done(function (data, status, response) {
                    newSize = response.getResponseHeader("X-Size");
                    if (newSize != lastSize) {
                        lastSize = newSize;
                        $(element).append(data);
                        $(parent).animate({scrollTop: $(element).height()}, 'fast');
                    }
                    if ("true" == response.getResponseHeader("X-Running")) {
                        setTimeout(function () {
                            dashboard.stages.fetchLog(element, id, index, spinner, lastSize, parent);
                        }, 1000);
                    } else {
                        $(spinner).hide();
                    }
                });

            },

            filter: function (search) {
                if (search !== '') {
                    var activeFilter;
                    activeFilter = dashboard.bootstrap.activeFilter;
                    document.location.hash = '#dashboard:search=' + search;

                    $('#all-stages').find('tr.stage').addClass('hidden');
                    $('#empty').addClass('hidden');

                    found = 0;

                    $('#all-stages').find('tr.stage' + (activeFilter == 'all' ? '' : '.' + activeFilter)).each(function () {
                        if ($(this).attr('data-extractionurl').indexOf(search) > -1 || $(this).attr('data-user').indexOf(search) > -1 || $(this).find('td.name span').html().indexOf(search) > -1) {
                            $(this).removeClass('hidden');
                            found++;
                        }
                    });

                    if (found === 0) {
                        $('#empty').removeClass('hidden');
                    }

                } else {
                    document.location.hash = '#dashboard';
                    $('#empty').addClass('hidden');
                    $('#all-stages').find('tr.stage' + (activeFilter == 'all' ? '' : '.' + activeFilter)).removeClass('hidden');
                }

            }
        },
        feedback: {
            init: function () {
                $('#feedback-submit').bind('click', function (e) {
                    e.preventDefault();
                    var text = $('#feedback-text');
                    if (text.val() != "") {
                        $.post("/feedback", { message: text.val()})
                            .done(function () {
                                text.parent().parent().prepend("
Thanks.
");
                                text.val("");
                            })
                            .fail(function () {
                                text.parent().parent()
                                    .prepend("
Opps, something wen't wrong =(.
");
                            });
                    }
                });
            }
        },

        interval: {
            withCounter: function (count) {
                return 5000;
            }
        }

    };
    dashboard.init();
});
<|endoftext|>


<commit_before>'use strict';

module.exports = [
	{
		"type": "BLANK",
		"gi": {
			"code": "BLANK",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		}
	},
	{
		"type": "SOAJS",
		"gi": {
			"code": "SOAJS",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		},
		"controller": {},
		"nginx": {}
	},
	{
		"type": "PORTAL",
		"gi": {
			"code": "PORTAL",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		},
		"cluster": {},
		"controller": {},
		"urac": {},
		"oauth": {},
		"nginx": {},
		"productize": {},
		"user": {}
	}
];<commit_msg>minor update in template type values<commit_after>'use strict';

module.exports = [
	{
		"type": "_BLANK",
		"gi": {
			"code": "BLANK",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		}
	},
	{
		"type": "_SOAJS",
		"gi": {
			"code": "SOAJS",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		},
		"controller": {},
		"nginx": {}
	},
	{
		"type": "_PORTAL",
		"gi": {
			"code": "PORTAL",
			"deployPortal": true
		},
		"deploy": {
			"deployment": {},
			"selectedDriver": ""
		},
		"cluster": {},
		"controller": {},
		"urac": {},
		"oauth": {},
		"nginx": {},
		"productize": {},
		"user": {}
	}
];<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactCSSTransitionGroup from 'react-addons-transition-group';
import {unstable_renderSubtreeIntoContainer, unmountComponentAtNode} from 'react-dom';

import Theme from '../Theme';

import PopupBody from './PopupBody';

import './Popup.css';

export default class Popup extends Component {

    constructor(props) {

        super(props);

        this.wrapper = null;
        this.element = null;

        this.renderWrapper = this::this.renderWrapper;
        this.renderer = this::this.renderer;
        this.renderElement = this::this.renderElement;
        this.unrender = this::this.unrender;
        this.requestCloseHandle = this::this.requestCloseHandle;

    }

    renderWrapper() {

        if (this.wrapper) {
            return;
        }

        const wrapper = document.querySelector('.popup-container');
        if (wrapper) {
            this.wrapper = wrapper;
        } else {
            this.wrapper = document.createElement('div');
            this.wrapper.className = 'popup-container';
            document.body.appendChild(this.wrapper);
        }

    }

    renderElement() {

        if (!this.props.visible) {
            return;
        }

        this.renderWrapper();
        this.element = unstable_renderSubtreeIntoContainer(this, this.renderer(), this.wrapper);

    }

    unrender() {

        if (!this.wrapper) {
            return;
        }

        unmountComponentAtNode(this.wrapper);
        document.body.removeChild(this.wrapper);
        this.element = null;
        this.wrapper = null;

    }

    requestCloseHandle() {

        const {onRequestClose} = this.props;

        this.unrender();
        onRequestClose && onRequestClose();

    }

    renderer() {
        return (
            <ReactCSSTransitionGroup component="div">
                <PopupBody {...this.props}
                           onRequestClose={this.requestCloseHandle}/>
            </ReactCSSTransitionGroup>
        );
    }

    componentDidUpdate() {
        this.renderElement();
    }

    componentWillUnmount() {
        this.unrender();
    }

    render() {
        return null;
    }

};

Popup.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Object.keys(Theme).map(key => Theme[key])),

    /**
     * The popover alignment.The value can be Popup.Position.LEFT or Popup.Position.RIGHT.
     */
    position: PropTypes.oneOf(Object.keys(PopupBody.Position).map(key => PopupBody.Position[key])),

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func

};

Popup.defaultProps = {

    className: '',
    style: null,

    triggerEl: null,
    visible: false,
    hasTriangle: true,
    theme: Theme.DEFAULT,
    position: PopupBody.Position.LEFT

};<commit_msg>update Popup<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactCSSTransitionGroup from 'react-addons-transition-group';
import {unstable_renderSubtreeIntoContainer, unmountComponentAtNode} from 'react-dom';

import Theme from '../Theme';

import PopupBody from './PopupBody';

import './Popup.css';

export default class Popup extends Component {

    constructor(props) {

        super(props);

        this.wrapper = null;
        this.element = null;

        this.renderWrapper = this::this.renderWrapper;
        this.renderer = this::this.renderer;
        this.renderElement = this::this.renderElement;
        this.unrender = this::this.unrender;
        this.requestCloseHandle = this::this.requestCloseHandle;

    }

    renderWrapper() {

        if (this.wrapper) {
            return;
        }

        const wrapper = document.querySelector('.popup-container');
        if (wrapper) {
            this.wrapper = wrapper;
        } else {
            this.wrapper = document.createElement('div');
            this.wrapper.className = 'popup-container';
            document.body.appendChild(this.wrapper);
        }

    }

    renderElement() {

        if (!this.props.visible) {
            return;
        }

        this.renderWrapper();
        this.element = unstable_renderSubtreeIntoContainer(this, this.renderer(), this.wrapper);

    }

    unrender() {

        if (!this.wrapper) {
            return;
        }

        unmountComponentAtNode(this.wrapper);
        document.body.removeChild(this.wrapper);
        this.element = null;
        this.wrapper = null;

    }

    requestCloseHandle() {

        const {onRequestClose} = this.props;

        this.unrender();
        onRequestClose && onRequestClose();

    }

    renderer() {
        return (
            <ReactCSSTransitionGroup component="div">
                <PopupBody {...this.props}
                           onRequestClose={this.requestCloseHandle}/>
            </ReactCSSTransitionGroup>
        );
    }

    componentDidUpdate() {
        this.renderElement();
    }

    componentWillUnmount() {
        this.unrender();
    }

    render() {
        return null;
    }

};

Popup.Position = {
    LEFT: 'left',
    RIGHT: 'right'
};

Popup.propTypes = {

    /**
     * The CSS class name of the root element.
     */
    className: PropTypes.string,

    /**
     * Override the styles of the root element.
     */
    style: PropTypes.object,

    /**
     * This is the DOM element that will be used to set the position of the popover.
     */
    triggerEl: PropTypes.object,

    /**
     * If true,the popover is visible.
     */
    visible: PropTypes.bool,

    /**
     * If true,the popover will have a triangle on the top of the DOM element.
     */
    hasTriangle: PropTypes.bool,

    /**
     * The popover theme.Can be primary,highlight,success,warning,error.
     */
    theme: PropTypes.oneOf(Object.keys(Theme).map(key => Theme[key])),

    /**
     * The popover alignment.The value can be Popup.Position.LEFT or Popup.Position.RIGHT.
     */
    position: PropTypes.oneOf(Object.keys(Popup.Position).map(key => Popup.Position[key])),

    /**
     * Callback function fired when the popover is requested to be closed.
     */
    onRequestClose: PropTypes.func

};

Popup.defaultProps = {

    className: '',
    style: null,

    triggerEl: null,
    visible: false,
    hasTriangle: true,
    theme: Theme.DEFAULT,
    position: Popup.Position.LEFT

};<|endoftext|>


<commit_before>/**
 * @file Popup component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';

import Portal from '../Portal';
import PopupBody from '../_PopupBody';
import Position from '../_statics/Position';

export default class Popup extends Component {

    static Position = Position;
    static TriggerMode = PopupBody.TriggerMode;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {
        return this.props.visible ?
            <Portal>
                <PopupBody {...this.props}/>
            </Portal>
            :
            null;
    }

};

Popup.propTypes = PopupBody.propTypes;
Popup.defaultProps = PopupBody.defaultProps;<commit_msg>optimize Popup<commit_after>/**
 * @file Popup component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';

import Portal from '../Portal';
import PopupBody from '../_PopupBody';
import Position from '../_statics/Position';
import TriggerMode from '../_statics/TriggerMode';

export default class Popup extends Component {

    static Position = Position;
    static TriggerMode = TriggerMode;

    constructor(props, ...restArgs) {
        super(props, ...restArgs);
    }

    render() {
        return this.props.visible ?
            <Portal>
                <PopupBody {...this.props}/>
            </Portal>
            :
            null;
    }

};

Popup.propTypes = PopupBody.propTypes;
Popup.defaultProps = PopupBody.defaultProps;<|endoftext|>


<commit_before>'use strict';
var https = require( 'https' ),
    GitHub = {
        users : {
            kokarn : {
                nick : 'Kokarn',
                id : false
            },
            gyran : {
                nick: 'Gyran',
                id : false
            },
            theseal: {
                nick : 'TheSeal',
                id : false
            },
            jwilsson : {
                nick : 'McGurk',
                id : false
            }
        },
        loadInterval : false,
        githubConfig : false,
        characters : {
            'lastMessage' : '┗',
            'message' : '┣'
        },
        start : function(){
            GitHub.getAllUsers();

            this.loadInterval = setInterval( function(){
                GitHub.getAllUsers();
            }, 60000 );
        },
        setup : function( bot ){
            this.githubConfig = require( './githubconfig.json' )[ 0 ];
            this.bot = bot;

            if( this.githubConfig === false ){
                console.log( 'Failed to load github config, skipping github setup' );
            } else {
                this.start();
            }
        },
        rightPad: function ( myString, size, character ) {
            myString = myString.toString();

            while( myString.length < size ) {
                myString = myString + character;
            }

            return myString;
        },
        handleResponse : function( responseData, user ){
            // We can't use camelCase here because of the vars coming from GitHub
            /*jshint camelcase: false */
            var message = false,
                commitText = 'commit',
                messageStart,
                commitMessage;

            if( this.users[ user ].id !== false ){
                switch( responseData[ 0 ].type ){
                    case 'PushEvent':

                        if( responseData[ 0 ].payload.commits.length > 1 ){
                            commitText = commitText + 's';
                        }
                        message = this.users[ user ].nick + ' pushed ' + responseData[ 0 ].payload.commits.length + ' ' + commitText + ' to ' + responseData[ 0 ].repo.name;
                        for( var i = 0; i < responseData[ 0 ].payload.commits.length; i = i + 1 ){
                            messageStart = '    ';
                            if( i === responseData[ 0 ].payload.commits.length - 1 ){
                                messageStart = messageStart + this.characters.lastMessage + '   ';
                            } else {
                                messageStart = messageStart + this.characters.message + '   ';
                            }

                            commitMessage = responseData[ 0 ].payload.commits[ i ].message.replace( /(\r\n|\n\r|\n\n|\r\r|\r|\n)/gm, this.rightPad( '\n', messageStart.length + 1, ' ' ) );

                            message = message + '\n' + messageStart + commitMessage;
                        }
                        break;
                    case 'CreateEvent':
                        switch( responseData[ 0 ].payload.ref_type ){
                            case 'repository':
                                message = this.users[ user ].nick + ' created repository ' + responseData[ 0 ].repo.name;
                                break;
                            case 'branch':
                                /* falls through */
                            default:
                                break;
                        }
                        break;
                    case 'PullRequestEvent' :
                        message = this.users[ user ].nick + ' created a pull request for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.pull_request.title + '"';
                        break;
                    case 'IssuesEvent':
                        switch( responseData[ 0 ].payload.action ){
                            case 'opened':
                                message = this.users[ user ].nick + ' created an issue for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.issue.title + '"';
                                break;
                            default:
                                break;
                        }
                        break;
                    case 'IssueCommentEvent':
                        //message = this.users[ user ].nick + ' commented on an issue for ' + responseData[ 0 ].repo.name;
                        //break;
                        /* falls through */
                    default:
                        break;
                }
            }

            this.users[ user ].id = responseData[ 0 ].id;

            if( message !== false ){
                this.say( message );
            }
        },
        say : function( message ){
            message = '\u0002GitHub:\u000F ' + message;

            // Say this in every channel it's connected to
            for( var channel in this.bot.opt.channels ){
                if( this.bot.opt.channels.hasOwnProperty( channel ) ){
                    this.bot.say( this.bot.opt.channels[ channel ], message );
                }
            }
        },
        getAllUsers : function(){
            var index = 0;

            for( var user in GitHub.users ){
                if( GitHub.users.hasOwnProperty( user ) ){
                    setTimeout( GitHub.getUserEvents( user ), index * 1000 );
                    index = index + 1;
                }
            }
        },
        getUserEvents : function( user ){
            var latestResponse = '',
                latestParsedResponse = '',
                request;

            request = https.get( {
                    hostname: 'api.github.com',
                    port: 443,
                    path: '/users/' + user + '/events?client_id=' + this.githubConfig.clientId + '&client_secret=' + this.githubConfig.clientSecret,
                    method: 'GET',
                    headers: {
                        'User-Agent': 'Kokarn'
                    }
                } );

            request.on( 'error', function( data ){
                console.log( data );
            });

            request.on( 'response', function( response ) {
                console.log( 'Got response ' + response.statusCode + ' for user "' + user + '". Request remaining until reset: ' + response.headers[ 'x-ratelimit-remaining' ] );

                response.on( 'data' , function( chunk ) {
                    latestResponse = latestResponse + chunk.toString();
                });

                response.on( 'error' , function( data ) {
                    console.log( data );
                });

                response.on( 'end', function(){
                    latestParsedResponse = JSON.parse( latestResponse );

                    if( typeof latestParsedResponse[ 0 ].id !== 'undefined' && latestParsedResponse[ 0 ].id !== GitHub.users[ user ].id ){
                        GitHub.handleResponse( latestParsedResponse, user );
                    }
                });
            });
        }
    };

module.exports = GitHub;
<commit_msg>Changed to a more appropriate User agent<commit_after>'use strict';
var https = require( 'https' ),
    GitHub = {
        users : {
            kokarn : {
                nick : 'Kokarn',
                id : false
            },
            gyran : {
                nick: 'Gyran',
                id : false
            },
            theseal: {
                nick : 'TheSeal',
                id : false
            },
            jwilsson : {
                nick : 'McGurk',
                id : false
            }
        },
        loadInterval : false,
        githubConfig : false,
        characters : {
            'lastMessage' : '┗',
            'message' : '┣'
        },
        start : function(){
            GitHub.getAllUsers();

            this.loadInterval = setInterval( function(){
                GitHub.getAllUsers();
            }, 60000 );
        },
        setup : function( bot ){
            this.githubConfig = require( './githubconfig.json' )[ 0 ];
            this.bot = bot;

            if( this.githubConfig === false ){
                console.log( 'Failed to load github config, skipping github setup' );
            } else {
                this.start();
            }
        },
        rightPad: function ( myString, size, character ) {
            myString = myString.toString();

            while( myString.length < size ) {
                myString = myString + character;
            }

            return myString;
        },
        handleResponse : function( responseData, user ){
            // We can't use camelCase here because of the vars coming from GitHub
            /*jshint camelcase: false */
            var message = false,
                commitText = 'commit',
                messageStart,
                commitMessage;

            if( this.users[ user ].id !== false ){
                switch( responseData[ 0 ].type ){
                    case 'PushEvent':

                        if( responseData[ 0 ].payload.commits.length > 1 ){
                            commitText = commitText + 's';
                        }
                        message = this.users[ user ].nick + ' pushed ' + responseData[ 0 ].payload.commits.length + ' ' + commitText + ' to ' + responseData[ 0 ].repo.name;
                        for( var i = 0; i < responseData[ 0 ].payload.commits.length; i = i + 1 ){
                            messageStart = '    ';
                            if( i === responseData[ 0 ].payload.commits.length - 1 ){
                                messageStart = messageStart + this.characters.lastMessage + '   ';
                            } else {
                                messageStart = messageStart + this.characters.message + '   ';
                            }

                            commitMessage = responseData[ 0 ].payload.commits[ i ].message.replace( /(\r\n|\n\r|\n\n|\r\r|\r|\n)/gm, this.rightPad( '\n', messageStart.length + 1, ' ' ) );

                            message = message + '\n' + messageStart + commitMessage;
                        }
                        break;
                    case 'CreateEvent':
                        switch( responseData[ 0 ].payload.ref_type ){
                            case 'repository':
                                message = this.users[ user ].nick + ' created repository ' + responseData[ 0 ].repo.name;
                                break;
                            case 'branch':
                                /* falls through */
                            default:
                                break;
                        }
                        break;
                    case 'PullRequestEvent' :
                        message = this.users[ user ].nick + ' created a pull request for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.pull_request.title + '"';
                        break;
                    case 'IssuesEvent':
                        switch( responseData[ 0 ].payload.action ){
                            case 'opened':
                                message = this.users[ user ].nick + ' created an issue for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.issue.title + '"';
                                break;
                            default:
                                break;
                        }
                        break;
                    case 'IssueCommentEvent':
                        //message = this.users[ user ].nick + ' commented on an issue for ' + responseData[ 0 ].repo.name;
                        //break;
                        /* falls through */
                    default:
                        break;
                }
            }

            this.users[ user ].id = responseData[ 0 ].id;

            if( message !== false ){
                this.say( message );
            }
        },
        say : function( message ){
            message = '\u0002GitHub:\u000F ' + message;

            // Say this in every channel it's connected to
            for( var channel in this.bot.opt.channels ){
                if( this.bot.opt.channels.hasOwnProperty( channel ) ){
                    this.bot.say( this.bot.opt.channels[ channel ], message );
                }
            }
        },
        getAllUsers : function(){
            var index = 0;

            for( var user in GitHub.users ){
                if( GitHub.users.hasOwnProperty( user ) ){
                    setTimeout( GitHub.getUserEvents( user ), index * 1000 );
                    index = index + 1;
                }
            }
        },
        getUserEvents : function( user ){
            var latestResponse = '',
                latestParsedResponse = '',
                request;

            request = https.get( {
                    hostname: 'api.github.com',
                    port: 443,
                    path: '/users/' + user + '/events?client_id=' + this.githubConfig.clientId + '&client_secret=' + this.githubConfig.clientSecret,
                    method: 'GET',
                    headers: {
                    }
                } );
                    'User-Agent': 'KokBot'

            request.on( 'error', function( data ){
                console.log( data );
            });

            request.on( 'response', function( response ) {
                console.log( 'Got response ' + response.statusCode + ' for user "' + user + '". Request remaining until reset: ' + response.headers[ 'x-ratelimit-remaining' ] );

                response.on( 'data' , function( chunk ) {
                    latestResponse = latestResponse + chunk.toString();
                });

                response.on( 'error' , function( data ) {
                    console.log( data );
                });

                response.on( 'end', function(){
                    latestParsedResponse = JSON.parse( latestResponse );

                    if( typeof latestParsedResponse[ 0 ].id !== 'undefined' && latestParsedResponse[ 0 ].id !== GitHub.users[ user ].id ){
                        GitHub.handleResponse( latestParsedResponse, user );
                    }
                });
            });
        }
    };

module.exports = GitHub;
<|endoftext|>


<commit_before>'use strict';
var https = require( 'https' ),
    GitHub = {
        users : {
            kokarn : {
                nick : 'Kokarn',
                id : false
            },
            gyran : {
                nick: 'Gyran',
                id : false
            },
            theseal: {
                nick : 'TheSeal',
                id : false
            },
            jwilsson : {
                nick : 'McGurk',
                id : false
            }
        },
        loadInterval : false,
        githubConfig : false,
        characters : {
            'lastMessage' : '┗',
            'message' : '┣'
        },
        start : function(){
            GitHub.getAllUsers();

            this.loadInterval = setInterval( function(){
                GitHub.getAllUsers();
            }, 60000 );
        },
        setup : function( bot ){
            this.githubConfig = require( './githubconfig.json' )[ 0 ];
            this.bot = bot;

            if( this.githubConfig === false ){
                console.log( 'Failed to load github config, skipping github setup' );
            } else {
                this.start();
            }
        },
        rightPad: function ( myString, size, character ) {
            myString = myString.toString();

            while( myString.length < size ) {
                myString = myString + character;
            }

            return myString;
        },
        handleResponse : function( responseData, user ){
            var message = false,
                commitText = 'commit',
                messageStart,
                commitMessage;

            if( this.users[ user ].id !== false ){
                switch( responseData[ 0 ].type ){
                    case 'PushEvent':

                        if( responseData[ 0 ].payload.commits.length > 1 ){
                            commitText = commitText + 's';
                        }
                        message = this.users[ user ].nick + ' pushed ' + responseData[ 0 ].payload.commits.length + ' ' + commitText + ' to ' + responseData[ 0 ].repo.name;
                        for( var i = 0; i < responseData[ 0 ].payload.commits.length; i = i + 1 ){
                            messageStart = '    ';
                            if( i === responseData[ 0 ].payload.commits.length - 1 ){
                                messageStart = messageStart + this.characters.lastMessage + '   ';
                            } else {
                                messageStart = messageStart + this.characters.message + '   ';
                            }

                            commitMessage = responseData[ 0 ].payload.commits[ i ].message.replace( /(\r\n|\n\r|\n\n|\r\r|\r|\n)/gm, this.rightPad( '\n', messageStart.length + 1, ' ' ) );

                            message = message + '\n' + messageStart + commitMessage;
                        }
                        break;
                    case 'CreateEvent':
                        switch( responseData[ 0 ].payload.ref_type ){
                            case 'repository':
                                message = this.users[ user ].nick + ' created repository ' + responseData[ 0 ].repo.name;
                                break;
                            case 'branch':
                            default:
                                break;
                        }
                        break;
                    case 'PullRequestEvent' :
                        message = this.users[ user ].nick + ' created a pull request for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.pull_request.title + '"';
                        break;
                    case 'IssuesEvent':
                        switch( responseData[ 0 ].payload.action ){
                            case 'opened':
                                message = this.users[ user ].nick + ' created an issue for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.issue.title + '"';
                                break;
                            default:
                                break;
                        }
                        break;
                    case 'IssueCommentEvent':
                        //message = this.users[ user ].nick + ' commented on an issue for ' + responseData[ 0 ].repo.name;
                        //break;
                    default:
                        break;
                }
            }

            this.users[ user ].id = responseData[ 0 ].id;

            if( message !== false ){
                this.say( message );
            }
        },
        say : function( message ){
            message = '\u0002GitHub:\u000F ' + message;

            // Say this in every channel it's connected to
            for( var channel in this.bot.opt.channels ){
                if( this.bot.opt.channels.hasOwnProperty( channel ) ){
                    this.bot.say( this.bot.opt.channels[ channel ], message );
                }
            }
        },
        getAllUsers : function(){
            for( var user in GitHub.users ){
                if( GitHub.users.hasOwnProperty( user ) ){
                    GitHub.getUserEvents( user );
                }
            }
        },
        getUserEvents : function( user ){
            var latestResponse = '',
                latestParsedResponse = '';

            https.get( {
                    hostname: 'api.github.com',
                    port: 443,
                    path: '/users/' + user + '/events?client_id=' + this.githubConfig.client_id + '&client_secret=' + this.githubConfig.client_secret,
                    method: 'GET',
                    headers: {
                        'User-Agent': 'Kokarn'
                    }
                }, function( response ) {
                    console.log( 'Got response ' + response.statusCode + ' for user "' + user + '". Request remaining until reset: ' + response.headers[ 'x-ratelimit-remaining' ] );

                    response.on( 'data' , function( chunk ) {
                        latestResponse = latestResponse + chunk.toString();
                    });

                    response.on( 'error' , function( data ) {
                        console.log( data );
                    });

                    response.on( 'end', function(){
                        latestParsedResponse = JSON.parse( latestResponse );

                        if( latestParsedResponse[ 0 ].id !== GitHub.users[ user ].id ){
                            GitHub.handleResponse( latestParsedResponse, user );
                        }
                    });
                }
            );
        }
    };

module.exports = GitHub;
<commit_msg>Fix jsHint errors<commit_after>'use strict';
var https = require( 'https' ),
    GitHub = {
        users : {
            kokarn : {
                nick : 'Kokarn',
                id : false
            },
            gyran : {
                nick: 'Gyran',
                id : false
            },
            theseal: {
                nick : 'TheSeal',
                id : false
            },
            jwilsson : {
                nick : 'McGurk',
                id : false
            }
        },
        loadInterval : false,
        githubConfig : false,
        characters : {
            'lastMessage' : '┗',
            'message' : '┣'
        },
        start : function(){
            GitHub.getAllUsers();

            this.loadInterval = setInterval( function(){
                GitHub.getAllUsers();
            }, 60000 );
        },
        setup : function( bot ){
            this.githubConfig = require( './githubconfig.json' )[ 0 ];
            this.bot = bot;

            if( this.githubConfig === false ){
                console.log( 'Failed to load github config, skipping github setup' );
            } else {
                this.start();
            }
        },
        rightPad: function ( myString, size, character ) {
            myString = myString.toString();

            while( myString.length < size ) {
                myString = myString + character;
            }

            return myString;
        },
        handleResponse : function( responseData, user ){
            // We can't use camelCase here because of the vars coming from GitHub
            /*jshint camelcase: false */
            var message = false,
                commitText = 'commit',
                messageStart,
                commitMessage;

            if( this.users[ user ].id !== false ){
                switch( responseData[ 0 ].type ){
                    case 'PushEvent':

                        if( responseData[ 0 ].payload.commits.length > 1 ){
                            commitText = commitText + 's';
                        }
                        message = this.users[ user ].nick + ' pushed ' + responseData[ 0 ].payload.commits.length + ' ' + commitText + ' to ' + responseData[ 0 ].repo.name;
                        for( var i = 0; i < responseData[ 0 ].payload.commits.length; i = i + 1 ){
                            messageStart = '    ';
                            if( i === responseData[ 0 ].payload.commits.length - 1 ){
                                messageStart = messageStart + this.characters.lastMessage + '   ';
                            } else {
                                messageStart = messageStart + this.characters.message + '   ';
                            }

                            commitMessage = responseData[ 0 ].payload.commits[ i ].message.replace( /(\r\n|\n\r|\n\n|\r\r|\r|\n)/gm, this.rightPad( '\n', messageStart.length + 1, ' ' ) );

                            message = message + '\n' + messageStart + commitMessage;
                        }
                        break;
                    case 'CreateEvent':
                        switch( responseData[ 0 ].payload.ref_type ){
                            case 'repository':
                                message = this.users[ user ].nick + ' created repository ' + responseData[ 0 ].repo.name;
                                break;
                            case 'branch':
                                /* falls through */
                            default:
                                break;
                        }
                        break;
                    case 'PullRequestEvent' :
                        message = this.users[ user ].nick + ' created a pull request for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.pull_request.title + '"';
                        break;
                    case 'IssuesEvent':
                        switch( responseData[ 0 ].payload.action ){
                            case 'opened':
                                message = this.users[ user ].nick + ' created an issue for ' + responseData[ 0 ].repo.name + ' titled "' + responseData[ 0 ].payload.issue.title + '"';
                                break;
                            default:
                                break;
                        }
                        break;
                    case 'IssueCommentEvent':
                        //message = this.users[ user ].nick + ' commented on an issue for ' + responseData[ 0 ].repo.name;
                        //break;
                        /* falls through */
                    default:
                        break;
                }
            }

            this.users[ user ].id = responseData[ 0 ].id;

            if( message !== false ){
                this.say( message );
            }
        },
        say : function( message ){
            message = '\u0002GitHub:\u000F ' + message;

            // Say this in every channel it's connected to
            for( var channel in this.bot.opt.channels ){
                if( this.bot.opt.channels.hasOwnProperty( channel ) ){
                    this.bot.say( this.bot.opt.channels[ channel ], message );
                }
            }
        },
        getAllUsers : function(){
            for( var user in GitHub.users ){
                if( GitHub.users.hasOwnProperty( user ) ){
                    GitHub.getUserEvents( user );
                }
            }
        },
        getUserEvents : function( user ){
            var latestResponse = '',
                latestParsedResponse = '';

            https.get( {
                    hostname: 'api.github.com',
                    port: 443,
                    path: '/users/' + user + '/events?client_id=' + this.githubConfig.clientId + '&client_secret=' + this.githubConfig.clientSecret,
                    method: 'GET',
                    headers: {
                        'User-Agent': 'Kokarn'
                    }
                }, function( response ) {
                    console.log( 'Got response ' + response.statusCode + ' for user "' + user + '". Request remaining until reset: ' + response.headers[ 'x-ratelimit-remaining' ] );

                    response.on( 'data' , function( chunk ) {
                        latestResponse = latestResponse + chunk.toString();
                    });

                    response.on( 'error' , function( data ) {
                        console.log( data );
                    });

                    response.on( 'end', function(){
                        latestParsedResponse = JSON.parse( latestResponse );

                        if( latestParsedResponse[ 0 ].id !== GitHub.users[ user ].id ){
                            GitHub.handleResponse( latestParsedResponse, user );
                        }
                    });
                }
            );
        }
    };

module.exports = GitHub;
<|endoftext|>


<commit_before>/**
 * Gookie
 * Lightweight & configurable GitHub Webhook Server
 * Created by Liam Gray (@hoxxep)
 * Released under the MIT License.
 */

var exec = require('child_process').exec,
    bodyParser = require('body-parser'),
    express = require('express'),
    app = express();

// Logging levels
var VERBOSE = true;

var repos;

function main() {
    /**
     * Load config and start serving
     * TODO: verbose/quiet command line args
     * TODO: add support for multiple events
     * TODO: add special thing for ping event
     */
    var config = loadConfig();
    server(config.port);
}

function loadConfig() {
    /**
     * Load users settings and add defaults
     * @type {{port: number, repositories: Array}}
     * @return config merged with defaults
     */

    var defaults = {
        "port": 8001,
        "repositories": []
    };

    var repo_defaults = {
        "url": "https://github.com/hoxxep/Gookie",
        "path": "~/Documents/Projects/Gookie",
        "deploy": "git pull",
        "secret": ""
    };

    var config = require('./config.json');
    config = merge(defaults, config);

    repos = {};
    for (var repo in config['repositories']) {
        repos[config['repositories'][repo].url] = merge(repo_defaults, config['repositories'][repo]);
    }

    return defaults;
}

function merge(object1, object2) {
    /**
     * Merge attributes of two objects
     * @param object1: default object
     * @param object2: customised object
     * @type {{}}
     * @return object1 overwritten by object2
     */
    var attr,
        object3 = {};
    for (attr in object1) {object3[attr] = object1[attr]}
    for (attr in object2) {object3[attr] = object2[attr]}
    return object3;
}

function server(port) {
    /**
     * Start listening for hook
     * TODO: stop server from sending errors to client
     * @param port: port to start Gookie server on
     */
    app.use(bodyParser.json({extended: false}));

    app.route('/')
        .get(function(req, res) {
            console.log(timePrefix() + 'GET request sent from ' + req.connection.remoteAddress);
            res.status(400).send('<title>Gookie</title>
Your Gookie server is running.
').end();
        })
        .post(function(req, res) {
            try {
                validateRequest(req.body);

                if (VERBOSE) console.log(timePrefix() + 'user ' + req.body.pusher.name + ' pushed to ' + req.body.repository.url);

                res.status(204).end();
            } catch (e) {
                console.log(timePrefix() + e);
                res.status(400).send('Bad request.').end();
                return;
            }

            matchRepo(req.body);
        });

    app.listen(port);
    if (VERBOSE) console.log(timePrefix() + 'Server started on port ' + port);
}

function validateRequest(json) {
    /**
     * Validate GitHub JSON by checking repo matches etc.
     * TODO: support for secret
     * TODO: better parsing of GitHub url
     * @param json: decoded JSON request body
     * @throws error when invalid JSON
     */
    if (!json.hasOwnProperty('repository') || !json.repository.hasOwnProperty('url')) {
        throw 'ERROR: invalid JSON!';
    }
    if (!repos.hasOwnProperty(json.repository.url)) {
        throw 'ERROR: ' + json.repository.url + ' just sent a webhook but is not configured?';
    }
}

function matchRepo(json) {
    /**
     * Match repo in JSON to deploy command
     * @param json: decoded JSON request body
     */
    var repo = repos[json.repository.url];
    deploy(repo.path, repo.deploy);
}

function deploy(directory, command) {
    /**
     * Run deploy command in git repo
     * Execs: cd "directory" && command
     * @param directory: directory of git repo
     * @param command: deploy command to run
     */
    command = 'cd "' + directory + '" && ' + command;
    if (VERBOSE) console.log(timePrefix() + command);
    exec(command, function(error, stdout, stderr) {
        if (VERBOSE && stdout) console.log(stdout);
        if (stderr) console.log(stderr);
        if (error) console.log(error);
        if (VERBOSE) console.log(timePrefix() + 'end of deploy output');
    });
}

function timePrefix() {
    /**
     * Time prefix for console.log
     * @return prefix in the form of ' [hh:mm:ss] '
     */
    var date = new Date();

    var hour = date.getHours();
    hour = (hour < 10 ? "0" : "") + hour;

    var min  = date.getMinutes();
    min = (min < 10 ? "0" : "") + min;

    var sec  = date.getSeconds();
    sec = (sec < 10 ? "0" : "") + sec;

    return ' [' + hour + ':' + min + ':' + sec + '] ';
}

main();<commit_msg>Changed GET request to return 204 response code<commit_after>/**
 * Gookie
 * Lightweight & configurable GitHub Webhook Server
 * Created by Liam Gray (@hoxxep)
 * Released under the MIT License.
 */

var exec = require('child_process').exec,
    bodyParser = require('body-parser'),
    express = require('express'),
    app = express();

// Logging levels
var VERBOSE = true;

var repos;

function main() {
    /**
     * Load config and start serving
     * TODO: verbose/quiet command line args
     * TODO: add support for multiple events
     * TODO: add special thing for ping event
     */
    var config = loadConfig();
    server(config.port);
}

function loadConfig() {
    /**
     * Load users settings and add defaults
     * @type {{port: number, repositories: Array}}
     * @return config merged with defaults
     */

    var defaults = {
        "port": 8001,
        "repositories": []
    };

    var repo_defaults = {
        "url": "https://github.com/hoxxep/Gookie",
        "path": "~/Documents/Projects/Gookie",
        "deploy": "git pull",
        "secret": ""
    };

    var config = require('./config.json');
    config = merge(defaults, config);

    repos = {};
    for (var repo in config['repositories']) {
        repos[config['repositories'][repo].url] = merge(repo_defaults, config['repositories'][repo]);
    }

    return defaults;
}

function merge(object1, object2) {
    /**
     * Merge attributes of two objects
     * @param object1: default object
     * @param object2: customised object
     * @type {{}}
     * @return object1 overwritten by object2
     */
    var attr,
        object3 = {};
    for (attr in object1) {object3[attr] = object1[attr]}
    for (attr in object2) {object3[attr] = object2[attr]}
    return object3;
}

function server(port) {
    /**
     * Start listening for hook
     * TODO: stop server from sending errors to client
     * @param port: port to start Gookie server on
     */
    app.use(bodyParser.json({extended: false}));

    app.route('/')
        .get(function(req, res) {
            console.log(timePrefix() + 'GET request sent from ' + req.connection.remoteAddress);
            res.status(204).send('<title>Gookie</title>
Your Gookie server is running.
').end();
        })
        .post(function(req, res) {
            try {
                validateRequest(req.body);

                if (VERBOSE) console.log(timePrefix() + 'user ' + req.body.pusher.name + ' pushed to ' + req.body.repository.url);

                res.status(204).end();
            } catch (e) {
                console.log(timePrefix() + e);
                res.status(400).send('Bad request.').end();
                return;
            }

            matchRepo(req.body);
        });

    app.listen(port);
    if (VERBOSE) console.log(timePrefix() + 'Server started on port ' + port);
}

function validateRequest(json) {
    /**
     * Validate GitHub JSON by checking repo matches etc.
     * TODO: support for secret
     * TODO: better parsing of GitHub url
     * @param json: decoded JSON request body
     * @throws error when invalid JSON
     */
    if (!json.hasOwnProperty('repository') || !json.repository.hasOwnProperty('url')) {
        throw 'ERROR: invalid JSON!';
    }
    if (!repos.hasOwnProperty(json.repository.url)) {
        throw 'ERROR: ' + json.repository.url + ' just sent a webhook but is not configured?';
    }
}

function matchRepo(json) {
    /**
     * Match repo in JSON to deploy command
     * @param json: decoded JSON request body
     */
    var repo = repos[json.repository.url];
    deploy(repo.path, repo.deploy);
}

function deploy(directory, command) {
    /**
     * Run deploy command in git repo
     * Execs: cd "directory" && command
     * @param directory: directory of git repo
     * @param command: deploy command to run
     */
    command = 'cd "' + directory + '" && ' + command;
    if (VERBOSE) console.log(timePrefix() + command);
    exec(command, function(error, stdout, stderr) {
        if (VERBOSE && stdout) console.log(stdout);
        if (stderr) console.log(stderr);
        if (error) console.log(error);
        if (VERBOSE) console.log(timePrefix() + 'end of deploy output');
    });
}

function timePrefix() {
    /**
     * Time prefix for console.log
     * @return prefix in the form of ' [hh:mm:ss] '
     */
    var date = new Date();

    var hour = date.getHours();
    hour = (hour < 10 ? "0" : "") + hour;

    var min  = date.getMinutes();
    min = (min < 10 ? "0" : "") + min;

    var sec  = date.getSeconds();
    sec = (sec < 10 ? "0" : "") + sec;

    return ' [' + hour + ':' + min + ':' + sec + '] ';
}

main();<|endoftext|>