<commit_before>(function($, _, Backbone, Handlebars, markdown, jsPDF) {

  /*jshint unused:false */
  'use strict';

  // ---------------------------------------------------------------------------
  // Namespace
  // ---------------------------------------------------------------------------

  var App = window.App = {
    dataURL     : window.APP_BASE_URL + 'data.json',
    templates   : {},
    models      : {},
    collections : {},
    views       : {}
  };

  // ---------------------------------------------------------------------------
  // Templates
  // ---------------------------------------------------------------------------

  App.templates.candidateCardTemplate    = Handlebars.compile($('#candidate-card-template').html());
  App.templates.runningMateListTemplate  = Handlebars.compile($('#running-mate-list-template').html());
  App.templates.candidateProgramTemplate = Handlebars.compile($('#candidate-program-template').html());
  App.templates.candidateListTemplate    = Handlebars.compile($('#candidate-list-template').html());
  App.templates.candidateDetailTemplate  = Handlebars.compile($('#candidate-detail-template').html());
  App.templates.themeDetailTemplate      = Handlebars.compile($('#theme-detail-template').html());
  App.templates.themeListTemplate        = Handlebars.compile($('#theme-list-template').html());
  App.templates.aboutTemplate            = Handlebars.compile($('#about-template').html());

  //----------------------------------------------------------------------------
  // Template Helpers
  // ---------------------------------------------------------------------------

  Handlebars.registerHelper('md2html', function(md) {
    return new Handlebars.SafeString(markdown.toHTML(md));
  });

  // ---------------------------------------------------------------------------
  // Models
  // ---------------------------------------------------------------------------

  App.models.ThemeModel       = Backbone.Model.extend({});
  App.models.CandidateModel   = Backbone.Model.extend({});
  App.models.RunningMateModel = Backbone.Model.extend({});
  App.models.ProgramModel     = Backbone.Model.extend({});

  // ---------------------------------------------------------------------------
  // Collections
  // ---------------------------------------------------------------------------

  App.collections.ThemeCollection = Backbone.Collection.extend({

    model: App.models.ThemeModel,
    url: App.dataURL,

    parse: function(res) {
      return res.themes;
    }
  });

  App.collections.CandidateCollection = Backbone.Collection.extend({

    model: App.models.CandidateModel,
    url: App.dataURL,

    parse: function(res) {
      return res.candidates;
    }
  });

  App.collections.RunningMateCollection = Backbone.Collection.extend({

    model: App.models.RunningMateModel,
    url: App.dataURL,

    parse: function(res) {
      return res.runningMates;
    },

    findByCandidate: function(id) {
      var models = this.filter(function(model) { return model.get('candidate').id === id; });
      return new App.collections.RunningMateCollection(models);
    }
  });

  App.collections.ProgramCollection = Backbone.Collection.extend({

    model: App.models.ProgramModel,
    url: App.dataURL,

    parse: function(response) {
      return response.programs;
    },

    findByCandidate: function(id) {
      return this.find(function(model) {
        if (model.get('candidate')) return model.get('candidate').id === id;
      });
    },

    findByTheme: function(id) {
      var models = [];
      this.each(function(model) {
        var projects = _.filter(model.get('projects'), function(project) {
          return project.theme.id === id;
        });
        if (projects.length > 0) {
          model.attributes.projects = projects;
          models.push(model);
        }
      });
      return new App.collections.ProgramCollection(models);
    },

    findByThemeAndGroupByCandidate: function(id) {
      var models = this.findByTheme(id);
      models = models.groupBy(function(m) { return m.get('candidate').name; });
      Object.keys(models).forEach(function(key) {
        models[key] = models[key][0].toJSON();
      });
      return models;
    },

    candidateProjects: function(id) {
      var model = this.findByCandidate(id);
      if (!model) return;
      var projects = model.get('projects');
      return _.groupBy(projects, function(obj) { return obj.theme.name; });
    }
  });

  // ---------------------------------------------------------------------------
  // Views
  // ---------------------------------------------------------------------------

  App.views.CandidateCardView = Backbone.View.extend({

    tagName   : 'div',
    className : 'candidate-card',

    initialize: function(options) {
      this.options = _.extend({showDetailLink: false}, options);
      this.showDetailLink = options.showDetailLink;
      this.template = App.templates.candidateCardTemplate;
      this.render();
    },

    render: function() {
      this.$el.html(this.template({
        model: this.model.toJSON(),
        showDetailLink: this.showDetailLink
      }));
    }
  });

  App.views.RunningMateListView = Backbone.View.extend({

    tagName: 'div',
    className: 'running-mate-list',

    initialize: function(options) {

      this.options = _.extend({
        modelId: null,
        runningMates: new App.collections.RunningMateCollection()
      }, options);

      this.modelId = this.options.modelId;
      this.runningMates = this.options.runningMates;
      this.template = App.templates.runningMateListTemplate;

      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.runningMates = this.collection.findByCandidate(this.modelId);
      this.render();
    },

    render: function() {
      this.$el.html(this.template({runningMates: this.runningMates.toJSON()}));
    }
  });

  App.views.CandidateProgramView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-program',

    initialize: function(options) {
      this.options = _.extend({modelId: null}, options);
      this.modelId = this.options.modelId;
      this.template = App.templates.candidateProgramTemplate;
      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.projects = this.collection.candidateProjects(this.modelId);
      this.render();
    },

    render: function() {
      this.$el.html(this.template({projects: this.projects}));
    }
  });

  App.views.CandidateListView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-list',

    initialize: function(options) {
      this.options = options || {};
      this.template = App.templates.candidateListTemplate;
      this.listenTo(this.collection, 'sync', this.render);
      this.collection.fetch();
    },

    render: function() {
      this.$el.empty();
      this.$el.html(this.template());
      this.collection.each(function(model) {
        var view = new App.views.CandidateCardView({
          tagName: 'li',
          model: model,
          showDetailLink: model.get('programUrl') ? true : false
        });
        this.$el.find('.candidate-list-container').append(view.el);
      }.bind(this));
    }
  });

  App.views.CandidateDetailView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-detail',

    events: {
      'click a.makePDF': 'makePDF'
    },

    initialize: function(options) {

      this.options = _.extend({
        modelId: null,
        programs: new App.collections.ProgramCollection(),
        runningMates: new App.collections.RunningMateCollection()
      }, options);

      this.modelId = this.options.modelId;
      this.programs = this.options.programs;
      this.runningMates = this.options.runningMates;
      this.template = App.templates.candidateDetailTemplate;

      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    makePDF: function() {
      /*jshint newcap: false */
      var doc = new jsPDF();
      doc.text(20, 20, 'This PDF has a title');
      doc.setProperties({
        title    : 'TITLE',
        subject  : 'SUBJECT',
        author   : 'AUTHOR',
        keywords : 'foo, bar, ok, hello, world',
        creator  : 'Gilles Fabio <gilles@gillesfabio.com>'
      });
      doc.save('programme.pdf');
    },

    prepare: function() {

      this.model = this.collection.findWhere({id: this.modelId});

      this.cardView = new App.views.CandidateCardView({
        model: this.model,
        showDetailLink: false
      });

      this.programView = new App.views.CandidateProgramView({
        collection: this.programs,
        modelId: this.modelId
      });

      this.runningMateListView = new App.views.RunningMateListView({
        collection : this.runningMates,
        modelId: this.modelId
      });

      this.render();
    },

    render: function() {
      this.$el.html(this.template({candidate: this.model.toJSON()}));
      this.$el.find('.candidate-detail-running-mates').html(this.runningMateListView.el);
      this.$el.find('.candidate-detail-card').html(this.cardView.el);
      this.$el.find('.candidate-detail-program').html(this.programView.el);
    }
  });

  App.views.ThemeDetailView = Backbone.View.extend({

    tagName: 'div',
    className: 'theme-detail',

    initialize: function(options) {
      this.options = _.extend({modelId: null, programs: null}, options);
      this.modelId = this.options.modelId;
      this.programs = this.options.programs;
      this.template = App.templates.themeDetailTemplate;
      this.theme = null;
      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.theme = this.collection.findWhere({id: this.modelId});
      if (!this.theme) return this.notFound();
      this.programs.fetch({success: function(collection) {
        this.groupedPrograms = this.programs.findByThemeAndGroupByCandidate(this.modelId);
        this.render();
      }.bind(this)});
    },

    notFound: function() {
      this.$el.html("Désolé, ce thème n'existe pas.");
    },

    render: function() {
      this.$el.html(this.template({
        theme: this.theme.toJSON(),
        programs: this.groupedPrograms
      }));
    }
  });

  App.views.ThemeListView = Backbone.View.extend({

    tagName: 'div',
    className: 'theme-list',

    initialize: function(options) {
      this.options = options || {};
      this.template = App.templates.themeListTemplate;
      this.listenTo(this.collection, 'sync', this.render);
      this.collection.fetch();
    },

    render: function() {
      this.$el.html(this.template(this.collection.toJSON()));
    }
  });

  // ---------------------------------------------------------------------------
  // Router
  // ---------------------------------------------------------------------------

  App.Router = Backbone.Router.extend({

    routes: {
      ''              : 'candidateListController',
      'candidats'     : 'candidateListController',
      'candidats/:id' : 'candidateDetailController',
      'themes'        : 'themeListController',
      'themes/:id'    : 'themeDetailController',
      'a-propos'      : 'aboutController'
    },

    initialize: function() {
      this.themes = new App.collections.ThemeCollection();
      this.candidates = new App.collections.CandidateCollection();
      this.runningMates = new App.collections.RunningMateCollection();
      this.programs = new App.collections.ProgramCollection();
      this.content = $('#content');
    },

    candidateListController: function() {
      var view = new App.views.CandidateListView({collection: this.candidates});
      this.content.html(view.el);
    },

    candidateDetailController: function(id) {
      var view = new App.views.CandidateDetailView({
        modelId: id,
        collection: this.candidates,
        programs: this.programs,
        runningMates: this.runningMates
      });
      this.content.html(view.el);
    },

    themeDetailController: function(id) {
      var view = new App.views.ThemeDetailView({
        collection: this.themes,
        modelId: id,
        programs: this.programs
      });
      this.content.html(view.el);
    },

    themeListController: function() {
      var view = new App.views.ThemeListView({collection: this.themes});
      this.content.html(view.el);
    },

    aboutController: function() {
      this.content.html(Handlebars.compile(App.templates.AboutTemplate)());
    }
  });

  // ---------------------------------------------------------------------------
  // Foundation CSS Framework
  // ---------------------------------------------------------------------------

  $(document).foundation();

  // ---------------------------------------------------------------------------
  // DOM Ready
  // ---------------------------------------------------------------------------

  $(function() {
    var router = new App.Router();
    Backbone.history.start({root: window.APP_BASE_URL});
  });

})(jQuery, _, Backbone, Handlebars, markdown, jsPDF);
<commit_msg>Reformat code.<commit_after>(function($, _, Backbone, Handlebars, markdown, jsPDF) {

  /*jshint unused:false */
  'use strict';

  // ---------------------------------------------------------------------------
  // Namespace
  // ---------------------------------------------------------------------------

  var App = window.App = {
    dataURL: window.APP_BASE_URL + 'data.json',
    templates: {},
    models: {},
    collections: {},
    views: {}
  };

  // ---------------------------------------------------------------------------
  // Templates
  // ---------------------------------------------------------------------------

  App.templates.candidateCardTemplate = Handlebars.compile($('#candidate-card-template').html());
  App.templates.runningMateListTemplate = Handlebars.compile($('#running-mate-list-template').html());
  App.templates.candidateProgramTemplate = Handlebars.compile($('#candidate-program-template').html());
  App.templates.candidateListTemplate = Handlebars.compile($('#candidate-list-template').html());
  App.templates.candidateDetailTemplate = Handlebars.compile($('#candidate-detail-template').html());
  App.templates.themeDetailTemplate = Handlebars.compile($('#theme-detail-template').html());
  App.templates.themeListTemplate = Handlebars.compile($('#theme-list-template').html());
  App.templates.aboutTemplate = Handlebars.compile($('#about-template').html());

  //----------------------------------------------------------------------------
  // Template Helpers
  // ---------------------------------------------------------------------------

  Handlebars.registerHelper('md2html', function(md) {
    return new Handlebars.SafeString(markdown.toHTML(md));
  });

  // ---------------------------------------------------------------------------
  // Models
  // ---------------------------------------------------------------------------

  App.models.ThemeModel = Backbone.Model.extend({});
  App.models.CandidateModel = Backbone.Model.extend({});
  App.models.RunningMateModel = Backbone.Model.extend({});
  App.models.ProgramModel = Backbone.Model.extend({});

  // ---------------------------------------------------------------------------
  // Collections
  // ---------------------------------------------------------------------------

  App.collections.ThemeCollection = Backbone.Collection.extend({

    model: App.models.ThemeModel,
    url: App.dataURL,

    parse: function(res) {
      return res.themes;
    }
  });

  App.collections.CandidateCollection = Backbone.Collection.extend({

    model: App.models.CandidateModel,
    url: App.dataURL,

    parse: function(res) {
      return res.candidates;
    }
  });

  App.collections.RunningMateCollection = Backbone.Collection.extend({

    model: App.models.RunningMateModel,
    url: App.dataURL,

    parse: function(res) {
      return res.runningMates;
    },

    findByCandidate: function(id) {
      var models = this.filter(function(model) { return model.get('candidate').id === id; });
      return new App.collections.RunningMateCollection(models);
    }
  });

  App.collections.ProgramCollection = Backbone.Collection.extend({

    model: App.models.ProgramModel,
    url: App.dataURL,

    parse: function(response) {
      return response.programs;
    },

    findByCandidate: function(id) {
      return this.find(function(model) {
        if (model.get('candidate')) return model.get('candidate').id === id;
      });
    },

    findByTheme: function(id) {
      var models = [];
      this.each(function(model) {
        var projects = _.filter(model.get('projects'), function(project) {
          return project.theme.id === id;
        });
        if (projects.length > 0) {
          model.attributes.projects = projects;
          models.push(model);
        }
      });
      return new App.collections.ProgramCollection(models);
    },

    findByThemeAndGroupByCandidate: function(id) {
      var models = this.findByTheme(id);
      models = models.groupBy(function(m) { return m.get('candidate').name; });
      Object.keys(models).forEach(function(key) {
        models[key] = models[key][0].toJSON();
      });
      return models;
    },

    candidateProjects: function(id) {
      var model = this.findByCandidate(id);
      if (!model) return;
      var projects = model.get('projects');
      return _.groupBy(projects, function(obj) { return obj.theme.name; });
    }
  });

  // ---------------------------------------------------------------------------
  // Views
  // ---------------------------------------------------------------------------

  App.views.CandidateCardView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-card',

    initialize: function(options) {
      this.options = _.extend({showDetailLink: false}, options);
      this.showDetailLink = options.showDetailLink;
      this.template = App.templates.candidateCardTemplate;
      this.render();
    },

    render: function() {
      this.$el.html(this.template({
        model: this.model.toJSON(),
        showDetailLink: this.showDetailLink
      }));
    }
  });

  App.views.RunningMateListView = Backbone.View.extend({

    tagName: 'div',
    className: 'running-mate-list',

    initialize: function(options) {

      this.options = _.extend({
        modelId: null,
        runningMates: new App.collections.RunningMateCollection()
      }, options);

      this.modelId = this.options.modelId;
      this.runningMates = this.options.runningMates;
      this.template = App.templates.runningMateListTemplate;

      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.runningMates = this.collection.findByCandidate(this.modelId);
      this.render();
    },

    render: function() {
      this.$el.html(this.template({runningMates: this.runningMates.toJSON()}));
    }
  });

  App.views.CandidateProgramView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-program',

    initialize: function(options) {
      this.options = _.extend({modelId: null}, options);
      this.modelId = this.options.modelId;
      this.template = App.templates.candidateProgramTemplate;
      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.projects = this.collection.candidateProjects(this.modelId);
      this.render();
    },

    render: function() {
      this.$el.html(this.template({projects: this.projects}));
    }
  });

  App.views.CandidateListView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-list',

    initialize: function(options) {
      this.options = options || {};
      this.template = App.templates.candidateListTemplate;
      this.listenTo(this.collection, 'sync', this.render);
      this.collection.fetch();
    },

    render: function() {
      this.$el.empty();
      this.$el.html(this.template());
      this.collection.each(function(model) {
        var view = new App.views.CandidateCardView({
          tagName: 'li',
          model: model,
          showDetailLink: model.get('programUrl') ? true : false
        });
        this.$el.find('.candidate-list-container').append(view.el);
      }.bind(this));
    }
  });

  App.views.CandidateDetailView = Backbone.View.extend({

    tagName: 'div',
    className: 'candidate-detail',

    events: {
      'click a.makePDF': 'makePDF'
    },

    initialize: function(options) {

      this.options = _.extend({
        modelId: null,
        programs: new App.collections.ProgramCollection(),
        runningMates: new App.collections.RunningMateCollection()
      }, options);

      this.modelId = this.options.modelId;
      this.programs = this.options.programs;
      this.runningMates = this.options.runningMates;
      this.template = App.templates.candidateDetailTemplate;

      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    makePDF: function() {
      /*jshint newcap: false */
      var doc = new jsPDF();
      doc.text(20, 20, 'This PDF has a title');
      doc.setProperties({
        title    : 'TITLE',
        subject  : 'SUBJECT',
        author   : 'AUTHOR',
        keywords : 'foo, bar, ok, hello, world',
        creator  : 'Gilles Fabio <gilles@gillesfabio.com>'
      });
      doc.save('programme.pdf');
    },

    prepare: function() {
      this.model = this.collection.findWhere({id: this.modelId});
      this.cardView = new App.views.CandidateCardView({
        model: this.model,
        showDetailLink: false
      });
      this.programView = new App.views.CandidateProgramView({
        collection: this.programs,
        modelId: this.modelId
      });
      this.runningMateListView = new App.views.RunningMateListView({
        collection : this.runningMates,
        modelId: this.modelId
      });
      this.render();
    },

    render: function() {
      this.$el.html(this.template({candidate: this.model.toJSON()}));
      this.$el.find('.candidate-detail-running-mates').html(this.runningMateListView.el);
      this.$el.find('.candidate-detail-card').html(this.cardView.el);
      this.$el.find('.candidate-detail-program').html(this.programView.el);
    }
  });

  App.views.ThemeDetailView = Backbone.View.extend({

    tagName: 'div',
    className: 'theme-detail',

    initialize: function(options) {
      this.options = _.extend({modelId: null, programs: null}, options);
      this.modelId = this.options.modelId;
      this.programs = this.options.programs;
      this.template = App.templates.themeDetailTemplate;
      this.theme = null;
      this.listenTo(this.collection, 'sync', this.prepare);
      this.collection.fetch();
    },

    prepare: function() {
      this.theme = this.collection.findWhere({id: this.modelId});
      if (!this.theme) return this.notFound();
      this.programs.fetch({success: function(collection) {
        this.groupedPrograms = this.programs.findByThemeAndGroupByCandidate(this.modelId);
        this.render();
      }.bind(this)});
    },

    notFound: function() {
      this.$el.html("Désolé, ce thème n'existe pas.");
    },

    render: function() {
      this.$el.html(this.template({
        theme: this.theme.toJSON(),
        programs: this.groupedPrograms
      }));
    }
  });

  App.views.ThemeListView = Backbone.View.extend({

    tagName: 'div',
    className: 'theme-list',

    initialize: function(options) {
      this.options = options || {};
      this.template = App.templates.themeListTemplate;
      this.listenTo(this.collection, 'sync', this.render);
      this.collection.fetch();
    },

    render: function() {
      this.$el.html(this.template(this.collection.toJSON()));
    }
  });

  // ---------------------------------------------------------------------------
  // Router
  // ---------------------------------------------------------------------------

  App.Router = Backbone.Router.extend({

    routes: {
      ''              : 'candidateListController',
      'candidats'     : 'candidateListController',
      'candidats/:id' : 'candidateDetailController',
      'themes'        : 'themeListController',
      'themes/:id'    : 'themeDetailController',
      'a-propos'      : 'aboutController'
    },

    initialize: function() {
      this.themes = new App.collections.ThemeCollection();
      this.candidates = new App.collections.CandidateCollection();
      this.runningMates = new App.collections.RunningMateCollection();
      this.programs = new App.collections.ProgramCollection();
      this.content = $('#content');
    },

    candidateListController: function() {
      var view = new App.views.CandidateListView({collection: this.candidates});
      this.content.html(view.el);
    },

    candidateDetailController: function(id) {
      var view = new App.views.CandidateDetailView({
        modelId: id,
        collection: this.candidates,
        programs: this.programs,
        runningMates: this.runningMates
      });
      this.content.html(view.el);
    },

    themeDetailController: function(id) {
      var view = new App.views.ThemeDetailView({
        collection: this.themes,
        modelId: id,
        programs: this.programs
      });
      this.content.html(view.el);
    },

    themeListController: function() {
      var view = new App.views.ThemeListView({collection: this.themes});
      this.content.html(view.el);
    },

    aboutController: function() {
      this.content.html(Handlebars.compile(App.templates.AboutTemplate)());
    }
  });

  // ---------------------------------------------------------------------------
  // Foundation CSS Framework
  // ---------------------------------------------------------------------------

  $(document).foundation();

  // ---------------------------------------------------------------------------
  // DOM Ready
  // ---------------------------------------------------------------------------

  $(function() {
    var router = new App.Router();
    Backbone.history.start({root: window.APP_BASE_URL});
  });

})(jQuery, _, Backbone, Handlebars, markdown, jsPDF);
<|endoftext|>


