<commit_before>// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>. Yo Jekyll!
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
  return connect.static(require('path').resolve(dir));
};
var yeomanConfig = {
  app: 'app',
  dist: 'dist',
  css: '<%= cssDir %>',<% if (cssPre) { %>
  cssPre: '<%= cssPreDir %>',<% } %>
  js: '<%= jsDir %>',<% if (jsPre) { %>
  jsPre: '<%= jsPreDir %>',<% } %>
  img: '<%= imgDir %>',
  fonts: '<%= fontsDir %>'
};

module.exports = function (grunt) {

  // Configuration
  grunt.initConfig({
    yeoman: yeomanConfig,

    watch: {<% if (cssPre === 'sass') { %>
      sass: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/**/*.{scss,sass}'],
        tasks: ['sass:server']
      },<% } %><% if (cssPre === 'compass') { %>
      compass: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/**/*.{scss,sass}'],
        tasks: ['compass:server']
      },<% } %><% if (jsPre === 'coffeescript') { %>
      coffee: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.jsPre %>/**/*.coffee'],
        tasks: ['coffee:server']
      },
      coffeeTest: {
        files: ['test/spec/**/*.coffee'],
        tasks: ['coffee:test']
      },<% } %>
      jekyll: {
        files: ['<%%= yeoman.app %>/**/*.{html,yml,md,mkd,markdown}',
                '!<%%= yeoman.app %>/{<% if (cssPre) { %><%%= yeoman.cssPre %>,<% } %><% if (jsPre) { %><%%= yeoman.jsPre %>,<% } %>_plugins}'],
        tasks: ['jekyll:server']
      },
      livereload: {
        options: {
          livereload: LIVERELOAD_PORT
        },
        files: [
          '.jekyll/**/*.html',
          '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.css %>/**/*.css',
          '{.tmp,<%%= yeoman.app %>}/<%%= js %>/**/*.js',
          '<%%= yeoman.app %>/<%%= yeoman.img %>/**/*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },
    connect: {
      options: {
        port: 9000,
        // Change hostname to null to access the server from outside.
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function (connect) {
            return [
            lrSnippet,
            mountFolder(connect, '.tmp'),
            mountFolder(connect, '.jekyll'),
            mountFolder(connect, yeomanConfig.app)];
          }
        }
      },
      test: {
        options: {
          middleware: function (connect) {
            return [
            mountFolder(connect, '.tmp'),
            mountFolder(connect, 'test')];
          }
        }
      },
      dist: {
        options: {
          middleware: function (connect) {
            return [
            mountFolder(connect, yeomanConfig.dist)];
          }
        }
      }
    },
    open: {
      server: {
        path: 'http://localhost:<%%= connect.options.port %>'
      }
    },
    // Running Jekyll also cleans all non-git files from its destination,
    // unless specifically declared in _config.yml's `keep_files`
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '<%%= yeoman.dist %>/*',
            '!<%%= yeoman.dist %>/.git*']
        }]
      },
      server: ['.tmp', '.jekyll']
    },<% if (cssPre === 'sass') { %>
    // Add the sass files you want to compile in this task.
    // TODO: Revise to watch default directories when
    // https://github.com/gruntjs/grunt-contrib-sass/issues/40 is resolved.
    sass: {
      options: {
        bundleExec: true,
        style: 'expanded',
        debugInfo: true,
        lineNumbers: true,
        loadPath: 'app/bower_components'
      },
      dist: {
        options: {
          debugInfo: false,
          lineNumbers: false
        },
        // RWRW tailor for templates
        files: {
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/main.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss',
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/syntax.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/syntax.scss'
        }
      },
      server: {
        files: {
          '.tmp/<%%= yeoman.css %>/main.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss',
          '.tmp/<%%= yeoman.css %>/syntax.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/syntax.scss'
        }
      }
    },<% } %><% if (cssPre === 'compass') { %>
    compass: {
      options: {
        // require: ['singularity', 'jacket'],
        bundleExec: true,
        sassDir: '<%%= yeoman.app %>/<%%= yeoman.cssPre %>',
        imagesDir: '<%%= yeoman.app %>/<%%= yeoman.img %>',
        fontsDir: '<%%= yeoman.app %>/<%%= yeoman.fonts %>',
        javascriptsDir: '<%%= yeoman.app %>/<%%= yeoman.js %>',
        relativeAssets: false,
        httpImagesPath: '/<%%= yeoman.img %>',
        httpGeneratedImagesPath: '/<%%= yeoman.img %>/generated',
        outputStyle: 'expanded',
        raw: 'asset_cache_buster :none \nextensions_dir = "<%%= yeoman.app %>/bower_components"\n'
      },
      dist: {
        options: {
          cssDir: '<%%= yeoman.dist %>/<%%= yeoman.css %>',
          generatedImagesDir: '<%%= yeoman.dist %>/<%%= yeoman.img %>/generated'
        }
      },
      server: {
        options: {
          debugInfo: true,
          cssDir: '.tmp/<%%= yeoman.css %>',
          generatedImagesDir: '.tmp/<%%= yeoman.img %>/generated'
        }
      }
    },<% } %><% if (jsPre === 'coffeescript') { %>
    coffee: {
      test: {
        files: [{
          expand: true,
          cwd: 'test/spec',
          src: '**/*.coffee',
          dest: '.tmp/spec',
          ext: '.js'
        }]
      },
      dist: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.jsPre %>',
          src: '**/*.coffee',
          dest: '<%%= yeoman.dist %>/<%%= yeoman.js %>',
          ext: '.js'
        }]
      },
      server: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.jsPre %>',
          src: '**/*.coffee',
          dest: '.tmp/<%%= yeoman.js %>',
          ext: '.js'
        }]
      },
    },<% } %>
    jekyll: {
      // TODO: switch config to options style after
      // https://github.com/dannygarcia/grunt-jekyll/pull/14
      dist: {
        bundleExec: true,
        src : '<%%= yeoman.app %>',
        dest: '<%%= yeoman.dist %>',
        server : false,
        auto : false,
        config: '_config.yml,_config.build.yml'
      },
      server: {
        bundleExec: true,
        src : '<%%= yeoman.app %>',
        dest: '.jekyll',
        server : false,
        auto : false,
        config: '_config.yml'
      }
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.js %>/**/*.js',
        '!<%%= yeoman.app %>/<%%= yeoman.js %>/vendor/**/*',
        'test/spec/**/*.js'],
      report: [
        '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.js %>/**/*.js',
        '!<%%= yeoman.app %>/<%%= yeoman.js %>/vendor/**/*']
    },
    // TODO: rewrite for globbing and bundleExec when 5.0 is released
    csscss: {
      options: {
        minMatch: 2,<% if (cssPre === 'compass' || cssPre === 'sass') { %>
        ignoreSassMixins: false,<% } %><% if (cssPre === 'compass') { %>
        compass: true,<% } %><% if (!cssPre) { %>
        showParserErrors: true,<% } %>
        colorize: true,
        shorthand: false,
        verbose: true
      },
      // Check files manually here until we can glob with 5.0
      report: {<% if (!cssPre) { %>
       src: ['<%%= yeoman.app %>/<%%= yeoman.css %>/main.css']<% } %><% if (cssPre === 'compass' || cssPre === 'sass') { %>
       src: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss']<% } %>
      }
    },
    csslint: {
      options: {
        csslintrc: '.csslintrc'
      },
      report: {
        src: ['{.tmp,<%%= yeoman.app %>}/<%%= yeoman.css %>/**/*.css']
      }
    },
    // Note that useminPrepare will only scan one page for usemin blocks. If
    // you have usemin blocks that aren't used in index.html, create a usemin
    // manifest page (hackery!) and point the task there.
    useminPrepare: {
      options: {
        dest: '<%%= yeoman.dist %>'
      },
      html: '<%%= yeoman.dist %>/index.html'
    },
    usemin: {
      options: {
          basedir: '<%%= yeoman.dist %>',
          dirs: ['<%%= yeoman.dist %>/**/*']
      },
      html: ['<%%= yeoman.dist %>/**/*.html'],
      css: ['<%%= yeoman.dist %>/<%%= yeoman.css %>/**/*.css']
    },
    // usemin runs concat, but this is left here if you need it
    /* concat: {
      dist: {}
    },*/
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeAttributeQuotes: true,
          removeRedundantAttributes: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%%= yeoman.dist %>',
          src: '**/*.html',
          dest: '<%%= yeoman.dist %>'
        }]
      }
    },
    cssmin: {
      dist: {
        options: {
          // banner: '/* See more of my projects at github.com/<%= github %> */',
          report: 'gzip'
        },
        files: {
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/main.css': [
            '.tmp/<%%= yeoman.css %>/{,*/}*.css',
            '<%%= yeoman.app %>/<%%= yeoman.css %>/{,*/}*.css']
        }
      }
    },
    uglify: {},
    imagemin: {
      dist: {
        options: {
          progressive: true
        },
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.img %>',
          src: '{,*/}*.{png,jpg,jpeg}',
          dest: '<%%= yeoman.dist %>/<%%= yeoman.img %>'
        }]
      }
    },
    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.img %>',
          src: '{,*/}*.svg',
          dest: '<%%= yeoman.dist %>/<%%= yeoman.img %>'
        }]
      }
    },
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%%= yeoman.app %>',
          dest: '<%%= yeoman.dist %>',
          src: [
            // Jekyll moves all html and text files
            // Copy transports image files and asset drectories
            // If your site requires it, add other file type patterns here
            '**/*.{png,jpg,jpeg,gif,webp,svg}',
            '<%%= yeoman.css %>',
            '<%%= yeoman.js %>',
            '<%%= yeoman.img %>',
            '<%%= yeoman.fonts %>']
        }]
      }
    },
    rev: {
      options: {
        length: 4
      },
      dist: {
        files: {
          src: [
            '<%%= yeoman.dist %>/<%%= yeoman.js %>/**/*.js',
            '<%%= yeoman.dist %>/<%%= yeoman.css %>/**/*.css',
            '<%%= yeoman.dist %>/<%%= yeoman.img %>/**/*.{png,jpg,jpeg,gif,webp,svg}',
            '<%%= yeoman.dist %>/<%%= yeoman.fonts %>/**/*.{eot*,woff,ttf,svg}']
        }
      }
    },
    concurrent: {
      server: [<% if (cssPre === 'sass') { %>
        'sass:server'<% } %><% if (cssPre === 'compass') { %>
        'compass:server'<% } %><% if (cssPre && jsPre) { %>,<% } %><% if (jsPre === 'coffee') { %>
        'coffee:server'<% } %>
      ],
      dist: [<% if (cssPre === 'sass') { %>
        'sass:dist',<% } %><% if (cssPre === 'compass') { %>
        'compass:dist',<% } %><% if (jsPre === 'coffee') { %>
        'coffee:dist',<% } %>
        'imagemin',
        'svgmin'
      ]
    }
  });

  // Load plugins
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  // Define Tasks
  grunt.registerTask('server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      // Jekyll cleans its destination before it compiles, so must run first
      'jekyll:server',
      'concurrent:server',
      'connect:livereload',
      'open',
      'watch']);
  });

  // No real tests yet. Add your own.
  // grunt.registerTask('test', [
  //   'clean:server',
  //   'concurrent:test',
  //   'connect:test']);

  grunt.registerTask('report', [
    'concurrent:server',
    'jshint:report',
    'csscss:report',
    'csslint:report'
    ]);

  grunt.registerTask('build', [
    'clean:dist',
    // Jekyll cleans its destination before it compiles, so must run first
    'jekyll:dist',
    'copy:dist',
    'concurrent:dist',
    'useminPrepare',
    'concat',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin'
    ]);

  grunt.registerTask('default', [
    'report',
    'build']);
};
<commit_msg>Fix imagemin and image copy :dist tasks<commit_after>// Generated on <%= (new Date).toISOString().split('T')[0] %> using <%= pkg.name %> <%= pkg.version %>. Yo Jekyll!
'use strict';
var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT});
var mountFolder = function (connect, dir) {
  return connect.static(require('path').resolve(dir));
};
var yeomanConfig = {
  app: 'app',
  dist: 'dist',
  css: '<%= cssDir %>',<% if (cssPre) { %>
  cssPre: '<%= cssPreDir %>',<% } %>
  js: '<%= jsDir %>',<% if (jsPre) { %>
  jsPre: '<%= jsPreDir %>',<% } %>
  img: '<%= imgDir %>',
  fonts: '<%= fontsDir %>'
};