<commit_before>// Generated on 2014-10-21 using generator-angular 0.9.8
'use strict';

var shell = require('shelljs');

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  var pkg = grunt.file.readJSON('package.json');
  var archiveBaseName = pkg.name + '-' + pkg.version;

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist',
    archive: archiveBaseName
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/,*/*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/,*/*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/,*/*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729,
      },
      server: {
        proxies: [
          {
            context: '/d',
            host: process.env.GROONGA_HOST || 'localhost',
            port: 10041
          }
        ]
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              require('grunt-connect-proxy/lib/utils').proxyRequest,
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp',
      archive: '<%= yeoman.archive %>'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/images/generated',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: './bower_components',
        httpImagesPath: '/images',
        httpGeneratedImagesPath: '/images/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%= yeoman.dist %>/images/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      },
      archive: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.dist %>',
          dest: '<%= yeoman.archive %>/html',
          src: [
            '**'
          ]
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          dest: '<%= yeoman.archive %>',
          src: [
            'LICENSE',
            'README.md',
            'doc/**/*'
          ]
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          dest: '<%= yeoman.archive %>/source',
          src: [
            '.bowerrc',
            '.jshintrc',
            'Gruntfile.js',
            'bower.json',
            'package.json',
            '<%= yeoman.app %>/**/*',
            '!<%= yeoman.app %>/styles/*.css',
            '!<%= yeoman.app %>/styles/.sass-cache/**/*'
          ]
        }]
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    },

    // Release settings
    release: {
      options: {
        bump: false,
        add: false,
        commit: false,
        npm: false
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'configureProxies:server',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'filerev',
    'usemin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);

  grunt.registerTask('archive', 'Archive distributed files', function () {
    shell.exec('tar cvzf ' + archiveBaseName + '.tar.gz ' + archiveBaseName);
  });

  grunt.registerTask('archive:upload', 'Upload archive', function () {
    shell.exec('scp ' +
               archiveBaseName + '.tar.gz ' +
               'packages@packages.groonga.org:public/source/groonga-admin/');
    shell.exec('ssh ' +
               'packages@packages.groonga.org ' +
               'ln -fs ' + archiveBaseName + '.tar.gz ' +
               'public/source/groonga-admin/' + pkg.name + '.tar.gz');
  });

  grunt.registerTask('git:tag', 'Tag the current version', function () {
    var news = grunt.file.read('doc/text/news.md', {'encoding': 'utf8'});
    var releaseNote = '## ' + news.split(/^## /m)[1];
    var releaseNoteFileName = 'release-note.md';
    grunt.file.write(releaseNoteFileName, releaseNote);
    shell.exec('git tag --annotate ' +
               '--file ' + releaseNoteFileName + ' ' +
               pkg.version);
    grunt.file.delete(releaseNoteFileName);
    shell.exec('git push --tags');
  });

  grunt.registerTask('release:package', 'Release archived package', [
    'build',
    'clean:archive',
    'copy:archive',
    'archive',
    'clean:archive',
    'archive:upload',
    'git:tag'
  ]);
};
<commit_msg>Add missing code<commit_after>// Generated on 2014-10-21 using generator-angular 0.9.8
'use strict';

var shell = require('shelljs');

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  var pkg = grunt.file.readJSON('package.json');
  var archiveBaseName = pkg.name + '-' + pkg.version;

  // Configurable paths for the application
  var appConfig = {
    app: require('./bower.json').appPath || 'app',
    dist: 'dist',
    archive: archiveBaseName
  };

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: appConfig,

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      bower: {
        files: ['bower.json'],
        tasks: ['wiredep']
      },
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/,*/*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: '<%= connect.options.livereload %>'
        }
      },
      jsTest: {
        files: ['test/spec/{,*/,*/*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      compass: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        tasks: ['compass:server', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/,*/*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729,
      },
      server: {
        proxies: [
          {
            context: '/d',
            host: process.env.GROONGA_HOST || 'localhost',
            port: 10041
          }
        ]
      },
      livereload: {
        options: {
          open: true,
          middleware: function (connect) {
            return [
              require('grunt-connect-proxy/lib/utils').proxyRequest,
              connect.static('.tmp'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      test: {
        options: {
          port: 9001,
          middleware: function (connect) {
            return [
              connect.static('.tmp'),
              connect.static('test'),
              connect().use(
                '/bower_components',
                connect.static('./bower_components')
              ),
              connect.static(appConfig.app)
            ];
          }
        }
      },
      dist: {
        options: {
          open: true,
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: {
        src: [
          'Gruntfile.js',
          '<%= yeoman.app %>/scripts/{,*/}*.js'
        ]
      },
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/{,*/}*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp',
      archive: '<%= yeoman.archive %>'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    wiredep: {
      app: {
        src: ['<%= yeoman.app %>/index.html'],
        ignorePath:  /\.\.\//
      },
      sass: {
        src: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
        ignorePath: /(\.\.\/){1,2}bower_components\//
      }
    },

    // Compiles Sass to CSS and generates necessary files if requested
    compass: {
      options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        generatedImagesDir: '.tmp/images/generated',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: './bower_components',
        httpImagesPath: '/images',
        httpGeneratedImagesPath: '/images/generated',
        httpFontsPath: '/styles/fonts',
        relativeAssets: false,
        assetCacheBuster: false,
        raw: 'Sass::Script::Number.precision = 10\n'
      },
      dist: {
        options: {
          generatedImagesDir: '<%= yeoman.dist %>/images/generated'
        }
      },
      server: {
        options: {
          debugInfo: true
        }
      }
    },

    // Renames files for browser caching purposes
    filerev: {
      dist: {
        src: [
          '<%= yeoman.dist %>/scripts/{,*/}*.js',
          '<%= yeoman.dist %>/styles/{,*/}*.css',
          '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
          '<%= yeoman.dist %>/styles/fonts/*'
        ]
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>',
        flow: {
          html: {
            steps: {
              js: ['concat'],
              css: ['cssmin']
            },
            post: {}
          }
        }
      }
    },

    // Performs rewrites based on filerev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images']
      }
    },

    // The following *-min tasks will produce minified files in the dist folder
    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },

    // ng-annotate tries to make the code safe for minification automatically
    // by using the Angular long form for dependency injection.
    ngAnnotate: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: ['*.js', '!oldieshim.js'],
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }, {
          expand: true,
          cwd: '.',
          src: 'bower_components/bootstrap-sass-official/assets/fonts/bootstrap/*',
          dest: '<%= yeoman.dist %>'
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      },
      archive: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.dist %>',
          dest: '<%= yeoman.archive %>/html',
          src: [
            '**'
          ]
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          dest: '<%= yeoman.archive %>',
          src: [
            'LICENSE',
            'README.md',
            'doc/**/*'
          ]
        }, {
          expand: true,
          dot: true,
          cwd: '.',
          dest: '<%= yeoman.archive %>/source',
          src: [
            '.bowerrc',
            '.jshintrc',
            'Gruntfile.js',
            'bower.json',
            'package.json',
            '<%= yeoman.app %>/**/*',
            '!<%= yeoman.app %>/styles/*.css',
            '!<%= yeoman.app %>/styles/.sass-cache/**/*'
          ]
        }]
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'compass:server'
      ],
      test: [
        'compass'
      ],
      dist: [
        'compass:dist',
        'imagemin',
        'svgmin'
      ]
    },

    // Test settings
    karma: {
      unit: {
        configFile: 'test/karma.conf.js',
        singleRun: true
      }
    },

    // Release settings
    release: {
      options: {
        bump: false,
        add: false,
        commit: false,
        npm: false
      }
    }
  });

  grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'wiredep',
      'configureProxies:server',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve:' + target]);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'wiredep',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngAnnotate',
    'copy:dist',
    'cdnify',
    'cssmin',
    'filerev',
    'usemin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);

  grunt.registerTask('archive', 'Archive distributed files', function () {
    shell.exec('tar cvzf ' + archiveBaseName + '.tar.gz ' + archiveBaseName);
  });

  grunt.registerTask('archive:upload', 'Upload archive', function () {
    shell.exec('scp ' +
               archiveBaseName + '.tar.gz ' +
               'packages@packages.groonga.org:public/source/groonga-admin/');
    shell.exec('ssh ' +
               'packages@packages.groonga.org ' +
               'ln -fs ' + archiveBaseName + '.tar.gz ' +
               'public/source/groonga-admin/' + pkg.name + '.tar.gz');
  });

  grunt.registerTask('git:tag', 'Tag the current version', function () {
    var news = grunt.file.read('doc/text/news.md', {'encoding': 'utf8'});
    var releaseNote = '## ' + news.split(/^## /m)[1];
    var releaseNoteFileName = 'release-note.md';
    grunt.file.write(releaseNoteFileName, releaseNote);
    shell.exec('git tag --annotate ' +
               '--file ' + releaseNoteFileName + ' ' +
               pkg.version);
    grunt.file.delete(releaseNoteFileName);
    shell.exec('git push --tags');
  });

  grunt.registerTask('release:package', 'Release archived package', [
    'build',
    'clean:archive',
    'copy:archive',
    'archive',
    'clean:archive',
    'archive:upload',
    'git:tag'
  ]);
};
<|endoftext|>


<commit_before>const gulp =        require('gulp');
const clean =       require('gulp-clean');
const vulcanize =   require('gulp-vulcanize');
const gulpCopy =    require('gulp-copy');
const manifest =    require('gulp-manifest');

gulp.task('default', ['clean', 'vulcanize', 'copy', 'manifest']);

gulp.task('clean', function () {
    return gulp.src('dist/*', {read: false})
        .pipe(clean());
});

gulp.task('vulcanize', ['clean'], function () {
    return gulp.src('src/index.html')
        .pipe(vulcanize({
            excludes: ['socket.io/socket.io.js'],
            stripExcludes: []
        }))
        .pipe(gulp.dest('dist'));
});

gulp.task('copy', ['clean'], function () {
    return gulp
        .src([
            'src/bower_components/async/dist/async.min.js',
            'src/bower_components/webcomponentsjs/webcomponents-lite.js',
            'src/bower_components/web-animations-js/web-animations-next-lite.min.js',
            'src/bower_components/components-font-awesome/css/font-awesome.min.css',
            'src/bower_components/components-font-awesome/fonts/*',
            'src/index.js'
        ])
        .pipe(gulpCopy('dist/', {prefix: 1}))
});

gulp.task('manifest', ['vulcanize', 'copy'], function (){
    gulp.src(['dist/**/*'], { base: './dist' })
        .pipe(manifest({
            hash: true,
            timestamp: false,
            preferOnline: false,
            network: ['*'],
            filename: 'app.manifest',
            exclude: 'dist/app.manifest'
        }))
        .pipe(gulp.dest('dist'));
});
<commit_msg>add promise polyfill to dist<commit_after>const gulp =        require('gulp');
const clean =       require('gulp-clean');
const vulcanize =   require('gulp-vulcanize');
const gulpCopy =    require('gulp-copy');
const manifest =    require('gulp-manifest');