module.exports = function (grunt) {

  // Configuration
  grunt.initConfig({
    yeoman: yeomanConfig,

    watch: {<% if (cssPre === 'sass') { %>
      sass: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/**/*.{scss,sass}'],
        tasks: ['sass:server']
      },<% } %><% if (cssPre === 'compass') { %>
      compass: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/**/*.{scss,sass}'],
        tasks: ['compass:server']
      },<% } %><% if (jsPre === 'coffeescript') { %>
      coffee: {
        files: ['<%%= yeoman.app %>/<%%= yeoman.jsPre %>/**/*.coffee'],
        tasks: ['coffee:server']
      },
      coffeeTest: {
        files: ['test/spec/**/*.coffee'],
        tasks: ['coffee:test']
      },<% } %>
      jekyll: {
        files: ['<%%= yeoman.app %>/**/*.{html,yml,md,mkd,markdown}',
                '!<%%= yeoman.app %>/{<% if (cssPre) { %><%%= yeoman.cssPre %>,<% } %><% if (jsPre) { %><%%= yeoman.jsPre %>,<% } %>_plugins}'],
        tasks: ['jekyll:server']
      },
      livereload: {
        options: {
          livereload: LIVERELOAD_PORT
        },
        files: [
          '.jekyll/**/*.html',
          '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.css %>/**/*.css',
          '{.tmp,<%%= yeoman.app %>}/<%%= js %>/**/*.js',
          '<%%= yeoman.app %>/<%%= yeoman.img %>/**/*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },
    connect: {
      options: {
        port: 9000,
        // Change hostname to null to access the server from outside.
        hostname: 'localhost'
      },
      livereload: {
        options: {
          middleware: function (connect) {
            return [
            lrSnippet,
            mountFolder(connect, '.tmp'),
            mountFolder(connect, '.jekyll'),
            mountFolder(connect, yeomanConfig.app)];
          }
        }
      },
      test: {
        options: {
          middleware: function (connect) {
            return [
            mountFolder(connect, '.tmp'),
            mountFolder(connect, 'test')];
          }
        }
      },
      dist: {
        options: {
          middleware: function (connect) {
            return [
            mountFolder(connect, yeomanConfig.dist)];
          }
        }
      }
    },
    open: {
      server: {
        path: 'http://localhost:<%%= connect.options.port %>'
      }
    },
    // Running Jekyll also cleans all non-git files from its destination,
    // unless specifically declared in _config.yml's `keep_files`
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '<%%= yeoman.dist %>/*',
            '!<%%= yeoman.dist %>/.git*']
        }]
      },
      server: ['.tmp', '.jekyll']
    },<% if (cssPre === 'sass') { %>
    // Add the sass files you want to compile in this task.
    // TODO: Revise to watch default directories when
    // https://github.com/gruntjs/grunt-contrib-sass/issues/40 is resolved.
    sass: {
      options: {
        bundleExec: true,
        style: 'expanded',
        debugInfo: true,
        lineNumbers: true,
        loadPath: 'app/bower_components'
      },
      dist: {
        options: {
          debugInfo: false,
          lineNumbers: false
        },
        // RWRW tailor for templates
        files: {
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/main.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss',
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/syntax.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/syntax.scss'
        }
      },
      server: {
        files: {
          '.tmp/<%%= yeoman.css %>/main.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss',
          '.tmp/<%%= yeoman.css %>/syntax.css': '<%%= yeoman.app %>/<%%= yeoman.cssPre %>/syntax.scss'
        }
      }
    },<% } %><% if (cssPre === 'compass') { %>
    compass: {
      options: {
        // require: ['singularity', 'jacket'],
        bundleExec: true,
        sassDir: '<%%= yeoman.app %>/<%%= yeoman.cssPre %>',
        imagesDir: '<%%= yeoman.app %>/<%%= yeoman.img %>',
        fontsDir: '<%%= yeoman.app %>/<%%= yeoman.fonts %>',
        javascriptsDir: '<%%= yeoman.app %>/<%%= yeoman.js %>',
        relativeAssets: false,
        httpImagesPath: '/<%%= yeoman.img %>',
        httpGeneratedImagesPath: '/<%%= yeoman.img %>/generated',
        outputStyle: 'expanded',
        raw: 'asset_cache_buster :none \nextensions_dir = "<%%= yeoman.app %>/bower_components"\n'
      },
      dist: {
        options: {
          cssDir: '<%%= yeoman.dist %>/<%%= yeoman.css %>',
          generatedImagesDir: '<%%= yeoman.dist %>/<%%= yeoman.img %>/generated'
        }
      },
      server: {
        options: {
          debugInfo: true,
          cssDir: '.tmp/<%%= yeoman.css %>',
          generatedImagesDir: '.tmp/<%%= yeoman.img %>/generated'
        }
      }
    },<% } %><% if (jsPre === 'coffeescript') { %>
    coffee: {
      test: {
        files: [{
          expand: true,
          cwd: 'test/spec',
          src: '**/*.coffee',
          dest: '.tmp/spec',
          ext: '.js'
        }]
      },
      dist: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.jsPre %>',
          src: '**/*.coffee',
          dest: '<%%= yeoman.dist %>/<%%= yeoman.js %>',
          ext: '.js'
        }]
      },
      server: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.jsPre %>',
          src: '**/*.coffee',
          dest: '.tmp/<%%= yeoman.js %>',
          ext: '.js'
        }]
      },
    },<% } %>
    jekyll: {
      // TODO: switch config to options style after
      // https://github.com/dannygarcia/grunt-jekyll/pull/14
      dist: {
        bundleExec: true,
        src : '<%%= yeoman.app %>',
        dest: '<%%= yeoman.dist %>',
        server : false,
        auto : false,
        config: '_config.yml,_config.build.yml'
      },
      server: {
        bundleExec: true,
        src : '<%%= yeoman.app %>',
        dest: '.jekyll',
        server : false,
        auto : false,
        config: '_config.yml'
      }
    },
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.js %>/**/*.js',
        '!<%%= yeoman.app %>/<%%= yeoman.js %>/vendor/**/*',
        'test/spec/**/*.js'],
      report: [
        '{.tmp,<%%= yeoman.app %>}/<%%= yeoman.js %>/**/*.js',
        '!<%%= yeoman.app %>/<%%= yeoman.js %>/vendor/**/*']
    },
    // TODO: rewrite for globbing and bundleExec when 5.0 is released
    csscss: {
      options: {
        minMatch: 2,<% if (cssPre === 'compass' || cssPre === 'sass') { %>
        ignoreSassMixins: false,<% } %><% if (cssPre === 'compass') { %>
        compass: true,<% } %><% if (!cssPre) { %>
        showParserErrors: true,<% } %>
        colorize: true,
        shorthand: false,
        verbose: true
      },
      // Check files manually here until we can glob with 5.0
      report: {<% if (!cssPre) { %>
       src: ['<%%= yeoman.app %>/<%%= yeoman.css %>/main.css']<% } %><% if (cssPre === 'compass' || cssPre === 'sass') { %>
       src: ['<%%= yeoman.app %>/<%%= yeoman.cssPre %>/main.scss']<% } %>
      }
    },
    csslint: {
      options: {
        csslintrc: '.csslintrc'
      },
      report: {
        src: ['{.tmp,<%%= yeoman.app %>}/<%%= yeoman.css %>/**/*.css']
      }
    },
    // Note that useminPrepare will only scan one page for usemin blocks. If
    // you have usemin blocks that aren't used in index.html, create a usemin
    // manifest page (hackery!) and point the task there.
    useminPrepare: {
      options: {
        dest: '<%%= yeoman.dist %>'
      },
      html: '<%%= yeoman.dist %>/index.html'
    },
    usemin: {
      options: {
          basedir: '<%%= yeoman.dist %>',
          dirs: ['<%%= yeoman.dist %>/**/*']
      },
      html: ['<%%= yeoman.dist %>/**/*.html'],
      css: ['<%%= yeoman.dist %>/<%%= yeoman.css %>/**/*.css']
    },
    // usemin runs concat, but this is left here if you need it
    /* concat: {
      dist: {}
    },*/
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeAttributeQuotes: true,
          removeRedundantAttributes: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%%= yeoman.dist %>',
          src: '**/*.html',
          dest: '<%%= yeoman.dist %>'
        }]
      }
    },
    cssmin: {
      dist: {
        options: {
          // banner: '/* See more of my projects at github.com/<%= github %> */',
          report: 'gzip'
        },
        files: {
          '<%%= yeoman.dist %>/<%%= yeoman.css %>/main.css': [
            '.tmp/<%%= yeoman.css %>/{,*/}*.css',
            '<%%= yeoman.app %>/<%%= yeoman.css %>/{,*/}*.css']
        }
      }
    },
    uglify: {},
    imagemin: {
      dist: {
        options: {
          progressive: true
        },
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>',
          src: ['*.png', '<%%= yeoman.img %>/**/*.{png,jpg,jpeg}'],
          dest: '<%%= yeoman.dist %>'
        }]
      }
    },
    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%%= yeoman.app %>/<%%= yeoman.img %>',
          src: '**/*.svg',
          dest: '<%%= yeoman.dist %>/<%%= yeoman.img %>'
        }]
      }
    },
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%%= yeoman.app %>',
          dest: '<%%= yeoman.dist %>',
          src: [
            // Jekyll moves all html and text files
            // Copy and *min tasks transport image files and asset drectories
            // If your site requires it, add other file type patterns here
            '**/*.{gif,ico,webp}',
            '<%%= yeoman.css %>',
            '<%%= yeoman.js %>',
            '<%%= yeoman.img %>',
            '<%%= yeoman.fonts %>']
        }]
      }
    },
    rev: {
      options: {
        length: 4
      },
      dist: {
        files: {
          src: [
            '<%%= yeoman.dist %>/<%%= yeoman.js %>/**/*.js',
            '<%%= yeoman.dist %>/<%%= yeoman.css %>/**/*.css',
            '<%%= yeoman.dist %>/<%%= yeoman.img %>/**/*.{png,jpg,jpeg,gif,webp,svg}',
            '<%%= yeoman.dist %>/<%%= yeoman.fonts %>/**/*.{eot*,woff,ttf,svg}']
        }
      }
    },
    concurrent: {
      server: [<% if (cssPre === 'sass') { %>
        'sass:server'<% } %><% if (cssPre === 'compass') { %>
        'compass:server'<% } %><% if (cssPre && jsPre) { %>,<% } %><% if (jsPre === 'coffee') { %>
        'coffee:server'<% } %>
      ],
      dist: [<% if (cssPre === 'sass') { %>
        'sass:dist',<% } %><% if (cssPre === 'compass') { %>
        'compass:dist',<% } %><% if (jsPre === 'coffee') { %>
        'coffee:dist',<% } %>
        'imagemin',
        'svgmin'
      ]
    }
  });

  // Load plugins
  require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);

  // Define Tasks
  grunt.registerTask('server', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'open', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      // Jekyll cleans its destination before it compiles, so must run first
      'jekyll:server',
      'concurrent:server',
      'connect:livereload',
      'open',
      'watch']);
  });

  // No real tests yet. Add your own.
  // grunt.registerTask('test', [
  //   'clean:server',
  //   'concurrent:test',
  //   'connect:test']);

  grunt.registerTask('report', [
    'concurrent:server',
    'jshint:report',
    'csscss:report',
    'csslint:report'
    ]);

  grunt.registerTask('build', [
    'clean:dist',
    // Jekyll cleans its destination before it compiles, so must run first
    'jekyll:dist',
    'copy:dist',
    'concurrent:dist',
    'useminPrepare',
    'concat',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin'
    ]);

  grunt.registerTask('default', [
    'report',
    'build']);
};
<|endoftext|>