gulp.task('default', ['clean', 'vulcanize', 'copy', 'manifest']);

gulp.task('clean', function () {
    return gulp.src('dist/*', {read: false})
        .pipe(clean());
});

gulp.task('vulcanize', ['clean'], function () {
    return gulp.src('src/index.html')
        .pipe(vulcanize({
            excludes: ['socket.io/socket.io.js'],
            stripExcludes: []
        }))
        .pipe(gulp.dest('dist'));
});

gulp.task('copy', ['clean'], function () {
    return gulp
        .src([
            'src/bower_components/async/dist/async.min.js',
            'src/bower_components/webcomponentsjs/webcomponents-lite.js',
            'src/bower_components/web-animations-js/web-animations-next-lite.min.js',
            'src/bower_components/components-font-awesome/css/font-awesome.min.css',
            'src/bower_components/promise-polyfill/Promise.js',
            'src/bower_components/promise-polyfill/Promise-Statics.js',
            'src/bower_components/components-font-awesome/fonts/*',
            'src/index.js'
        ])
        .pipe(gulpCopy('dist/', {prefix: 1}))
});

gulp.task('manifest', ['vulcanize', 'copy'], function (){
    gulp.src(['dist/**/*'], { base: './dist' })
        .pipe(manifest({
            hash: true,
            timestamp: false,
            preferOnline: false,
            network: ['*'],
            filename: 'app.manifest',
            exclude: 'dist/app.manifest'
        }))
        .pipe(gulp.dest('dist'));
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
const program         = require('commander')
const globby          = require('globby')
const applyEachSeries = require('async/applyEachSeries')
const queue           = require('async/queue')
const path            = require('path')
const fs              = require('fs')
const scrolex         = require('scrolex')
const debug           = require('depurar')('invig')
const pkgUp           = require('pkg-up')

const rootDir         = path.dirname(pkgUp.sync(__filename))
const npmDir          = `${rootDir}/node_modules`
const npmBinDir       = `${npmDir}/.bin`
const untildify       = require('untildify')

program
  .version(require('../package.json').version)
  .option('-s, --src <dir>', 'Directory or file to convert. DESTRUCTIVE. MAKE SURE IT\'S UNDER SOURCE CONTROL. ')
  .option('-d, --dryrun', 'Wether to execute commands or just output them')
  // .option('-c, --concurrency <int>', 'Directory to convert. DESTRUCTIVE. MAKE SURE IT\'S UNDER SOURCE CONTROL. ')
  .parse(process.argv)

if (!program.src) {
  console.error('You should provide at least a --src <dir> argument')
  process.exit(1)
}
if (!program.concurrency) { program.concurrency = 1 }
if (!program.init) { program.init = false }

program.src    = untildify(program.src)
program.dryrun = !!program.dryrun

const scrolexOpts = (opts) => {
  const defaultOpts = {}
  defaultOpts.mode     = 'singlescroll'
  // defaultOpts.mode     = 'passthru'
  defaultOpts.shell    = true
  defaultOpts.announce = false
  defaultOpts.fatal    = true
  if (program.dryrun === true) {
    defaultOpts.announce = true
    defaultOpts.dryrun   = true
  }
  return Object.assign({}, defaultOpts, opts)
}

const initProject = (projectPackagePath, cb) => {
  const projectPackage = require(projectPackagePath)
  const projectRoot    = path.dirname(projectPackagePath)
  const projectRootRel = path.relative(process.cwd(), projectRoot)
  const invigRoot      = `${__dirname}/..`
  // const invigRootRel   = path.relative(process.cwd(), invigRoot)
  const invigPackage   = require(`${invigRoot}/package.json`)

  scrolex.out('Adding eslint project config', scrolexOpts({ components: `invig>${projectRootRel}>toEslintStandard` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintrc`)) {
      if (!projectPackage.eslintConfig) {
        projectPackage.eslintConfig = invigPackage.eslintConfig
      }
    }
  }

  scrolex.out('Adding npm task project config', scrolexOpts({ components: `invig>${projectRootRel}>npm` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintrc`)) {
      if (!projectPackage.scripts) {
        projectPackage.scripts = {}
      }
      if (!projectPackage.scripts.lint) {
        projectPackage.scripts.lint = 'eslint .'
      }
      if (!projectPackage.scripts.fix) {
        projectPackage.scripts.fix = 'eslint . --fix'
      }
    }
  }

  scrolex.out('Adding babel project config', scrolexOpts({ components: `invig>${projectRootRel}>toEs6` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.babelrc`)) {
      if (!projectPackage.babel) {
        projectPackage.babel = invigPackage.babel
      }
    }
  }

  scrolex.out('Writing eslint ignores', scrolexOpts({ components: `invig>${projectRootRel}>toJs` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintignore`)) {
      fs.writeFileSync(`${projectRoot}/.eslintignore`, 'utf-8', fs.readFileSync(`${invigRoot}/.eslintignore`, 'utf-8'))
    }
  }

  scrolex.out('Writing back project config ', scrolexOpts({ components: `invig>${projectRootRel}>init` }))
  fs.writeFileSync(projectPackagePath, JSON.stringify(projectPackage, null, 2), 'utf-8')

  return cb(null)
}

const toJs = (srcPath, cb) => {
  const cmd = `${npmBinDir}/decaffeinate --keep-commonjs --prefer-const --loose-default-params ${srcPath}`
  scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toJs` }), cb)
}

const toEs6 = (srcPath, cb) => {
  const safe = [
    'arrow',
    'for-of',
    'for-each',
    'arg-rest',
    'arg-spread',
    'obj-method',
    'obj-shorthand',
    'no-strict',
    'multi-var',
    // 'commonjs',
    // 'exponent',
  ]
  const unsafe = [
    'let',
    'class',
    'template',
    'default-param',
    'destruct-param',
    // 'includes'
  ]

  const list = [].concat(safe, unsafe).join(',')
  const cmd = `${npmBinDir}/lebab --transform=${list} ${srcPath} --out-file ${srcPath}.es6 && mv -f ${srcPath}.es6 ${srcPath} && echo lebabed`

  scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toEs6` }), cb)
}