<commit_before>var request = require('request'),
	JSONStream = require('JSONStream');

// credentials
	// flowdock
var apiToken = "",
	flows = [],
	// github
	githubToken = "",
	githubDomain = null, // null for default
	// weather underground
	wunderToken = "";

// get flow data
var flowData = {};
for (var i = 0; i < flows.length; i++) {
	request(encodeURI("https://" + apiToken + ":DUMMY@api.flowdock.com/flows/" + flows[i]), function(error, response, body) {
		if (!error && response.statusCode == 200) {
			var flow = JSON.parse(body);
			flowData[flow.id] = { name: flow.organization.parameterized_name + "/" + flow.parameterized_name, token: flow.api_token };
		}
		else console.error("Error getting flow data: " + JSON.stringify(error || response) + "\n");
	});
}

// stream messages
var stream = request(encodeURI("https://" + apiToken + ":DUMMY@stream.flowdock.com/flows?filter=" + flows.join(",")))
	.pipe(JSONStream.parse());
stream.on('data', function(data) {
	if (data.event == "message" && typeof data.content == "string") {

		// check if message is for CheezeBot
		var match = data.content.match(/chee(?:s|z)ebot (.+)/i);
		if (match) {
			var message = match[1];

			// post reply
			var reply;
			for (var i = 0; i < commands.length; i++) {
				var command = commands[i];
				var match = message.match(command.pattern);
				if (match) {
					reply = command.reply(match, data);
					break;
				}
			}
			if (reply != null && reply != undefined) post(reply, data);
		}
	}
});
stream.on('error', function(error) {
	console.error("Error receiving stream data from flowdock: " + JSON.stringify(error) + "\n");
	// todo: handle stream errors more gracefully
});

// post a message
function post(reply, data) {
	var flow = flowData[data.flow];
	var options = {
		url: encodeURI("https://api.flowdock.com/v1/messages/chat/" + flow.token),
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify({ "content": reply.toString(), "external_user_name": "CheezeBot" })
	};
	request(options, function(error, response, body) {
		// log
		if (!error && response.statusCode == 200) {
			if (data) console.log("---" + flow.name + "---\n" + data.user + ": " + data.content);
			console.log("CheezeBot: " + reply + "\n");
		}
		else console.error("Error posting reply: " + JSON.stringify(error || response) + "\n");
	});
}

/* commands:
each command requires:
	* a 'description' for the help listing
	* a regex 'pattern' to trigger the command
	* a 'reply' method which takes parameters:
		* the regex 'match' array
		* the flowdock stream 'data' object
	  the 'reply' method returns CheezeBot's reply (or null for no reply)
	  (alternatively, 'reply' can call the 'post' method directly, which is useful inside a callback)
*/
var commands = [
	{
		description: "help:\t\t\t\tdisplay this message",
		pattern: /^help/,
		reply: function() {
			var help = "CheezeBot commands:";
			for (var i = 0; i < commands.length; i++) {
				var command = commands[i];
				help += "\n\t" + command.description;
			}
			return help;
		}
	},
	{
		description: "cheese:\t\t\t\tname a type of cheese",
		pattern: /^chee(?:s|z)e/,
		reply: function() {
			var cheeses = ["Anejo", "Asiago", "Blue", "Brie", "Butterkase", "Camembert", "Cheddar", "Chevres",
						   "Colby", "Cotija", "Edam", "Feta", "Fontina", "Gorgonzola", "Gouda", "Gruyere",
						   "Havarti", "Limburger", "Monterey Jack", "Mozzarella", "Munster", "Neufchatel",
						   "Parmesan", "Provolone", "Queso Blanco", "Raclette", "Romano", "Swiss"];
			return cheeses[Math.floor(Math.random() * cheeses.length)];
		}
	},
	{
		description: "now:\t\t\t\tcurrent date and time",
		pattern: /^now/,
		reply: function() {
			var date = new Date();
			return date.toDateString() + " - " + date.toTimeString();
		}
	},
	{
		description: "gif {search string}:\t\tshow a gif based on a search string",
		pattern: /^gif (.+)/,
		reply: function(match, data) {
			request(encodeURI("http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=" + match[1]),
				function(error, response, body) {
					if (!error && response.statusCode == 200) {
						var imageData = JSON.parse(body).data[0];
						if (imageData) post(imageData.images.original.url, data);
						else post("No suitable gif found - try a different search", data);
					}
					else console.error("Error requesting gif: " + JSON.stringify(error || response) + "\n");
				});
		}
	},
	{
		description: "timer [start|stop]:\t\tstart/stop a timer",
		pattern: /^timer(?: (start|stop))?/,
		reply: function(match, data) {
			var command = match[1];
			var time = 0;
			if (command == "start") this.timers[data.user] = new Date().getTime();
			else {
				time = (new Date().getTime() - this.timers[data.user]) / 1000;
				if (command == "stop") this.timers[data.user] = undefined;
			}
			return "timer: " + time + " s";
		},
		timers: {}
	},
	{
		description: "roll {dice}d{sides}:\t\troll dice (eg roll 2d6)",
		pattern: /^roll (\d*)d(\d+)/,
		reply: function(match) {
			var dice = Number(match[1]) || 1;
			var sides = Number(match[2]);
			var result = "";
			for (var i = 0; i < dice; i++) {
				result += (Math.floor(Math.random() * sides) + 1) + " ";
			}
			return result;
		}
	},
	{
		description: "math {func} [{params...}]:\tmath functions (eg cos, floor, pow)",
		pattern: /^math( [^ ]+)+/,
		reply: function(match) {
			var args = match[0].split(" ");
			var func = Math[args[1]];
			if (typeof func == "function") {
				args = args.slice(2).map(function(x) { return Number(x); });
				return func.apply(this, args);
			}
			else if (func != undefined) {
				return func;
			}
			return "math function unknown: " + args[1];
		}
	},
	{
		description: "pullrequests {user}/{repo}:\tlist open pull requests",
		pattern: /^pullrequests (.+\/.+)/,
		reply: function(match, data) {
			var repo = match[1].trim();
			var domain = githubDomain ? githubDomain + "/api/v3" : "https://api.github.com";
			var options = {
				url: encodeURI(domain + "/repos/" + repo + "/pulls?access_token=" + githubToken),
				method: "GET",
				rejectUnauthorized: false,
				headers: { "User-Agent": "CheezeBot" }
			};
			request(options, function(error, response, body) {
				if (!error && response.statusCode == 200) {
					var prs = JSON.parse(body);
					if (prs.length) {
						var result = "Pull requests for " + repo + ":";
						for (var i = 0; i < prs.length; i++) {
							var pr = prs[i];
							result += "\n\t*  " + pr.title + ": " + pr.html_url;
						}
						post(result, data);
					}
					else post("no open pull requests for " + repo, data);
				}
				else console.error("Error requesting github data: " + JSON.stringify(error || response) + "\n");
			});
		}
	},
	{
		description: "weather {city}:\t\t\tweather forecast information",
		pattern: /^weather (.+)/,
		reply: function(match, data) {
			request(encodeURI("http://autocomplete.wunderground.com/aq?query=" + match[1]), function(error, response, body) {
				if (!error && response.statusCode == 200) {
					var cities = JSON.parse(body).RESULTS;
					if (cities.length)  {
						request(encodeURI("http://api.wunderground.com/api/" + wunderToken + "/forecast10day" + cities[0].l + ".json"),
							function(error, response, body) {
								if (!error && response.statusCode == 200) {
									var forecast = JSON.parse(body).forecast;
									if (forecast) {
										var forecastDays = forecast.simpleforecast.forecastday;
										var result = "Weather forecast for " + cities[0].name + ":";
										for (var i = 0; i < 7; i++) {
											var day = forecastDays[i];
											result += "\n\t*  " + day.date.weekday + ":\t" + pad(20, day.conditions).substr(0, 20) + "| " +
													  day.high.celsius + "/" + day.low.celsius + "°C\t| " +
													  day.avewind.kph + "kph " + day.avewind.dir;
										}
										post(result, data);
									}
									else post("No weather information found", data);
								}
								else console.error("Error requesting weather information: " + JSON.stringify(error || response) + "\n");
							});
					}
					else post("No city found matching search", data);
				}
				else console.error("Error requesting weather information: " + JSON.stringify(error || response) + "\n");
			});
		}
	},
	{
		description: "catfact:\t\t\tthankyou for signing up for cat facts",
		pattern: /^catfact/,
		reply: function(match, data) {
			request("http://catfacts-api.appspot.com/api/facts?number=1",
				function(error, response, body) {
					if (!error && response.statusCode == 200) {
						post(JSON.parse(body).facts[0], data);
					}
					else console.error("Error requesting cat fact: " + JSON.stringify(error || response) + "\n");
				});
		}
	},
	{
		description: "about:\t\t\t\tdeveloper and source info",
		pattern: /^about/,
		reply: function() {
			return ["CheezeBot by Adam-G", "Source: git.io/1roJvQ", "Suggestions or contributions welcome.",,
			"API Credits:", "FLOWDOCK.com/api", "WUNDERGROUND.com/weather/api", "developer.GITHUB.com/v3/",
			"github.com/GIPHY/giphyapi", "CATFACTS-api.appspot.com"].join("\n");
		}
	},
];

// utility functions
function pad(len, str) {
	while (str.length < len) str += " ";
	return str;
}
<commit_msg>catch stream's "end" and "close" events, add quote command, remove "now" command<commit_after>var request = require('request'),
	fs = require('fs'),
	JSONStream = require('JSONStream');

// credentials
	// flowdock
var apiToken = "",
	flows = [],
	// github
	githubToken = "",
	githubDomain = null, // null for default
	// weather underground
	wunderToken = "";

// get flow data
var flowData = {};
for (var i = 0; i < flows.length; i++) {
	request(encodeURI("https://" + apiToken + ":DUMMY@api.flowdock.com/flows/" + flows[i]), function(error, response, body) {
		if (!error && response.statusCode == 200) {
			var flow = JSON.parse(body);
			flowData[flow.id] = { name: flow.organization.parameterized_name + "/" + flow.parameterized_name, token: flow.api_token };
		}
		else console.error("Error getting flow data: " + JSON.stringify(error || response) + "\n");
	});
}

// stream messages
var stream = request(encodeURI("https://" + apiToken + ":DUMMY@stream.flowdock.com/flows?filter=" + flows.join(",")))
	.pipe(JSONStream.parse());
stream.on('data', function(data) {
	if (data.event == "message" && typeof data.content == "string") {

		// check if message is for CheezeBot
		var match = data.content.match(/chee(?:s|z)ebot (.+)/i);
		if (match) {
			var message = match[1];

			// post reply
			var reply;
			for (var i = 0; i < commands.length; i++) {
				var command = commands[i];
				var match = message.match(command.pattern);
				if (match) {
					reply = command.reply(match, data);
					break;
				}
			}
			if (reply != null && reply != undefined) post(reply, data);
		}
	}
});
stream.on('end', function() {
	console.error("flowdock stream ended");
	// todo: handle stream end more gracefully
});
stream.on('close', function() {
	console.error("flowdock stream closed");
	// todo: handle stream close more gracefully
});
stream.on('error', function(error) {
	console.error("Error receiving stream data from flowdock: " + JSON.stringify(error) + "\n");
	// todo: handle stream error more gracefully
});

// post a message
function post(reply, data) {
	var flow = flowData[data.flow];
	var options = {
		url: encodeURI("https://api.flowdock.com/v1/messages/chat/" + flow.token),
		method: "POST",
		headers: { "Content-Type": "application/json" },
		body: JSON.stringify({ "content": reply.toString(), "external_user_name": "CheezeBot" })
	};
	request(options, function(error, response, body) {
		// log
		if (!error && response.statusCode == 200) {
			if (data) console.log("---" + flow.name + "---\n" + data.user + ": " + data.content);
			console.log("CheezeBot: " + reply + "\n");
		}
		else console.error("Error posting reply: " + JSON.stringify(error || response) + "\n");
	});
}