const toPrettier = (srcPath, cb) => {
  const cmd = `${npmBinDir}/prettier --write ${srcPath} --fix ${srcPath}`
  scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toPrettier` }), cb)
}

const toEslintStandard = (srcPath, cb) => {
  const cmd = `${npmBinDir}/eslint --fix ${srcPath}`
  scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toEslintStandard` }), cb)
}

const convertFile = (srcPath, cb) => {
  const fns       = []
  const extension = path.extname(srcPath).toLowerCase()
  if (extension === 'coffee') {
    fns.push(toJs)
  }
  fns.push(toEs6)
  fns.push(toPrettier)
  fns.push(toEslintStandard)

  applyEachSeries(fns, srcPath, cb)
}

let files = []
let relative
if (fs.lstatSync(program.src).isFile()) {
  // File
  relative = path.relative(process.cwd(), program.src)
  files    = [ relative ]
} else if (fs.lstatSync(program.src).isDirectory()) {
  // Directory
  relative = path.relative(process.cwd(), program.src)
  files    = globby.sync([
    `${relative}/**/*.js`,
    `${relative}/**/*.coffee`,
    `${relative}/**/*.es5`,
    `${relative}/**/*.es6`,
  ])
} else {
  // Pattern
  files = globby.sync(program.src)
}

if (!files || files.length === 0) {
  console.error(`Source argument: "${program.src}" returned no input files to work on.`)
  process.exit(1)
}

initProject(pkgUp.sync(path.dirname(files[0])), (err) => {
  if (err) {
    console.error(`Error while doing project init. ${err}`)
    process.exit(1)
  }
  debug({files})
  const q = queue(convertFile, program.concurrency)
  q.push(files)
  q.drain = () =>
    console.log('Done. ')
})
<commit_msg>Casing<commit_after>#!/usr/bin/env node
const program         = require('commander')
const globby          = require('globby')
const applyEachSeries = require('async/applyEachSeries')
const queue           = require('async/queue')
const path            = require('path')
const fs              = require('fs')
const Scrolex         = require('scrolex')
const debug           = require('depurar')('invig')
const pkgUp           = require('pkg-up')

const rootDir         = path.dirname(pkgUp.sync(__filename))
const npmDir          = `${rootDir}/node_modules`
const npmBinDir       = `${npmDir}/.bin`
const untildify       = require('untildify')

program
  .version(require('../package.json').version)
  .option('-s, --src <dir>', 'Directory or file to convert. DESTRUCTIVE. MAKE SURE IT\'S UNDER SOURCE CONTROL. ')
  .option('-d, --dryrun', 'Wether to execute commands or just output them')
  // .option('-c, --concurrency <int>', 'Directory to convert. DESTRUCTIVE. MAKE SURE IT\'S UNDER SOURCE CONTROL. ')
  .parse(process.argv)

if (!program.src) {
  console.error('You should provide at least a --src <dir> argument')
  process.exit(1)
}
if (!program.concurrency) { program.concurrency = 1 }
if (!program.init) { program.init = false }

program.src    = untildify(program.src)
program.dryrun = !!program.dryrun

const scrolexOpts = (opts) => {
  const defaultOpts = {}
  defaultOpts.mode     = 'singlescroll'
  // defaultOpts.mode     = 'passthru'
  defaultOpts.shell    = true
  defaultOpts.announce = false
  defaultOpts.fatal    = true
  if (program.dryrun === true) {
    defaultOpts.announce = true
    defaultOpts.dryrun   = true
  }
  return Object.assign({}, defaultOpts, opts)
}

const initProject = (projectPackagePath, cb) => {
  const projectPackage = require(projectPackagePath)
  const projectRoot    = path.dirname(projectPackagePath)
  const projectRootRel = path.relative(process.cwd(), projectRoot)
  const invigRoot      = `${__dirname}/..`
  // const invigRootRel   = path.relative(process.cwd(), invigRoot)
  const invigPackage   = require(`${invigRoot}/package.json`)

  Scrolex.out('Adding eslint project config', scrolexOpts({ components: `invig>${projectRootRel}>toEslintStandard` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintrc`)) {
      if (!projectPackage.eslintConfig) {
        projectPackage.eslintConfig = invigPackage.eslintConfig
      }
    }
  }

  Scrolex.out('Adding npm task project config', scrolexOpts({ components: `invig>${projectRootRel}>npm` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintrc`)) {
      if (!projectPackage.scripts) {
        projectPackage.scripts = {}
      }
      if (!projectPackage.scripts.lint) {
        projectPackage.scripts.lint = 'eslint .'
      }
      if (!projectPackage.scripts.fix) {
        projectPackage.scripts.fix = 'eslint . --fix'
      }
    }
  }

  Scrolex.out('Adding babel project config', scrolexOpts({ components: `invig>${projectRootRel}>toEs6` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.babelrc`)) {
      if (!projectPackage.babel) {
        projectPackage.babel = invigPackage.babel
      }
    }
  }

  Scrolex.out('Writing eslint ignores', scrolexOpts({ components: `invig>${projectRootRel}>toJs` }))
  if (program.dryrun === false) {
    if (!fs.existsSync(`${projectRoot}/.eslintignore`)) {
      fs.writeFileSync(`${projectRoot}/.eslintignore`, 'utf-8', fs.readFileSync(`${invigRoot}/.eslintignore`, 'utf-8'))
    }
  }

  Scrolex.out('Writing back project config ', scrolexOpts({ components: `invig>${projectRootRel}>init` }))
  fs.writeFileSync(projectPackagePath, JSON.stringify(projectPackage, null, 2), 'utf-8')

  return cb(null)
}

const toJs = (srcPath, cb) => {
  const cmd = `${npmBinDir}/decaffeinate --keep-commonjs --prefer-const --loose-default-params ${srcPath}`
  Scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toJs` }), cb)
}

const toEs6 = (srcPath, cb) => {
  const safe = [
    'arrow',
    'for-of',
    'for-each',
    'arg-rest',
    'arg-spread',
    'obj-method',
    'obj-shorthand',
    'no-strict',
    'multi-var',
    // 'commonjs',
    // 'exponent',
  ]
  const unsafe = [
    'let',
    'class',
    'template',
    'default-param',
    'destruct-param',
    // 'includes'
  ]

  const list = [].concat(safe, unsafe).join(',')
  const cmd = `${npmBinDir}/lebab --transform=${list} ${srcPath} --out-file ${srcPath}.es6 && mv -f ${srcPath}.es6 ${srcPath} && echo lebabed`

  Scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toEs6` }), cb)
}

const toPrettier = (srcPath, cb) => {
  const cmd = `${npmBinDir}/prettier --write ${srcPath} --fix ${srcPath}`
  Scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toPrettier` }), cb)
}

const toEslintStandard = (srcPath, cb) => {
  const cmd = `${npmBinDir}/eslint --fix ${srcPath}`
  Scrolex.exe(cmd, scrolexOpts({ components: `invig>${srcPath}>toEslintStandard` }), cb)
}

const convertFile = (srcPath, cb) => {
  const fns       = []
  const extension = path.extname(srcPath).toLowerCase()
  if (extension === 'coffee') {
    fns.push(toJs)
  }
  fns.push(toEs6)
  fns.push(toPrettier)
  fns.push(toEslintStandard)

  applyEachSeries(fns, srcPath, cb)
}

let files = []
let relative
if (fs.lstatSync(program.src).isFile()) {
  // File
  relative = path.relative(process.cwd(), program.src)
  files    = [ relative ]
} else if (fs.lstatSync(program.src).isDirectory()) {
  // Directory
  relative = path.relative(process.cwd(), program.src)
  files    = globby.sync([
    `${relative}/**/*.js`,
    `${relative}/**/*.coffee`,
    `${relative}/**/*.es5`,
    `${relative}/**/*.es6`,
  ])
} else {
  // Pattern
  files = globby.sync(program.src)
}

if (!files || files.length === 0) {
  console.error(`Source argument: "${program.src}" returned no input files to work on.`)
  process.exit(1)
}