/* commands:
each command requires:
	* a 'description' for the help listing
	* a regex 'pattern' to trigger the command
	* a 'reply' method which takes parameters:
		* the regex 'match' array
		* the flowdock stream 'data' object
	  the 'reply' method returns CheezeBot's reply (or null for no reply)
	  (alternatively, 'reply' can call the 'post' method directly, which is useful inside a callback)
*/
var commands = [
	{
		description: "help:\t\t\t\tdisplay this message",
		pattern: /^help/,
		reply: function() {
			var help = "CheezeBot commands:";
			for (var i = 0; i < commands.length; i++) {
				var command = commands[i];
				help += "\n\t" + command.description;
			}
			return help;
		}
	},
	{
		description: "cheese:\t\t\t\tname a type of cheese",
		pattern: /^chee(?:s|z)e/,
		reply: function() {
			var cheeses = ["Anejo", "Asiago", "Blue", "Brie", "Butterkase", "Camembert", "Cheddar", "Chevres",
						   "Colby", "Cotija", "Edam", "Feta", "Fontina", "Gorgonzola", "Gouda", "Gruyere",
						   "Havarti", "Limburger", "Monterey Jack", "Mozzarella", "Munster", "Neufchatel",
						   "Parmesan", "Provolone", "Queso Blanco", "Raclette", "Romano", "Swiss"];
			return cheeses[Math.floor(Math.random() * cheeses.length)];
		}
	},
	{
		description: "gif {search string}:\t\tshow a gif based on a search string",
		pattern: /^gif (.+)/,
		reply: function(match, data) {
			request(encodeURI("http://api.giphy.com/v1/gifs/search?api_key=dc6zaTOxFJmzC&limit=1&q=" + match[1]),
				function(error, response, body) {
					if (!error && response.statusCode == 200) {
						var imageData = JSON.parse(body).data[0];
						if (imageData) post(imageData.images.original.url, data);
						else post("No suitable gif found - try a different search", data);
					}
					else console.error("Error requesting gif: " + JSON.stringify(error || response) + "\n");
				});
		}
	},
	{
		description: "timer [start|stop]:\t\tstart/stop a timer",
		pattern: /^timer(?: (start|stop))?/,
		reply: function(match, data) {
			var command = match[1];
			var time = 0;
			if (command == "start") this.timers[data.user] = new Date().getTime();
			else {
				time = (new Date().getTime() - this.timers[data.user]) / 1000;
				if (command == "stop") this.timers[data.user] = undefined;
			}
			return "timer: " + time + " s";
		},
		timers: {}
	},
	{
		description: "roll {dice}d{sides}:\t\troll dice (eg roll 2d6)",
		pattern: /^roll (\d*)d(\d+)/,
		reply: function(match) {
			var dice = Number(match[1]) || 1;
			var sides = Number(match[2]);
			var result = "";
			for (var i = 0; i < dice; i++) {
				result += (Math.floor(Math.random() * sides) + 1) + " ";
			}
			return result;
		}
	},
	{
		description: "math {func} [{params...}]:\tmath functions (eg cos, floor, pow)",
		pattern: /^math( [^ ]+)+/,
		reply: function(match) {
			var args = match[0].split(" ");
			var func = Math[args[1]];
			if (typeof func == "function") {
				args = args.slice(2).map(function(x) { return Number(x); });
				return func.apply(this, args);
			}
			else if (func != undefined) {
				return func;
			}
			return "math function unknown: " + args[1];
		}
	},
	{
		description: "pullrequests {user}/{repo}:\tlist open pull requests",
		pattern: /^pullrequests (.+\/.+)/,
		reply: function(match, data) {
			var repo = match[1].trim();
			var domain = githubDomain ? githubDomain + "/api/v3" : "https://api.github.com";
			var options = {
				url: encodeURI(domain + "/repos/" + repo + "/pulls?access_token=" + githubToken),
				method: "GET",
				rejectUnauthorized: false,
				headers: { "User-Agent": "CheezeBot" }
			};
			request(options, function(error, response, body) {
				if (!error && response.statusCode == 200) {
					var prs = JSON.parse(body);
					if (prs.length) {
						var result = "Pull requests for " + repo + ":";
						for (var i = 0; i < prs.length; i++) {
							var pr = prs[i];
							result += "\n\t*  " + pr.title + ": " + pr.html_url;
						}
						post(result, data);
					}
					else post("no open pull requests for " + repo, data);
				}
				else console.error("Error requesting github data: " + JSON.stringify(error || response) + "\n");
			});
		}
	},
	{
		description: "weather {city}:\t\t\tweather forecast information",
		pattern: /^weather (.+)/,
		reply: function(match, data) {
			request(encodeURI("http://autocomplete.wunderground.com/aq?query=" + match[1]), function(error, response, body) {
				if (!error && response.statusCode == 200) {
					var cities = JSON.parse(body).RESULTS;
					if (cities.length)  {
						request(encodeURI("http://api.wunderground.com/api/" + wunderToken + "/forecast10day" + cities[0].l + ".json"),
							function(error, response, body) {
								if (!error && response.statusCode == 200) {
									var forecast = JSON.parse(body).forecast;
									if (forecast) {
										var forecastDays = forecast.simpleforecast.forecastday;
										var result = "Weather forecast for " + cities[0].name + ":";
										for (var i = 0; i < 7; i++) {
											var day = forecastDays[i];
											result += "\n\t*  " + day.date.weekday + ":\t" + pad(20, day.conditions).substr(0, 20) + "| " +
													  day.high.celsius + "/" + day.low.celsius + "°C\t| " +
													  day.avewind.kph + "kph " + day.avewind.dir;
										}
										post(result, data);
									}
									else post("No weather information found", data);
								}
								else console.error("Error requesting weather information: " + JSON.stringify(error || response) + "\n");
							});
					}
					else post("No city found matching search", data);
				}
				else console.error("Error requesting weather information: " + JSON.stringify(error || response) + "\n");
			});
		}
	},
	{
		description: "catfact:\t\t\tthankyou for signing up for cat facts",
		pattern: /^catfact/,
		reply: function(match, data) {
			request("http://catfacts-api.appspot.com/api/facts?number=1",
				function(error, response, body) {
					if (!error && response.statusCode == 200) {
						post(JSON.parse(body).facts[0], data);
					}
					else console.error("Error requesting cat fact: " + JSON.stringify(error || response) + "\n");
				});
		}
	},
	{
		description: "quote [\"{quote}\" - {quotee}]:\tquote store",
		pattern: /^quote(?: (.+))?/,
		reply: function(match, data) {
			if (typeof match[1] == "string") {
				var quote = match[1].trim();
				if (quote.match(/".+" - .+/)) {
					fs.appendFile("quotes.txt", quote + "\n", function(error) {
						if (!error) post("Thanks for the new quote!", data);
						else console.error("Error writing quote: " + JSON.stringify(error));
					});
				}
				else post("No! Badly formatted quote!", data);
			}
			else {
				fs.readFile("quotes.txt", {encoding: "utf8"}, function(error, quoteFile) {
					if (!error) {
						var quotes = quoteFile.split("\n").filter(function(x) { return !!x; });
						post(quotes[Math.floor(Math.random() * quotes.length)], data);
					}
					else console.error("Error reading quote: " + JSON.stringify(error));
				});
			}
		}
	},
	{
		description: "about:\t\t\t\tdeveloper and source info",
		pattern: /^about/,
		reply: function() {
			return ["CheezeBot by Adam-G", "Source: git.io/1roJvQ", "Suggestions or contributions welcome.",,
			"API Credits:", "FLOWDOCK.com/api", "WUNDERGROUND.com/weather/api", "developer.GITHUB.com/v3/",
			"github.com/GIPHY/giphyapi", "CATFACTS-api.appspot.com"].join("\n");
		}
	},
];

// utility functions
function pad(len, str) {
	while (str.length < len) str += " ";
	return str;
}
<|endoftext|>