initProject(pkgUp.sync(path.dirname(files[0])), (err) => {
  if (err) {
    console.error(`Error while doing project init. ${err}`)
    process.exit(1)
  }
  debug({files})
  const q = queue(convertFile, program.concurrency)
  q.push(files)
  q.drain = () =>
    console.log('Done. ')
})
<|endoftext|>


<commit_before>'use strict';

const CONFIG = require('./config');

const app = require('app');
const BrowserWindow = require('browser-window');

const YoutubeApi = require('./youtubeapi');

const Hapi = require('hapi');
const isOnline = require('is-online');
const youtubeRegex = require('youtube-regex');

// Create the Hapi Web Server
const server = new Hapi.Server();
server.connection({
  host: 'localhost',
  port: '@@PORT'
});

// Start the server
server.start((err) => {
  if (err) {
    throw err;
  }

  console.log('Server running at:', server.info.uri);
});

const AUTH_WINDOW = 'auth';
const MAIN_WINDOW = 'main';

const io = require('socket.io')(server.listener);

// report crashes to the Electron project
require('crash-reporter').start();

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();

// prevent window being garbage collected
let windows = {};

function onClosed(windowName) {
  // dereference the window
  windows[windowName] = null;
}

function createWindow(windowName, url, width, height, isDevToolsOpen) {
  const win = new BrowserWindow({ width, height });

  win.loadUrl(url);
  win.on('closed', onClosed.bind(windowName));

  if (isDevToolsOpen) win.openDevTools();

  return win;
}

function createMainWindow() {
  const url = 'file://' + __dirname + '/client/index.html';

  return createWindow(
    MAIN_WINDOW,
    url,
    CONFIG.MAIN_WINDOW.WIDTH,
    CONFIG.MAIN_WINDOW.HEIGHT,
    CONFIG.MAIN_WINDOW.IS_DEV_TOOLS_OPEN
  );
}

function createLogInWindow(url) {
  return createWindow(
    AUTH_WINDOW,
    url,
    CONFIG.AUTH_WINDOW.WIDTH,
    CONFIG.AUTH_WINDOW.HEIGHT,
    CONFIG.AUTH_WINDOW.IS_DEV_TOOLS_OPEN
  );
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate-with-no-open-windows', () => {
  if (!windows[MAIN_WINDOW]) {
    windows[MAIN_WINDOW] = createMainWindow();
  }
});

app.on('ready', () => {
  windows[MAIN_WINDOW] = createMainWindow();

  io.on('connection', (socket) => {
    socket.on('youtube/auth', () => {
      socket.emit('youtube/waiting');

      YoutubeApi.getAuthUrl((url) => {
        socket.emit('youtube/waitingforuser')
        windows[AUTH_WINDOW] = createLogInWindow(url);
      });
    });

    socket.on('subscriptions/list', () => {
      YoutubeApi.getSubscriptions((err, subscriptions) => {
        if (err) {
          console.error(err);
        } else {
          socket.emit('subscriptions/list', subscriptions);
        }
      });
    });

    // Video
    socket.on('video/start', (id) => {
      console.log('Video started: ', id);
      isVideoPlaying = true;
    });

    socket.on('video/pause', (id) => {
      console.log('Video paused: ', id);
    });

    socket.on('video/buffer', (id) => {
      console.log('Video buffering: ', id);
    });

    function playVideo(video) {
      console.log('Play video: ', video.id);

      playlist.playNow(video);
      socket.emit('playlist/update', playlist);

      if (isVideoPlaying)
        socket.emit('video/play', video.id);
      else
        socket.emit('video/cue', video.id);
    }

    /* This method is called to play a video immediately */
    socket.on('video/play', playVideo);

    /* Put a video on the end of the playlist */
    socket.on('video/cue', (video) => {
      console.log('Cueing video: ', video.id);
      if (!playlist.length)
        socket.emit('video/cue', video.id);

      if (!playlist.contains(video.id)) {
        console.log('Pushing a video into the playlist (' + video.id + ')');
        playlist.push(video);
        socket.emit('playlist/update', playlist);
      }
    });

    /* Set a video as next to play */
    socket.on('video/next', (video) => {
      console.log('Set next video: ', video.id);
      if (!playlist.length)
        socket.emit('video/cue', video.id);

      if (!playlist.contains(video.id)) {
        playlist.setNext(video);
      }

      socket.emit('playlist/update', playlist);
    });

    /* When something is pasted, if it is a YouTube video, play it */
    socket.on('video/paste', (text) => {
      if (!youtubeRegex().test(text))
        return;

      let videoId = youtubeRegex().exec(text)[1];
      YoutubeApi.getVideo(videoId, function (err, theVideo) {
        if (err) return;
        playVideo(theVideo);
      });
    });

    socket.on('video/end', (id) => {
      console.log('Video ended: ', id);
      isVideoPlaying = false;
      playlist.remove(id);
      socket.emit('playlist/update', playlist);

      if (!playlist.length) return;

      socket.emit('video/play', playlist[0].id);
    });

    function isVideoInPlaylist(videoId) {
      return ~concoctPlaylistVideoIds().indexOf(videoId);
    }

    // === _.pluck(playlist, 'id')
    function concoctPlaylistVideoIds() {
      let playlistVideosIds = [];

      for (let video of playlist)
        playlistVideosIds.push(video.id);

      return playlistVideosIds;
    }

    function removeVideoFromPlaylist(videoId) {
      if (!playlist.contains(videoId)) return;

      playlist.splice(playlist.concoctVideoIds().indexOf(videoId), 1);
    }

    function playVideoNow(video) {
      if (playlist.contains(video.id))
        playlist.remove(video.id);
      playlist.splice(0, 0, video);
    }

    function setNextVideoInPlaylist(video) {
      if (playlist.length)
        playlist.splice(1, 0, video);
      else
        playlist.push(video);
    }

    function launchApp() {
      if (isOnline((err, online) => {
        if (err || !online) {
          return socket.emit('internet/notconnected');
        }

        YoutubeApi.tryStoredAccessToken((noValidAccessToken, token) => {
          if (noValidAccessToken) {
            socket.emit('youtube/notauthenticated');
          } else {
            socket.emit('youtube/callback', token);
          }
        });
      }));
    }

    socket.on('internet/reconnect', launchApp);

    let playlist = [];
    playlist.concoctVideoIds = concoctPlaylistVideoIds;
    playlist.remove = removeVideoFromPlaylist;
    playlist.contains = isVideoInPlaylist;
    playlist.playNow = playVideoNow;
    playlist.setNext = setNextVideoInPlaylist;

    let isVideoPlaying = false;
    launchApp();
  });

  server.route({
    method: 'GET',
    path:'/youtube/callback',
    handler: function (request, reply) {
      io.emit('youtube/waiting');
      YoutubeApi.getToken(request.query.code, function (token) {
        io.emit('youtube/callback', token);

        windows[AUTH_WINDOW].close();
      });
    }
  });
});
<commit_msg>Close fullscreen when player goes fullscreen<commit_after>'use strict';

const CONFIG = require('./config');

const app = require('app');
const BrowserWindow = require('browser-window');

const YoutubeApi = require('./youtubeapi');

const Hapi = require('hapi');
const isOnline = require('is-online');
const youtubeRegex = require('youtube-regex');

// Create the Hapi Web Server
const server = new Hapi.Server();
server.connection({
  host: 'localhost',
  port: '@@PORT'
});

// Start the server
server.start((err) => {
  if (err) {
    throw err;
  }

  console.log('Server running at:', server.info.uri);
});

const AUTH_WINDOW = 'auth';
const MAIN_WINDOW = 'main';

const io = require('socket.io')(server.listener);

// report crashes to the Electron project
require('crash-reporter').start();

// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();

// prevent window being garbage collected
let windows = {};

function onClosed(windowName) {
  // dereference the window
  windows[windowName] = null;
}

function createWindow(windowName, url, width, height, isDevToolsOpen) {
  const win = new BrowserWindow({ width, height });

  win.loadUrl(url);
  win.on('closed', onClosed.bind(windowName));
  win.on('enter-html-full-screen', (event) => {
    // tmp
    // This event is called when the YouTube player goes fullscreen
    // It should only fullscreen the webview, but it does fullscreen the app
    setTimeout(function () {
      win.setFullScreen(false);
    }, 1000);
  });

  win.setMinimumSize(780, 270);

  if (isDevToolsOpen) win.openDevTools();

  return win;
}

function createMainWindow() {
  const url = 'file://' + __dirname + '/client/index.html';

  return createWindow(
    MAIN_WINDOW,
    url,
    CONFIG.MAIN_WINDOW.WIDTH,
    CONFIG.MAIN_WINDOW.HEIGHT,
    CONFIG.MAIN_WINDOW.IS_DEV_TOOLS_OPEN
  );
}

function createLogInWindow(url) {
  return createWindow(
    AUTH_WINDOW,
    url,
    CONFIG.AUTH_WINDOW.WIDTH,
    CONFIG.AUTH_WINDOW.HEIGHT,
    CONFIG.AUTH_WINDOW.IS_DEV_TOOLS_OPEN
  );
}

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate-with-no-open-windows', () => {
  if (!windows[MAIN_WINDOW]) {
    windows[MAIN_WINDOW] = createMainWindow();
  }
});