<commit_before>$(function() {
"use strict";

function vercmp(a, b) {
  var re = /^([0-9]*)([^0-9]*)(.*)$/;
  var abits, bbits;
  var anum, bnum;
  while (a.length && b.length) {
    abits = re.exec(a);
    bbits = re.exec(b);
    if ((abits[1] === '') != (bbits[1] === '')) {
      return abits[1] ? -1 : 1;
    }
    if (abits[1] !== '') {
      anum = parseInt(abits[1]);
      bnum = parseInt(bbits[1])
      if (anum !== bnum)
        return anum - bnum;
    }

    if (abits[2] !== bbits[2]) {
      return abits[2] < bbits[2] ? -1 : 1
    }

    a = abits[3];
    b = bbits[3];
  }

  return a.length - b.length;
}

function shorten(ref) {
  var match = /^refs\/(tags|branches)\/(.*)/.exec(ref);
  if (match)
    return match[2];
  match = /^([0-9a-f]{8})[0-9a-f]+$/.exec(ref);
  if (match)
    return match[1];
  return ref;
}

var Match = Backbone.Model.extend({
  initialize: function() {
    this.get('contexts').forEach(function (ctx) {
        ctx.paths.sort(function (a,b) {return vercmp(a.ref, b.ref);})
    });
    this.get('contexts').sort(function (a,b) {
        return vercmp(a.paths[0].ref, b.paths[0].ref);
    });
    this.set({
               context: this.get('contexts')[0],
               path: this.get('contexts')[0].paths[0]
             });
  },
  url: function() {
    var tree = this.get('path').ref;
    var pieces, name = '', ref;
    pieces = tree.split(":");
    if (pieces.length == 1) {
      ref = tree;
    } else {
      name = pieces[0];
      ref = pieces[1];
    }
    return ""
    return "https://github.com/" + CodesearchUI.github_repos[this.get('backend')][name] +
      "/blob/" + shorten(ref) + "/" + this.get('path').path +
      "#L" + this.get('context').lno;
  }
});

var MatchView = Backbone.View.extend({
  tagName: 'div',
  initialize: function() {
    this.model.on('change', this.render, this);
  },
  render: function() {
    var div = this._render();
    this.$el.empty();
    this.$el.append(div);
    return this;
  },
  _render: function() {
    var h = new HTMLFactory();
    var ctx = this.model.get('context');
    var i;
    var ctx_before = [], ctx_after = [];
    for (i = 0; i < ctx.context_before.length; i ++) {
      ctx_before.unshift(h.div([
                                 h.span({cls: 'lno'}, [ctx.lno - i - 1, ":"]),
                                 ctx.context_before[i]
                               ]));
    }
    for (i = 0; i < ctx.context_after.length; i ++) {
      ctx_after.push(h.div([
                             h.span({cls: 'lno'}, [ctx.lno + i + 1, ":"]),
                             ctx.context_after[i]
                           ]));
    }
    var line = this.model.get('line');
    var bounds = this.model.get('bounds');
    var pieces = [line.substring(0, bounds[0]),
                  line.substring(bounds[0], bounds[1]),
                  line.substring(bounds[1])];

    return h.div({cls: 'match'}, [
        h.div({}, [
          h.span({cls: 'label'}, [
            h.a({href: this.model.url()}, [
                  shorten(this.model.get('path').ref),
                  ":",
                  this.model.get('path').path])])]),
        h.div({cls: 'contents'}, [
                ctx_before,
                h.div({cls: 'matchline'}, [
                  h.span({cls: 'lno'}, [ctx.lno + ":"]),
                  pieces[0],
                  h.span({cls: 'matchstr'}, [pieces[1]]),
                  pieces[2]
                ]),
                ctx_after]),
        this.render_contexts(h)]);
  },
  render_contexts: function(h) {
    var self = this;
    if (this.model.get('contexts').length == 1 &&
        this.model.get('context').paths.length == 1)
      return [];
    return h.div({cls: 'contexts'}, [
          h.span({cls: 'label'}, ["Also in:"]),
          h.ul({},
          this.model.get('contexts').map(function (ctx) {
            return h.li(ctx === self.model.get('context') ? {cls: 'selected'} : {}, [
                h.a({href: "#",
                     click: _.bind(self.switch_context, self, ctx)}, [
                shorten(ctx.paths[0].ref)]),
                ctx.paths.length > 1 ? (" +" + (ctx.paths.length - 1) + " identical") : [],
            ]);
          }))])
  },
  switch_context: function(ctx) {
    this.model.set({
                     context: ctx,
                     path: ctx.paths[0]
                   });
    return false;
  }
});

var MatchSet = Backbone.Collection.extend({
  model: Match
});

var SearchState = Backbone.Model.extend({
  defaults: function() {
    return {
      displaying: null,
      error: null,
      time: null,
      why: null
    };
  },

  initialize: function() {
    this.search_map = {};
    this.matches = new MatchSet();
    this.search_id = 0;
    this.on('change:displaying', this.new_search, this);
  },

  new_search: function() {
    this.set({
        error: null,
        time: null,
        why: null
    });
    this.matches.reset();
    for (var k in this.search_map) {
      if (parseInt(k) < this.get('displaying'))
        delete this.search_map[k];
    }
  },

  next_id: function() {
    return ++this.search_id;
  },

  dispatch: function (search) {
    var id = this.next_id();
    search.id = id;
    this.search_map[id] = {
      q: search.line,
      file: search.file,
      backend: search.backend,
      repo: search.repo
    };
    if (!search.line.length)
      this.set('displaying', id);
  },

  url: function() {
    var q = {};
    var current = this.search_map[this.get('displaying')];
    if (!current)
      return '/search';
    var base = '/search';

    ['q','file', 'repo'].forEach(function (key) {
      if(current[key])
        q[key] = current[key];
    });

    if (q.backend) {
      base += "/" + q.backend
    } else if (CodesearchUI.input_backend) {
      base += "/" + CodesearchUI.input_backend.val();
    }
    var qs = $.param(q);
    return base + (qs ? "?" + qs : "");
  },

  handle_error: function (search, error) {
    if (search === this.search_id) {
      this.set('displaying', search);
      this.set('error', error);
    }
  },

  handle_match: function (search, match) {
    if (search < this.get('displaying'))
      return false;
    this.set('displaying', search);
    this.matches.add({
                       backend: this.search_map[search].backend,
                       line: match.line,
                       bounds: match.bounds,
                       contexts: match.contexts
                     });
  },
  handle_done: function (search, time, why) {
    this.set('displaying', search);
    this.set({time: time, why: why});
  }
});

var MatchesView = Backbone.View.extend({
  el: $('#results'),
  initialize: function() {
    this.model.matches.bind('add', this.add_one, this);
    this.model.matches.bind('reset', this.add_all, this);
  },
  add_one: function(model) {
    var view = new MatchView({model: model});
    this.$el.append(view.render().el);
  },
  add_all: function() {
    this.$el.empty();
    this.model.matches.each(this.add_one);
  }
});

var ResultView = Backbone.View.extend({
  el: $('#resultarea'),
  initialize: function() {
    this.matches_view = new MatchesView({model: this.model});
    this.permalink = this.$('#permalink');
    this.results   = this.$('#numresults');
    this.errorbox  = $('#regex-error');
    this.time      = this.$('#searchtime');
    this.last_url  = null;

    this.model.on('all', this.render, this);
    this.model.matches.on('all', this.render, this);
  },

  render: function() {
    if (this.model.get('error')) {
      this.errorbox.find('#errortext').text(this.model.get('error'));
      this.errorbox.show();
    } else {
      this.errorbox.hide()
    }

    var url = this.model.url();
    if (this.last_url !== url ) {
      this.permalink.attr('href', url);
      if (history.replaceState) {
        history.replaceState(null, '', url);
      }
      this.last_url = url;
    }

    if (this.model.search_map[this.model.get('displaying')].q === '' ||
       this.model.get('error')) {
      this.$el.hide();
      return this;
    }

    this.$el.show();

    if (this.model.get('time')) {
      this.$('#searchtimebox').show();
      var time = this.model.get('time');
      this.time.text((time/1000) + "s")
    } else {
      this.$('#searchtimebox').hide();
    }

    var results = '' + this.model.matches.size();
    if (this.model.get('why') === 'limit')
      results = results + '+';
    this.results.text(results);

    return this;
  }
});

var CodesearchUI = function() {
  return {
    state: new SearchState(),
    view: null,
    onload: function() {
      if (CodesearchUI.input)
        return;

      CodesearchUI.view = new ResultView({model: CodesearchUI.state});

      CodesearchUI.input      = $('#searchbox');
      CodesearchUI.input_file = $('#filebox');
      CodesearchUI.input_repo = $('#repobox');
      CodesearchUI.input_backend = $('#backend');
      if (CodesearchUI.input_backend.length == 0)
        CodesearchUI.input_backend = null;
      CodesearchUI.parse_url();

      CodesearchUI.input.keydown(CodesearchUI.keypress);
      CodesearchUI.input_file.keydown(CodesearchUI.keypress);
      CodesearchUI.input_repo.keydown(CodesearchUI.keypress);
      CodesearchUI.input.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input_file.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input_repo.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input.focus();
      if (CodesearchUI.input_backend)
        CodesearchUI.input_backend.change(CodesearchUI.select_backend);

      Codesearch.connect(CodesearchUI);
    },
    parse_url: function() {
      var parms = CodesearchUI.parse_query_params();
      if (parms.q)
        CodesearchUI.input.val(parms.q);
      if (parms.file)
        CodesearchUI.input_file.val(parms.file);
      if (parms.repo)
        CodesearchUI.input_repo.val(parms.repo);
      var backend = null;
      if (parms.backend)
        backend = parms.backend;
      var m;
      if (m = (new RegExp("/search/(\\w+)/?").exec(window.location.pathname))) {
        backend = m[1];
      }
      if (backend && CodesearchUI.input_backend)
        CodesearchUI.input_backend.val(backend);
      setTimeout(CodesearchUI.select_backend, 0);
    },
    parse_query_params: function() {
      var urlParams = {};
      var e,
          a = /\+/g,
          r = /([^&=]+)=?([^&]*)/g,
          d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
          q = window.location.search.substring(1);

      while (e = r.exec(q))
        urlParams[d(e[1])] = d(e[2]);
      return urlParams;
    },
    on_connect: function() {
      CodesearchUI.newsearch();
    },
    select_backend: function() {
      if (!CodesearchUI.input_backend)
        return;
      var backend = CodesearchUI.input_backend.val();
      if (Object.keys(CodesearchUI.github_repos[backend]).length == 1) {
        CodesearchUI.input_repo.val('');
        $('#reposel').hide();
      } else {
        $('#reposel').show();
      }
      CodesearchUI.keypress();
    },
    keypress: function() {
      setTimeout(CodesearchUI.newsearch, 0);
    },
    newsearch: function() {
      var search = {
        line: CodesearchUI.input.val(),
        file: CodesearchUI.input_file.val(),
        repo: CodesearchUI.input_repo.val(),
      };
      if(CodesearchUI.input_backend)
        search.backend = CodesearchUI.input_backend.val();
      CodesearchUI.state.dispatch(search);
      Codesearch.new_search(search);
    },
    error: function(search, error) {
      CodesearchUI.state.handle_error(search, error);
    },
    match: function(search, match) {
      CodesearchUI.state.handle_match(search, match);
    },
    search_done: function(search, time, why) {
      CodesearchUI.state.handle_done(search, time, why);
    },
    github_repos: {}
  };
}();
CodesearchUI.onload();
window.CodesearchUI = CodesearchUI;
});
<commit_msg>Don't dispatch empty searches to the server.<commit_after>$(function() {
"use strict";

function vercmp(a, b) {
  var re = /^([0-9]*)([^0-9]*)(.*)$/;
  var abits, bbits;
  var anum, bnum;
  while (a.length && b.length) {
    abits = re.exec(a);
    bbits = re.exec(b);
    if ((abits[1] === '') != (bbits[1] === '')) {
      return abits[1] ? -1 : 1;
    }
    if (abits[1] !== '') {
      anum = parseInt(abits[1]);
      bnum = parseInt(bbits[1])
      if (anum !== bnum)
        return anum - bnum;
    }

    if (abits[2] !== bbits[2]) {
      return abits[2] < bbits[2] ? -1 : 1
    }

    a = abits[3];
    b = bbits[3];
  }

  return a.length - b.length;
}

function shorten(ref) {
  var match = /^refs\/(tags|branches)\/(.*)/.exec(ref);
  if (match)
    return match[2];
  match = /^([0-9a-f]{8})[0-9a-f]+$/.exec(ref);
  if (match)
    return match[1];
  return ref;
}

var Match = Backbone.Model.extend({
  initialize: function() {
    this.get('contexts').forEach(function (ctx) {
        ctx.paths.sort(function (a,b) {return vercmp(a.ref, b.ref);})
    });
    this.get('contexts').sort(function (a,b) {
        return vercmp(a.paths[0].ref, b.paths[0].ref);
    });
    this.set({
               context: this.get('contexts')[0],
               path: this.get('contexts')[0].paths[0]
             });
  },
  url: function() {
    var tree = this.get('path').ref;
    var pieces, name = '', ref;
    pieces = tree.split(":");
    if (pieces.length == 1) {
      ref = tree;
    } else {
      name = pieces[0];
      ref = pieces[1];
    }
    return ""
    return "https://github.com/" + CodesearchUI.github_repos[this.get('backend')][name] +
      "/blob/" + shorten(ref) + "/" + this.get('path').path +
      "#L" + this.get('context').lno;
  }
});

var MatchView = Backbone.View.extend({
  tagName: 'div',
  initialize: function() {
    this.model.on('change', this.render, this);
  },
  render: function() {
    var div = this._render();
    this.$el.empty();
    this.$el.append(div);
    return this;
  },
  _render: function() {
    var h = new HTMLFactory();
    var ctx = this.model.get('context');
    var i;
    var ctx_before = [], ctx_after = [];
    for (i = 0; i < ctx.context_before.length; i ++) {
      ctx_before.unshift(h.div([
                                 h.span({cls: 'lno'}, [ctx.lno - i - 1, ":"]),
                                 ctx.context_before[i]
                               ]));
    }
    for (i = 0; i < ctx.context_after.length; i ++) {
      ctx_after.push(h.div([
                             h.span({cls: 'lno'}, [ctx.lno + i + 1, ":"]),
                             ctx.context_after[i]
                           ]));
    }
    var line = this.model.get('line');
    var bounds = this.model.get('bounds');
    var pieces = [line.substring(0, bounds[0]),
                  line.substring(bounds[0], bounds[1]),
                  line.substring(bounds[1])];

    return h.div({cls: 'match'}, [
        h.div({}, [
          h.span({cls: 'label'}, [
            h.a({href: this.model.url()}, [
                  shorten(this.model.get('path').ref),
                  ":",
                  this.model.get('path').path])])]),
        h.div({cls: 'contents'}, [
                ctx_before,
                h.div({cls: 'matchline'}, [
                  h.span({cls: 'lno'}, [ctx.lno + ":"]),
                  pieces[0],
                  h.span({cls: 'matchstr'}, [pieces[1]]),
                  pieces[2]
                ]),
                ctx_after]),
        this.render_contexts(h)]);
  },
  render_contexts: function(h) {
    var self = this;
    if (this.model.get('contexts').length == 1 &&
        this.model.get('context').paths.length == 1)
      return [];
    return h.div({cls: 'contexts'}, [
          h.span({cls: 'label'}, ["Also in:"]),
          h.ul({},
          this.model.get('contexts').map(function (ctx) {
            return h.li(ctx === self.model.get('context') ? {cls: 'selected'} : {}, [
                h.a({href: "#",
                     click: _.bind(self.switch_context, self, ctx)}, [
                shorten(ctx.paths[0].ref)]),
                ctx.paths.length > 1 ? (" +" + (ctx.paths.length - 1) + " identical") : [],
            ]);
          }))])
  },
  switch_context: function(ctx) {
    this.model.set({
                     context: ctx,
                     path: ctx.paths[0]
                   });
    return false;
  }
});

var MatchSet = Backbone.Collection.extend({
  model: Match
});

var SearchState = Backbone.Model.extend({
  defaults: function() {
    return {
      displaying: null,
      error: null,
      time: null,
      why: null
    };
  },

  initialize: function() {
    this.search_map = {};
    this.matches = new MatchSet();
    this.search_id = 0;
    this.on('change:displaying', this.new_search, this);
  },

  new_search: function() {
    this.set({
        error: null,
        time: null,
        why: null
    });
    this.matches.reset();
    for (var k in this.search_map) {
      if (parseInt(k) < this.get('displaying'))
        delete this.search_map[k];
    }
  },

  next_id: function() {
    return ++this.search_id;
  },

  dispatch: function (search) {
    var id = this.next_id();
    search.id = id;
    this.search_map[id] = {
      q: search.line,
      file: search.file,
      backend: search.backend,
      repo: search.repo
    };
    if (!search.line.length)
      this.set('displaying', id);
  },

  url: function() {
    var q = {};
    var current = this.search_map[this.get('displaying')];
    if (!current)
      return '/search';
    var base = '/search';

    ['q','file', 'repo'].forEach(function (key) {
      if(current[key])
        q[key] = current[key];
    });

    if (q.backend) {
      base += "/" + q.backend
    } else if (CodesearchUI.input_backend) {
      base += "/" + CodesearchUI.input_backend.val();
    }
    var qs = $.param(q);
    return base + (qs ? "?" + qs : "");
  },

  handle_error: function (search, error) {
    if (search === this.search_id) {
      this.set('displaying', search);
      this.set('error', error);
    }
  },

  handle_match: function (search, match) {
    if (search < this.get('displaying'))
      return false;
    this.set('displaying', search);
    this.matches.add({
                       backend: this.search_map[search].backend,
                       line: match.line,
                       bounds: match.bounds,
                       contexts: match.contexts
                     });
  },
  handle_done: function (search, time, why) {
    this.set('displaying', search);
    this.set({time: time, why: why});
  }
});

var MatchesView = Backbone.View.extend({
  el: $('#results'),
  initialize: function() {
    this.model.matches.bind('add', this.add_one, this);
    this.model.matches.bind('reset', this.add_all, this);
  },
  add_one: function(model) {
    var view = new MatchView({model: model});
    this.$el.append(view.render().el);
  },
  add_all: function() {
    this.$el.empty();
    this.model.matches.each(this.add_one);
  }
});

var ResultView = Backbone.View.extend({
  el: $('#resultarea'),
  initialize: function() {
    this.matches_view = new MatchesView({model: this.model});
    this.permalink = this.$('#permalink');
    this.results   = this.$('#numresults');
    this.errorbox  = $('#regex-error');
    this.time      = this.$('#searchtime');
    this.last_url  = null;

    this.model.on('all', this.render, this);
    this.model.matches.on('all', this.render, this);
  },

  render: function() {
    if (this.model.get('error')) {
      this.errorbox.find('#errortext').text(this.model.get('error'));
      this.errorbox.show();
    } else {
      this.errorbox.hide()
    }

    var url = this.model.url();
    if (this.last_url !== url ) {
      this.permalink.attr('href', url);
      if (history.replaceState) {
        history.replaceState(null, '', url);
      }
      this.last_url = url;
    }

    if (this.model.search_map[this.model.get('displaying')].q === '' ||
       this.model.get('error')) {
      this.$el.hide();
      return this;
    }

    this.$el.show();

    if (this.model.get('time')) {
      this.$('#searchtimebox').show();
      var time = this.model.get('time');
      this.time.text((time/1000) + "s")
    } else {
      this.$('#searchtimebox').hide();
    }

    var results = '' + this.model.matches.size();
    if (this.model.get('why') === 'limit')
      results = results + '+';
    this.results.text(results);

    return this;
  }
});