app.on('ready', () => {
  windows[MAIN_WINDOW] = createMainWindow();

  io.on('connection', (socket) => {
    socket.on('youtube/auth', () => {
      socket.emit('youtube/waiting');

      YoutubeApi.getAuthUrl((url) => {
        socket.emit('youtube/waitingforuser')
        windows[AUTH_WINDOW] = createLogInWindow(url);
      });
    });

    socket.on('subscriptions/list', () => {
      YoutubeApi.getSubscriptions((err, subscriptions) => {
        if (err) {
          console.error(err);
        } else {
          socket.emit('subscriptions/list', subscriptions);
        }
      });
    });

    // Video
    socket.on('video/start', (id) => {
      console.log('Video started: ', id);
      isVideoPlaying = true;
    });

    socket.on('video/pause', (id) => {
      console.log('Video paused: ', id);
    });

    socket.on('video/buffer', (id) => {
      console.log('Video buffering: ', id);
    });

    function playVideo(video) {
      console.log('Play video: ', video.id);

      playlist.playNow(video);
      socket.emit('playlist/update', playlist);

      if (isVideoPlaying)
        socket.emit('video/play', video.id);
      else
        socket.emit('video/cue', video.id);
    }

    /* This method is called to play a video immediately */
    socket.on('video/play', playVideo);

    /* Put a video on the end of the playlist */
    socket.on('video/cue', (video) => {
      console.log('Cueing video: ', video.id);
      if (!playlist.length)
        socket.emit('video/cue', video.id);

      if (!playlist.contains(video.id)) {
        console.log('Pushing a video into the playlist (' + video.id + ')');
        playlist.push(video);
        socket.emit('playlist/update', playlist);
      }
    });

    /* Set a video as next to play */
    socket.on('video/next', (video) => {
      console.log('Set next video: ', video.id);
      if (!playlist.length)
        socket.emit('video/cue', video.id);

      if (!playlist.contains(video.id)) {
        playlist.setNext(video);
      }

      socket.emit('playlist/update', playlist);
    });

    /* When something is pasted, if it is a YouTube video, play it */
    socket.on('video/paste', (text) => {
      if (!youtubeRegex().test(text))
        return;

      let videoId = youtubeRegex().exec(text)[1];
      YoutubeApi.getVideo(videoId, function (err, theVideo) {
        if (err) return;
        playVideo(theVideo);
      });
    });

    socket.on('video/end', (id) => {
      console.log('Video ended: ', id);
      isVideoPlaying = false;
      playlist.remove(id);
      socket.emit('playlist/update', playlist);

      if (!playlist.length) return;

      socket.emit('video/play', playlist[0].id);
    });

    function isVideoInPlaylist(videoId) {
      return ~concoctPlaylistVideoIds().indexOf(videoId);
    }

    // === _.pluck(playlist, 'id')
    function concoctPlaylistVideoIds() {
      let playlistVideosIds = [];

      for (let video of playlist)
        playlistVideosIds.push(video.id);

      return playlistVideosIds;
    }

    function removeVideoFromPlaylist(videoId) {
      if (!playlist.contains(videoId)) return;

      playlist.splice(playlist.concoctVideoIds().indexOf(videoId), 1);
    }

    function playVideoNow(video) {
      if (playlist.contains(video.id))
        playlist.remove(video.id);
      playlist.splice(0, 0, video);
    }

    function setNextVideoInPlaylist(video) {
      if (playlist.length)
        playlist.splice(1, 0, video);
      else
        playlist.push(video);
    }

    function launchApp() {
      if (isOnline((err, online) => {
        if (err || !online) {
          return socket.emit('internet/notconnected');
        }

        YoutubeApi.tryStoredAccessToken((noValidAccessToken, token) => {
          if (noValidAccessToken) {
            socket.emit('youtube/notauthenticated');
          } else {
            socket.emit('youtube/callback', token);
          }
        });
      }));
    }

    socket.on('internet/reconnect', launchApp);

    let playlist = [];
    playlist.concoctVideoIds = concoctPlaylistVideoIds;
    playlist.remove = removeVideoFromPlaylist;
    playlist.contains = isVideoInPlaylist;
    playlist.playNow = playVideoNow;
    playlist.setNext = setNextVideoInPlaylist;

    let isVideoPlaying = false;
    launchApp();
  });

  server.route({
    method: 'GET',
    path:'/youtube/callback',
    handler: function (request, reply) {
      io.emit('youtube/waiting');
      YoutubeApi.getToken(request.query.code, function (token) {
        io.emit('youtube/callback', token);

        windows[AUTH_WINDOW].close();
      });
    }
  });
});
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {

  grunt.initConfig({

    jshint: {

      files: [

        'Gruntfile.js',
        'src/**/*.js',
        'package.json',
        '.jshintrc',
        'test/**/*.js'

      ],

      options: {
        jshintrc: '.jshintrc'
      }

    },

    jsdoc: {

        dist : {
            src: ['src/**/*.js'],

            options: {
                destination: 'docs'
            }
        }

    },

    karma: {
        unit: {
            configFile: 'karma.unit.conf.js'
        },

        continuous: {
            configFile: 'karma.continuous.conf.js'

        }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-jsdoc');

  grunt.registerTask('travis', ['jshint', 'karma:continuous']);

};<commit_msg>Split continuous<commit_after>'use strict';

module.exports = function (grunt) {

  grunt.initConfig({

    jshint: {

      files: [

        'Gruntfile.js',
        'src/**/*.js',
        'package.json',
        '.jshintrc',
        'test/**/*.js'

      ],

      options: {
        jshintrc: '.jshintrc'
      }

    },

    jsdoc: {

        dist : {
            src: ['src/**/*.js'],

            options: {
                destination: 'docs'
            }
        }

    },

    karma: {
        unit: {
            configFile: 'karma.unit.conf.js'
        },

        continuous1: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_Chrome', 'SL_Firefox', 'PhantomJS']
        },

        continuous2: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_Safari', 'SL_IE_8']
        },

        continuous3: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_IE_9', 'SL_IE_10']
        }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-jsdoc');

  grunt.registerTask('travis', ['jshint', 'karma:continuous1', 'karma:continuous2', 'karma:continuous3']);

};<|endoftext|>


<commit_before>var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var bodyParser = require('body-parser');
var lessMiddleware = require('less-middleware');
var MongoClient = require('mongodb').MongoClient
  , assert = require('assert');

var url = 'mongodb://localhost:27017/accumulator';
var db;
var index = require('./routes/index');

process.title = "ca-app";

var environment = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
environment = environment.trim();
console.log('NODE_ENV: ' + environment);

var app = express();

MongoClient.connect(url, function (err, mongoDb) {
  assert.equal(null, err);
  console.log("Connected to database");

  db = mongoDb;
});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
if (environment === 'development') {
  app.locals.pretty = true;
}

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(lessMiddleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);

var insertDocument = function (db, document, callback) {
  var collection = db.collection('documents');
  collection.insertOne(document, function (err, result) {
    callback(err, JSON.stringify(result.ops[0]));
  });
};

var findAllDocuments = function (db, callback) {
  var collection = db.collection('documents');
  collection.find({}).toArray(function (err, result) {
    callback(err, result);
  });
}

// Insert message
app.post('/api', function (req, res) {
  var data = req.body;
  insertDocument(db, data, function (err, result) {
    res.status(201).send(result)
  })
});

// Get messages
app.get('/api', function (req, res) {
  findAllDocuments(db, function (err, result) {
    res.send(result);
  });
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = environment === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
<commit_msg>list most recent message at the top<commit_after>var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var bodyParser = require('body-parser');
var lessMiddleware = require('less-middleware');
var MongoClient = require('mongodb').MongoClient
  , assert = require('assert');

var url = 'mongodb://localhost:27017/accumulator';
var db;
var index = require('./routes/index');

process.title = "ca-app";

var environment = process.env.NODE_ENV ? process.env.NODE_ENV : 'development';
environment = environment.trim();
console.log('NODE_ENV: ' + environment);

var app = express();

MongoClient.connect(url, function (err, mongoDb) {
  assert.equal(null, err);
  console.log("Connected to database");

  db = mongoDb;
});

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
if (environment === 'development') {
  app.locals.pretty = true;
}

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(lessMiddleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', index);

var insertDocument = function (db, document, callback) {
  var collection = db.collection('documents');
  collection.insertOne(document, function (err, result) {
    callback(err, JSON.stringify(result.ops[0]));
  });
};

var findAllDocuments = function (db, callback) {
  var collection = db.collection('documents');
  collection.find({}).toArray(function (err, result) {
    callback(err, result.reverse());
  });
}

// Insert message
app.post('/api', function (req, res) {
  var data = req.body;
  insertDocument(db, data, function (err, result) {
    res.status(201).send(result)
  })
});

// Get messages
app.get('/api', function (req, res) {
  findAllDocuments(db, function (err, result) {
    res.send(result);
  });
});

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = environment === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
<|endoftext|>


<commit_before>module.exports = function (grunt) {
  'use strict';

  // check task runtime
  require('time-grunt')(grunt);

  // load generic configs
  var configs = require('dalek-build-tools');

  grunt.initConfig({

    // load module meta data
    pkg: grunt.file.readJSON('package.json'),

    // define a src set of files for other tasks
    src: {
      lint: ['Gruntfile.js', 'index.js', 'lib/**/*.js', 'test/*.js'],
      complexity: ['index.js', 'lib/**/*.js'],
      test: ['test/*.js'],
      src: ['index.js', 'lib/**/*.js']
    },

    // clean automatically generated helper files & docs
    clean: configs.clean,

    // speed up build by defining concurrent tasks
    concurrent: configs.concurrent,

    // linting
    jshint: configs.jshint,

    // testing
    mochaTest: configs.mocha,

    // code metrics
    complexity: configs.complexity,
    plato: configs.plato(grunt.file.readJSON('.jshintrc')),

    // api docs
    yuidoc: configs.yuidocs(),

    // up version, tag & commit
    bump: configs.bump({
      pushTo: 'git@github.com:dalekjs/dalek-driver-sauce.git',
      files: ['package.json', 'CONTRIBUTORS.md', 'CHANGELOG.md']
    }),

    // generate contributors file
    contributors: configs.contributors,

    // compress artifacts
    compress: configs.compress,

    // user docs
    documantix: {
      options: {
        header: 'dalekjs/dalekjs.com/master/assets/header.html',
        footer: 'dalekjs/dalekjs.com/master/assets/footer.html',
        target: 'report/docs',
        vars: {
          title: 'DalekJS - Documentation - Driver Sauce Labs',
          desc: 'DalekJS - Documentation - Driver Sauce Labs',
          docs: true
        }
      },
      src: ['index.js']
    },

    // prepare files for grunt-plato to
    // avoid error messages (weird issue...)
    preparePlato: {
      options: {
        folders: [
          'coverage',
          'report',
          'report/complexity',
          'report/complexity/files',
          'report/complexity/files/test',
          'report/complexity/files/index_js',
          'report/complexity/files/lib_browser_js',
          'report/complexity/files/lib_commands_cookie_js',
          'report/complexity/files/lib_commands_element_js',
          'report/complexity/files/lib_commands_frame_js',
          'report/complexity/files/lib_commands_page_js',
          'report/complexity/files/lib_commands_screenshot_js',
          'report/complexity/files/lib_commands_window_js',
          'report/complexity/files/lib_commands_url_js'
        ],
        files: [
          'report.history.json',
          'files/report.history.json',
          'files/index_js/report.history.json',
          'files/lib_commands_cookie_js/report.history.json',
          'files/lib_browser_js/report.history.json',
          'files/lib_commands_element_js/report.history.json',
          'files/lib_commands_frame_js/report.history.json',
          'files/lib_commands_page_js/report.history.json',
          'files/lib_commands_screenshot_js/report.history.json',
          'files/lib_commands_url_js/report.history.json',
          'files/lib_commands_window_js/report.history.json'
        ]
      }
    },

    // prepare files & folders for coverage
    prepareCoverage: {
      options: {
        folders: ['coverage', 'report', 'report/coverage'],
        pattern: '[require("fs").realpathSync(__dirname + "/../index.js"), require("fs").realpathSync(__dirname + "/../lib/")]'
      }
    },

    // add current timestamp to the html document
    includereplace: {
      dist: {
        options: {
          globals: {
            timestamp: '<%= grunt.template.today("dddd, mmmm dS, yyyy, h:MM:ss TT") %>'
          },
        },
        src: 'report/docs/*.html',
        dest: '.'
      }
    },

    // archive docs
    archive: {
      options: {
        files: ['sauce.html']
      }
    },

    // release canary version
    'release-canary': {
      options: {
        files: []
      }
    }

  });

  // load 3rd party tasks
  require('load-grunt-tasks')(grunt);
  grunt.loadTasks('./node_modules/dalek-build-tools/tasks');
  grunt.loadNpmTasks('grunt-documantix');

  // define runner tasks
  grunt.registerTask('lint', 'jshint');

  // split test & docs for speed
  grunt.registerTask('test', ['clean:coverage', 'prepareCoverage', 'concurrent:test', 'generateCoverageBadge']);
  grunt.registerTask('docs', ['clean:reportZip', 'clean:report', 'preparePlato', 'concurrent:docs', 'documantix', 'includereplace', 'compress']);

  // release tasks
  grunt.registerTask('releasePatch', ['test', 'bump-only:patch', 'contributors', 'changelog', 'bump-commit']);
  grunt.registerTask('releaseMinor', ['test', 'bump-only:minor', 'contributors', 'changelog', 'bump-commit']);
  grunt.registerTask('releaseMajor', ['test', 'bump-only:major', 'contributors', 'changelog', 'bump-commit']);

  // clean, test, generate docs (the CI task)
  grunt.registerTask('all', ['clean', 'test', 'docs']);
};
<commit_msg>chore(gruntfile): updated Gruntfile<commit_after>module.exports = function (grunt) {
  'use strict';

  // check task runtime
  require('time-grunt')(grunt);

  // load generic configs
  var configs = require('dalek-build-tools');

  grunt.initConfig({

    // load module meta data
    pkg: grunt.file.readJSON('package.json'),

    // define a src set of files for other tasks
    src: {
      lint: ['Gruntfile.js', 'index.js', 'lib/**/*.js', 'test/*.js'],
      complexity: ['index.js', 'lib/**/*.js'],
      test: ['test/*.js'],
      src: ['index.js', 'lib/**/*.js']
    },

    // clean automatically generated helper files & docs
    clean: configs.clean,

    // speed up build by defining concurrent tasks
    concurrent: configs.concurrent,

    // linting
    jshint: configs.jshint,

    // testing
    mochaTest: configs.mocha,

    // code metrics
    complexity: configs.complexity,
    plato: configs.plato(grunt.file.readJSON('.jshintrc')),

    // api docs
    yuidoc: configs.yuidocs(),

    // up version, tag & commit
    bump: configs.bump({
      pushTo: 'git@github.com:dalekjs/dalek-driver-sauce.git',
      files: ['package.json', 'CONTRIBUTORS.md', 'CHANGELOG.md']
    }),

    // generate contributors file
    contributors: configs.contributors,

    // compress artifacts
    compress: configs.compress,

    // user docs
    documantix: {
      options: {
        header: 'dalekjs/dalekjs.com/master/assets/header.html',
        footer: 'dalekjs/dalekjs.com/master/assets/footer.html',
        target: 'report/docs',
        vars: {
          title: 'DalekJS - Documentation - Driver Sauce Labs',
          desc: 'DalekJS - Documentation - Driver Sauce Labs',
          docs: true
        }
      },
      src: ['index.js']
    },

    // prepare files for grunt-plato to
    // avoid error messages (weird issue...)
    preparePlato: {
      options: {
        folders: [
          'coverage',
          'report',
          'report/complexity',
          'report/complexity/files',
          'report/complexity/files/test',
          'report/complexity/files/index_js',
          'report/complexity/files/lib_browser_js',
          'report/complexity/files/lib_commands_cookie_js',
          'report/complexity/files/lib_commands_element_js',
          'report/complexity/files/lib_commands_frame_js',
          'report/complexity/files/lib_commands_page_js',
          'report/complexity/files/lib_commands_screenshot_js',
          'report/complexity/files/lib_commands_window_js',
          'report/complexity/files/lib_commands_url_js'
        ],
        files: [
          'report.history.json',
          'files/report.history.json',
          'files/index_js/report.history.json',
          'files/lib_commands_cookie_js/report.history.json',
          'files/lib_browser_js/report.history.json',
          'files/lib_commands_element_js/report.history.json',
          'files/lib_commands_frame_js/report.history.json',
          'files/lib_commands_page_js/report.history.json',
          'files/lib_commands_screenshot_js/report.history.json',
          'files/lib_commands_url_js/report.history.json',
          'files/lib_commands_window_js/report.history.json'
        ]
      }
    },

    // prepare files & folders for coverage
    prepareCoverage: {
      options: {
        folders: ['coverage', 'report', 'report/coverage'],
        pattern: '[require("fs").realpathSync(__dirname + "/../index.js"), require("fs").realpathSync(__dirname + "/../lib/")]'
      }
    },

    // add current timestamp to the html document
    includereplace: {
      dist: {
        options: {
          globals: {
            timestamp: '<%= grunt.template.today("dddd, mmmm dS, yyyy, h:MM:ss TT") %>'
          },
        },
        src: 'report/docs/*.html',
        dest: './'
      }
    },

    // archive docs
    archive: {
      options: {
        files: ['sauce.html']
      }
    },

    // release canary version
    'release-canary': {
      options: {
        files: []
      }
    }

  });

  // load 3rd party tasks
  require('load-grunt-tasks')(grunt);
  grunt.loadTasks('./node_modules/dalek-build-tools/tasks');
  grunt.loadNpmTasks('grunt-documantix');

  // define runner tasks
  grunt.registerTask('lint', 'jshint');

  // split test & docs for speed
  grunt.registerTask('test', ['clean:coverage', 'prepareCoverage', 'concurrent:test', 'generateCoverageBadge']);
  grunt.registerTask('docs', ['clean:reportZip', 'clean:report', 'preparePlato', 'concurrent:docs', 'documantix', 'includereplace', 'compress']);

  // release tasks
  grunt.registerTask('releasePatch', ['test', 'bump-only:patch', 'contributors', 'changelog', 'bump-commit']);
  grunt.registerTask('releaseMinor', ['test', 'bump-only:minor', 'contributors', 'changelog', 'bump-commit']);
  grunt.registerTask('releaseMajor', ['test', 'bump-only:major', 'contributors', 'changelog', 'bump-commit']);

  // clean, test, generate docs (the CI task)
  grunt.registerTask('all', ['clean', 'test', 'docs']);
};
<|endoftext|>