var CodesearchUI = function() {
  return {
    state: new SearchState(),
    view: null,
    onload: function() {
      if (CodesearchUI.input)
        return;

      CodesearchUI.view = new ResultView({model: CodesearchUI.state});

      CodesearchUI.input      = $('#searchbox');
      CodesearchUI.input_file = $('#filebox');
      CodesearchUI.input_repo = $('#repobox');
      CodesearchUI.input_backend = $('#backend');
      if (CodesearchUI.input_backend.length == 0)
        CodesearchUI.input_backend = null;
      CodesearchUI.parse_url();

      CodesearchUI.input.keydown(CodesearchUI.keypress);
      CodesearchUI.input_file.keydown(CodesearchUI.keypress);
      CodesearchUI.input_repo.keydown(CodesearchUI.keypress);
      CodesearchUI.input.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input_file.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input_repo.bind('paste', CodesearchUI.keypress);
      CodesearchUI.input.focus();
      if (CodesearchUI.input_backend)
        CodesearchUI.input_backend.change(CodesearchUI.select_backend);

      Codesearch.connect(CodesearchUI);
    },
    parse_url: function() {
      var parms = CodesearchUI.parse_query_params();
      if (parms.q)
        CodesearchUI.input.val(parms.q);
      if (parms.file)
        CodesearchUI.input_file.val(parms.file);
      if (parms.repo)
        CodesearchUI.input_repo.val(parms.repo);
      var backend = null;
      if (parms.backend)
        backend = parms.backend;
      var m;
      if (m = (new RegExp("/search/(\\w+)/?").exec(window.location.pathname))) {
        backend = m[1];
      }
      if (backend && CodesearchUI.input_backend)
        CodesearchUI.input_backend.val(backend);
      setTimeout(CodesearchUI.select_backend, 0);
    },
    parse_query_params: function() {
      var urlParams = {};
      var e,
          a = /\+/g,
          r = /([^&=]+)=?([^&]*)/g,
          d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
          q = window.location.search.substring(1);

      while (e = r.exec(q))
        urlParams[d(e[1])] = d(e[2]);
      return urlParams;
    },
    on_connect: function() {
      CodesearchUI.newsearch();
    },
    select_backend: function() {
      if (!CodesearchUI.input_backend)
        return;
      var backend = CodesearchUI.input_backend.val();
      if (Object.keys(CodesearchUI.github_repos[backend]).length == 1) {
        CodesearchUI.input_repo.val('');
        $('#reposel').hide();
      } else {
        $('#reposel').show();
      }
      CodesearchUI.keypress();
    },
    keypress: function() {
      setTimeout(CodesearchUI.newsearch, 0);
    },
    newsearch: function() {
      var search = {
        line: CodesearchUI.input.val(),
        file: CodesearchUI.input_file.val(),
        repo: CodesearchUI.input_repo.val(),
      };
      if(CodesearchUI.input_backend)
        search.backend = CodesearchUI.input_backend.val();
      CodesearchUI.state.dispatch(search);
      if (search.line.length > 0)
        Codesearch.new_search(search);
    },
    error: function(search, error) {
      CodesearchUI.state.handle_error(search, error);
    },
    match: function(search, match) {
      CodesearchUI.state.handle_match(search, match);
    },
    search_done: function(search, time, why) {
      CodesearchUI.state.handle_done(search, time, why);
    },
    github_repos: {}
  };
}();
CodesearchUI.onload();
window.CodesearchUI = CodesearchUI;
});
<|endoftext|>


<commit_before>(function() {

  'use strict';
  Polymer({
    is: 'arc-xhr-request',

    behaviors: [
      ArcBehaviors.ArcRequestBehavior
    ],

    properties: {
      request: Object,
      // If true the request has been aborted and the response shouldn't be reported.
      aborted: Boolean,
      // Set by chrome-connect element.
      connected: {
        type: Boolean,
        notify: true
      }
    },

    run: function() {
      this.aborted = false;
      if (!this.$.ext.connected) {
        this.fire('error', {
          message: 'ARC extension is not installed.',
          code: 900
        });
        return null;
      }
      this.getRequestTimeout()
      .then(() => this._prepareRequest())
      .catch((e) => {
        this.fire('error', {
          message: e.message
        });
        return null;
      })
      .then((init) => {
        if (!init) {
          this.fire('error', {
            message: 'The request object could not be initialized'
          });
          return null;
        }
        this.$.ext.postMessage({
          'payload': 'proxy-xhr',
          'request': init
        });
      });
    },

    _prepareRequest: function() {
      return new Promise((resolve) => {
        var init = {
          method: this.request.method,
          url: this.request.url,
          payload: this.request.payload,
          timeout: this.timeout
        };
        if (this.request.headers) {
          init.headers = arc.app.headers.toJSON(this.request.headers);
        }
        init.timeout = this.timeout;
        if (this.request.files && this.request.files.length > 0) {
          this.serializeFiles(this.request.files).then((files) => {
            init.files = files;
            resolve(init);
          });
        } else {
          resolve(init);
        }
      });
    },

    _onExternalMessage: function(e) {
      var msg = e.detail;
      if (!msg || !msg.payload) {
        return;
      }
      if (msg.payload === 'proxy-xhr') {
        delete msg.payload;
        this._processResponse(msg);
      }
    },

    _processResponse: function(msg) {
      if (this.aborted) {
        return;
      }
      if (msg.log && msg.log.length) {
        msg.log.forEach((log) => console.log(log));
      }
      if (msg.error) {
        let detail = {
          request: {
            xhr: true
          },
          response: {
            error: {
              message: msg.message || 'Unknown error in connection.'
            }
          }
        };
        this.fire('ready', detail);
        return;
      }
      var response = msg.response;
      var h = arc.app.headers.toJSON(response.headers);
      var list = {};
      h.forEach((item) => list[item.name] = item.value);
      var hObj;
      try {
        hObj = new Headers(list);
      } catch (e) {
        let msg = 'Hi!. I was unable to create a Headers object. This sucks but the issue report ';
        msg += 'should be reported to the Chromium project guys.';
        console.log(msg);
        hObj = new Headers();
      }
      response._headers = hObj;
      response.headers = h;
      response.redirects = [];
      response.ok = true;
      response.auth = {};
      response.stats = response.stats;

      var ct = hObj.get('content-type');
      var cl = Number(hObj.get('content-length'));
      if (cl && cl === 0) {
        response.body = '';
      } else if (ct && ct.indexOf('json') !== -1) {
        try {
          response.body = JSON.parse(response.response);
        } catch (e) {
          response.body = response.response;
        }
      } else {
        response.body = response.response;
      }
      delete response.response;
      let detail = {
        request: {
          xhr: true
        },
        response: response
      };
      this.fire('ready', detail);
    },

    abort: function() {
      this.aborted = true;
    }
  });
})();
<commit_msg>Fix: Fixes an issue whereusing XHR no response is displayed (fix #798)<commit_after>(function() {

  'use strict';
  Polymer({
    is: 'arc-xhr-request',

    behaviors: [
      ArcBehaviors.ArcRequestBehavior
    ],

    properties: {
      request: Object,
      // If true the request has been aborted and the response shouldn't be reported.
      aborted: Boolean,
      // Set by chrome-connect element.
      connected: {
        type: Boolean,
        notify: true
      }
    },

    run: function() {
      this.aborted = false;
      if (!this.$.ext.connected) {
        this.fire('error', {
          message: 'ARC extension is not installed.',
          code: 900
        });
        return null;
      }
      this.getRequestTimeout()
      .then(() => this._prepareRequest())
      .catch((e) => {
        this.fire('error', {
          message: e.message
        });
        return null;
      })
      .then((init) => {
        if (!init) {
          this.fire('error', {
            message: 'The request object could not be initialized'
          });
          return null;
        }
        this.$.ext.postMessage({
          'payload': 'proxy-xhr',
          'request': init
        });
      });
    },

    _prepareRequest: function() {
      return new Promise((resolve) => {
        var init = {
          method: this.request.method,
          url: this.request.url,
          payload: this.request.payload,
          timeout: this.timeout
        };
        if (this.request.headers) {
          init.headers = arc.app.headers.toJSON(this.request.headers);
        }
        init.timeout = this.timeout;
        if (this.request.files && this.request.files.length > 0) {
          this.serializeFiles(this.request.files).then((files) => {
            init.files = files;
            resolve(init);
          });
        } else {
          resolve(init);
        }
      });
    },

    _onExternalMessage: function(e) {
      var msg = e.detail;
      if (!msg || !msg.payload) {
        return;
      }
      if (msg.payload === 'proxy-xhr') {
        delete msg.payload;
        this._processResponse(msg);
      }
    },

    _processResponse: function(msg) {
      if (this.aborted) {
        return;
      }
      if (msg.log && msg.log.length) {
        msg.log.forEach((log) => console.log(log));
      }
      if (msg.error) {
        let detail = {
          request: {
            xhr: true
          },
          response: {
            error: {
              message: msg.message || 'Unknown error in connection.'
            }
          }
        };
        this.fire('ready', detail);
        return;
      }
      var response = msg.response;
      var h = arc.app.headers.toJSON(response.headers);
      var list = {};
      h.forEach((item) => list[item.name] = item.value);
      var hObj;
      try {
        hObj = new Headers(list);
      } catch (e) {
        let msg = 'Hi!. I was unable to create a Headers object. This sucks but the issue report ';
        msg += 'should be reported to the Chromium project guys.';
        console.log(msg);
        hObj = new Headers();
      }
      response._headers = hObj;
      response.headers = h;
      response.redirects = [];
      response.ok = true;
      response.auth = {};
      response.stats = response.stats;
      response.rawBody = response.response;

      var ct = hObj.get('content-type');
      var cl = Number(hObj.get('content-length'));
      if (cl && cl === 0) {
        response.body = '';
      } else if (ct && ct.indexOf('json') !== -1) {
        try {
          response.body = JSON.parse(response.response);
        } catch (e) {
          response.body = response.response;
        }
      } else {
        response.body = response.response;
      }
      delete response.response;
      let detail = {
        request: {
          xhr: true
        },
        response: response
      };
      this.fire('ready', detail);
    },

    abort: function() {
      this.aborted = true;
    }
  });
})();
<|endoftext|>