<commit_before>/*!
 * Bootstrap's Gruntfile
 * http://getbootstrap.com
 * Copyright 2013-2016 The Bootstrap Authors
 * Copyright 2013-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

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

  // Force use of Unix newlines
  grunt.util.linefeed = '\n';

  RegExp.quote = function (string) {
    return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  var fs = require('fs');
  var path = require('path');
  var isTravis = require('is-travis');

  var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });

  Object.keys(configBridge.paths).forEach(function (key) {
    configBridge.paths[key].forEach(function (val, i, arr) {
      arr[i] = path.join('./docs/assets', val);
    });
  });

  // Project configuration.
  grunt.initConfig({

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*!\n' +
            ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
            ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
            ' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
            ' */\n',
    jqueryCheck: 'if (typeof jQuery === \'undefined\') {\n' +
                 '  throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery\')\n' +
                 '}\n',
    jqueryVersionCheck: '+function ($) {\n' +
                        '  var version = $.fn.jquery.split(\' \')[0].split(\'.\')\n' +
                        '  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 3)) {\n' +
                        '    throw new Error(\'Bootstrap\\\'s JavaScript requires at least jQuery v1.9.1 but less than v3.0.0\')\n' +
                        '  }\n' +
                        '}(jQuery);\n\n',

    // Task configuration.
    clean: {
      dist: 'dist',
      docs: 'docs/dist'
    },

    // JS build configuration
    babel: {
      dev: {
        options: {
          sourceMap: true,
          modules: 'ignore'
        },
        files: {
          'js/dist/util.js'      : 'js/src/util.js',
          'js/dist/alert.js'     : 'js/src/alert.js',
          'js/dist/button.js'    : 'js/src/button.js',
          'js/dist/carousel.js'  : 'js/src/carousel.js',
          'js/dist/collapse.js'  : 'js/src/collapse.js',
          'js/dist/dropdown.js'  : 'js/src/dropdown.js',
          'js/dist/modal.js'     : 'js/src/modal.js',
          'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
          'js/dist/tab.js'       : 'js/src/tab.js',
          'js/dist/tooltip.js'   : 'js/src/tooltip.js',
          'js/dist/popover.js'   : 'js/src/popover.js'
        }
      },
      dist: {
        options: {
          modules: 'ignore'
        },
        files: {
          '<%= concat.bootstrap.dest %>' : '<%= concat.bootstrap.dest %>'
        }
      }
    },

    stamp: {
      options: {
        banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>\n+function ($) {\n',
        footer: '\n}(jQuery);'
      },
      bootstrap: {
        files: {
          src: '<%= concat.bootstrap.dest %>'
        }
      }
    },

    concat: {
      options: {
        // Custom function to remove all export and import statements
        process: function (src) {
          return src.replace(/^(export|import).*/gm, '');
        },
        stripBanners: false
      },
      bootstrap: {
        src: [
          'js/src/util.js',
          'js/src/alert.js',
          'js/src/button.js',
          'js/src/carousel.js',
          'js/src/collapse.js',
          'js/src/dropdown.js',
          'js/src/modal.js',
          'js/src/scrollspy.js',
          'js/src/tab.js',
          'js/src/tooltip.js',
          'js/src/popover.js'
        ],
        dest: 'dist/js/<%= pkg.name %>.js'
      }
    },

    uglify: {
      options: {
        compress: {
          warnings: false
        },
        mangle: true,
        preserveComments: /^!|@preserve|@license|@cc_on/i
      },
      core: {
        src: '<%= concat.bootstrap.dest %>',
        dest: 'dist/js/<%= pkg.name %>.min.js'
      },
      docsJs: {
        src: configBridge.paths.docsJs,
        dest: 'docs/assets/js/docs.min.js'
      }
    },

    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: 'js/tests/index.html'
    },

    // CSS build configuration
    scsslint: {
      options: {
        bundleExec: true,
        config: 'scss/.scss-lint.yml',
        reporterOutput: null
      },
      core: {
        src: ['scss/*.scss', '!scss/_normalize.scss']
      },
      docs: {
        src: ['docs/assets/scss/*.scss', '!docs/assets/scss/docs.scss']
      }
    },

    cssmin: {
      options: {
        // TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
        //    and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
        compatibility: 'ie9',
        keepSpecialComments: '*',
        sourceMap: true,
        advanced: false
      },
      core: {
        files: [
          {
            expand: true,
            cwd: 'dist/css',
            src: ['*.css', '!*.min.css'],
            dest: 'dist/css',
            ext: '.min.css'
          }
        ]
      },
      docs: {
        src: 'docs/assets/css/docs.min.css',
        dest: 'docs/assets/css/docs.min.css'
      }
    },

    copy: {
      docs: {
        expand: true,
        cwd: 'dist/',
        src: [
          '**/*'
        ],
        dest: 'docs/dist/'
      }
    },

    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
        }
      }
    },

    jekyll: {
      options: {
        bundleExec: true,
        config: '_config.yml',
        incremental: false
      },
      docs: {},
      github: {
        options: {
          raw: 'github: true'
        }
      }
    },

    htmllint: {
      options: {
        ignore: [
          'Element “img” is missing required attribute “src”.',
          'Attribute “autocomplete” is only allowed when the input type is “color”, “date”, “datetime”, “datetime-local”, “email”, “month”, “number”, “password”, “range”, “search”, “tel”, “text”, “time”, “url”, or “week”.',
          'Attribute “autocomplete” not allowed on element “button” at this point.',
          'Element “div” not allowed as child of element “progress” in this context. (Suppressing further errors from this subtree.)',
          'Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).',
          'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “color” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “date” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “datetime-local” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “month” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “time” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.'
        ]
      },
      src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html']
    },

    watch: {
      src: {
        files: '<%= concat.bootstrap.src %>',
        tasks: ['babel:dev']
      },
      sass: {
        files: 'scss/**/*.scss',
        tasks: ['dist-css', 'docs']
      },
      docs: {
        files: 'docs/assets/scss/**/*.scss',
        tasks: ['dist-css', 'docs']
      }
    },

    'saucelabs-qunit': {
      all: {
        options: {
          build: process.env.TRAVIS_JOB_ID,
          concurrency: 10,
          maxRetries: 3,
          maxPollRetries: 4,
          urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
          browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
        }
      }
    },

    exec: {
      postcss: {
        command: 'npm run postcss'
      },
      'postcss-docs': {
        command: 'npm run postcss-docs'
      }
    },

    buildcontrol: {
      options: {
        dir: '_gh_pages',
        commit: true,
        push: true,
        message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
      },
      pages: {
        options: {
          remote: 'git@github.com:twbs/derpstrap.git',
          branch: 'gh-pages'
        }
      }
    },

    compress: {
      main: {
        options: {
          archive: 'bootstrap-<%= pkg.version %>-dist.zip',
          mode: 'zip',
          level: 9,
          pretty: true
        },
        files: [
          {
            expand: true,
            cwd: 'dist/',
            src: ['**'],
            dest: 'bootstrap-<%= pkg.version %>-dist'
          }
        ]
      }
    }

  });

  // These plugins provide necessary tasks.
  require('load-grunt-tasks')(grunt, { scope: 'devDependencies',
    // Exclude Sass compilers. We choose the one to load later on.
    pattern: ['grunt-*', '!grunt-sass', '!grunt-contrib-sass'] });
  require('time-grunt')(grunt);

  // Docs HTML validation task
  grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);

  var runSubset = function (subset) {
    return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
  };
  var isUndefOrNonZero = function (val) {
    return val === undefined || val !== '0';
  };

  // Test task.
  var testSubtasks = [];
  // Skip core tests if running a different subset of the test suite
  if (runSubset('core') &&
    // Skip core tests if this is a Savage build
    process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
    testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'qunit', 'docs']);
  }
  // Skip HTML validation if running a different subset of the test suite
  if (runSubset('validate-html') &&
      isTravis &&
      // Skip HTML5 validator when [skip validator] is in the commit message
      isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
    testSubtasks.push('validate-html');
  }
  // Only run Sauce Labs tests if there's a Sauce access key
  if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
      // Skip Sauce if running a different subset of the test suite
      runSubset('sauce-js-unit') &&
      // Skip Sauce on Travis when [skip sauce] is in the commit message
      isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
    testSubtasks.push('babel:dev');
    testSubtasks.push('connect');
    testSubtasks.push('saucelabs-qunit');
  }
  grunt.registerTask('test', testSubtasks);

  // JS distribution task.
  grunt.registerTask('dist-js', ['babel:dev', 'concat', 'babel:dist', 'stamp', 'uglify:core']);

  grunt.registerTask('test-scss', ['scsslint:core']);

  // CSS distribution task.
  // Supported Compilers: sass (Ruby) and libsass.
  (function (sassCompilerName) {
    require('./grunt/bs-sass-compile/' + sassCompilerName + '.js')(grunt);
  })(process.env.TWBS_SASS || 'libsass');
  // grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs']);
  grunt.registerTask('sass-compile', ['sass:core', 'sass:docs']);

  grunt.registerTask('dist-css', ['sass-compile', 'exec:postcss', 'cssmin:core', 'cssmin:docs']);

  // Full distribution task.
  grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js']);

  // Default task.
  grunt.registerTask('default', ['clean:dist', 'test']);

  // Docs task.
  grunt.registerTask('docs-css', ['cssmin:docs', 'exec:postcss-docs']);
  grunt.registerTask('lint-docs-css', ['scsslint:docs']);
  grunt.registerTask('docs-js', ['uglify:docsJs']);
  grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs']);
  grunt.registerTask('docs-github', ['jekyll:github']);

  grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);

  // Publish to GitHub
  grunt.registerTask('publish', ['buildcontrol:pages']);
};
<commit_msg>clean task  주석처리<commit_after>/*!
 * Bootstrap's Gruntfile
 * http://getbootstrap.com
 * Copyright 2013-2016 The Bootstrap Authors
 * Copyright 2013-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */

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

  // Force use of Unix newlines
  grunt.util.linefeed = '\n';

  RegExp.quote = function (string) {
    return string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  var fs = require('fs');
  var path = require('path');
  var isTravis = require('is-travis');

  var configBridge = grunt.file.readJSON('./grunt/configBridge.json', { encoding: 'utf8' });

  Object.keys(configBridge.paths).forEach(function (key) {
    configBridge.paths[key].forEach(function (val, i, arr) {
      arr[i] = path.join('./docs/assets', val);
    });
  });

  // Project configuration.
  grunt.initConfig({

    // Metadata.
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*!\n' +
            ' * Bootstrap v<%= pkg.version %> (<%= pkg.homepage %>)\n' +
            ' * Copyright 2011-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
            ' * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n' +
            ' */\n',
    jqueryCheck: 'if (typeof jQuery === \'undefined\') {\n' +
                 '  throw new Error(\'Bootstrap\\\'s JavaScript requires jQuery\')\n' +
                 '}\n',
    jqueryVersionCheck: '+function ($) {\n' +
                        '  var version = $.fn.jquery.split(\' \')[0].split(\'.\')\n' +
                        '  if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] >= 3)) {\n' +
                        '    throw new Error(\'Bootstrap\\\'s JavaScript requires at least jQuery v1.9.1 but less than v3.0.0\')\n' +
                        '  }\n' +
                        '}(jQuery);\n\n',

    // Task configuration.
    clean: {
      // dist: 'dist',
      // docs: 'docs/dist'
    },

    // JS build configuration
    babel: {
      dev: {
        options: {
          sourceMap: true,
          modules: 'ignore'
        },
        files: {
          'js/dist/util.js'      : 'js/src/util.js',
          'js/dist/alert.js'     : 'js/src/alert.js',
          'js/dist/button.js'    : 'js/src/button.js',
          'js/dist/carousel.js'  : 'js/src/carousel.js',
          'js/dist/collapse.js'  : 'js/src/collapse.js',
          'js/dist/dropdown.js'  : 'js/src/dropdown.js',
          'js/dist/modal.js'     : 'js/src/modal.js',
          'js/dist/scrollspy.js' : 'js/src/scrollspy.js',
          'js/dist/tab.js'       : 'js/src/tab.js',
          'js/dist/tooltip.js'   : 'js/src/tooltip.js',
          'js/dist/popover.js'   : 'js/src/popover.js'
        }
      },
      dist: {
        options: {
          modules: 'ignore'
        },
        files: {
          '<%= concat.bootstrap.dest %>' : '<%= concat.bootstrap.dest %>'
        }
      }
    },

    stamp: {
      options: {
        banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>\n+function ($) {\n',
        footer: '\n}(jQuery);'
      },
      bootstrap: {
        files: {
          src: '<%= concat.bootstrap.dest %>'
        }
      }
    },

    concat: {
      options: {
        // Custom function to remove all export and import statements
        process: function (src) {
          return src.replace(/^(export|import).*/gm, '');
        },
        stripBanners: false
      },
      bootstrap: {
        src: [
          'js/src/util.js',
          'js/src/alert.js',
          'js/src/button.js',
          'js/src/carousel.js',
          'js/src/collapse.js',
          'js/src/dropdown.js',
          'js/src/modal.js',
          'js/src/scrollspy.js',
          'js/src/tab.js',
          'js/src/tooltip.js',
          'js/src/popover.js'
        ],
        dest: 'dist/js/<%= pkg.name %>.js'
      }
    },

    uglify: {
      options: {
        compress: {
          warnings: false
        },
        mangle: true,
        preserveComments: /^!|@preserve|@license|@cc_on/i
      },
      core: {
        src: '<%= concat.bootstrap.dest %>',
        dest: 'dist/js/<%= pkg.name %>.min.js'
      },
      docsJs: {
        src: configBridge.paths.docsJs,
        dest: 'docs/assets/js/docs.min.js'
      }
    },

    qunit: {
      options: {
        inject: 'js/tests/unit/phantom.js'
      },
      files: 'js/tests/index.html'
    },

    // CSS build configuration
    scsslint: {
      options: {
        bundleExec: true,
        config: 'scss/.scss-lint.yml',
        reporterOutput: null
      },
      core: {
        src: ['scss/*.scss', '!scss/_normalize.scss']
      },
      docs: {
        src: ['docs/assets/scss/*.scss', '!docs/assets/scss/docs.scss']
      }
    },

    cssmin: {
      options: {
        // TODO: disable `zeroUnits` optimization once clean-css 3.2 is released
        //    and then simplify the fix for https://github.com/twbs/bootstrap/issues/14837 accordingly
        compatibility: 'ie9',
        keepSpecialComments: '*',
        sourceMap: true,
        advanced: false
      },
      core: {
        files: [
          {
            expand: true,
            cwd: 'dist/css',
            src: ['*.css', '!*.min.css'],
            dest: 'dist/css',
            ext: '.min.css'
          }
        ]
      },
      docs: {
        src: 'docs/assets/css/docs.min.css',
        dest: 'docs/assets/css/docs.min.css'
      }
    },

    copy: {
      docs: {
        expand: true,
        cwd: 'dist/',
        src: [
          '**/*'
        ],
        dest: 'docs/dist/'
      }
    },

    connect: {
      server: {
        options: {
          port: 3000,
          base: '.'
        }
      }
    },

    jekyll: {
      options: {
        bundleExec: true,
        config: '_config.yml',
        incremental: false
      },
      docs: {},
      github: {
        options: {
          raw: 'github: true'
        }
      }
    },

    htmllint: {
      options: {
        ignore: [
          'Element “img” is missing required attribute “src”.',
          'Attribute “autocomplete” is only allowed when the input type is “color”, “date”, “datetime”, “datetime-local”, “email”, “month”, “number”, “password”, “range”, “search”, “tel”, “text”, “time”, “url”, or “week”.',
          'Attribute “autocomplete” not allowed on element “button” at this point.',
          'Element “div” not allowed as child of element “progress” in this context. (Suppressing further errors from this subtree.)',
          'Consider using the “h1” element as a top-level heading only (all “h1” elements are treated as top-level headings by many screen readers and other tools).',
          'The “datetime” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “color” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “date” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “datetime-local” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “month” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “time” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.',
          'The “week” input type is not supported in all browsers. Please be sure to test, and consider using a polyfill.'
        ]
      },
      src: ['_gh_pages/**/*.html', 'js/tests/visual/*.html']
    },

    watch: {
      src: {
        files: '<%= concat.bootstrap.src %>',
        tasks: ['babel:dev']
      },
      sass: {
        files: 'scss/**/*.scss',
        tasks: ['dist-css', 'docs']
      },
      docs: {
        files: 'docs/assets/scss/**/*.scss',
        tasks: ['dist-css', 'docs']
      }
    },

    'saucelabs-qunit': {
      all: {
        options: {
          build: process.env.TRAVIS_JOB_ID,
          concurrency: 10,
          maxRetries: 3,
          maxPollRetries: 4,
          urls: ['http://127.0.0.1:3000/js/tests/index.html?hidepassed'],
          browsers: grunt.file.readYAML('grunt/sauce_browsers.yml')
        }
      }
    },

    exec: {
      postcss: {
        command: 'npm run postcss'
      },
      'postcss-docs': {
        command: 'npm run postcss-docs'
      }
    },

    buildcontrol: {
      options: {
        dir: '_gh_pages',
        commit: true,
        push: true,
        message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%'
      },
      pages: {
        options: {
          remote: 'git@github.com:twbs/derpstrap.git',
          branch: 'gh-pages'
        }
      }
    },

    compress: {
      main: {
        options: {
          archive: 'bootstrap-<%= pkg.version %>-dist.zip',
          mode: 'zip',
          level: 9,
          pretty: true
        },
        files: [
          {
            expand: true,
            cwd: 'dist/',
            src: ['**'],
            dest: 'bootstrap-<%= pkg.version %>-dist'
          }
        ]
      }
    }

  });

  // These plugins provide necessary tasks.
  require('load-grunt-tasks')(grunt, { scope: 'devDependencies',
    // Exclude Sass compilers. We choose the one to load later on.
    pattern: ['grunt-*', '!grunt-sass', '!grunt-contrib-sass'] });
  require('time-grunt')(grunt);

  // Docs HTML validation task
  grunt.registerTask('validate-html', ['jekyll:docs', 'htmllint']);

  var runSubset = function (subset) {
    return !process.env.TWBS_TEST || process.env.TWBS_TEST === subset;
  };
  var isUndefOrNonZero = function (val) {
    return val === undefined || val !== '0';
  };

  // Test task.
  var testSubtasks = [];
  // Skip core tests if running a different subset of the test suite
  if (runSubset('core') &&
    // Skip core tests if this is a Savage build
    process.env.TRAVIS_REPO_SLUG !== 'twbs-savage/bootstrap') {
    testSubtasks = testSubtasks.concat(['dist-css', 'dist-js', 'test-scss', 'qunit', 'docs']);
  }
  // Skip HTML validation if running a different subset of the test suite
  if (runSubset('validate-html') &&
      isTravis &&
      // Skip HTML5 validator when [skip validator] is in the commit message
      isUndefOrNonZero(process.env.TWBS_DO_VALIDATOR)) {
    testSubtasks.push('validate-html');
  }
  // Only run Sauce Labs tests if there's a Sauce access key
  if (typeof process.env.SAUCE_ACCESS_KEY !== 'undefined' &&
      // Skip Sauce if running a different subset of the test suite
      runSubset('sauce-js-unit') &&
      // Skip Sauce on Travis when [skip sauce] is in the commit message
      isUndefOrNonZero(process.env.TWBS_DO_SAUCE)) {
    testSubtasks.push('babel:dev');
    testSubtasks.push('connect');
    testSubtasks.push('saucelabs-qunit');
  }
  grunt.registerTask('test', testSubtasks);

  // JS distribution task.
  grunt.registerTask('dist-js', ['babel:dev', 'concat', 'babel:dist', 'stamp', 'uglify:core']);

  grunt.registerTask('test-scss', ['scsslint:core']);

  // CSS distribution task.
  // Supported Compilers: sass (Ruby) and libsass.
  (function (sassCompilerName) {
    require('./grunt/bs-sass-compile/' + sassCompilerName + '.js')(grunt);
  })(process.env.TWBS_SASS || 'libsass');
  // grunt.registerTask('sass-compile', ['sass:core', 'sass:extras', 'sass:docs']);
  grunt.registerTask('sass-compile', ['sass:core', 'sass:docs']);

  grunt.registerTask('dist-css', ['sass-compile', 'exec:postcss', 'cssmin:core', 'cssmin:docs']);

  // Full distribution task.
  grunt.registerTask('dist', ['clean:dist', 'dist-css', 'dist-js']);

  // Default task.
  grunt.registerTask('default', ['clean:dist', 'test']);

  // Docs task.
  grunt.registerTask('docs-css', ['cssmin:docs', 'exec:postcss-docs']);
  grunt.registerTask('lint-docs-css', ['scsslint:docs']);
  grunt.registerTask('docs-js', ['uglify:docsJs']);
  grunt.registerTask('docs', ['lint-docs-css', 'docs-css', 'docs-js', 'clean:docs', 'copy:docs']);
  grunt.registerTask('docs-github', ['jekyll:github']);

  grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);

  // Publish to GitHub
  grunt.registerTask('publish', ['buildcontrol:pages']);
};
<|endoftext|>


<commit_before>// Generated on 2013-10-21 using generator-webapp 0.4.3
'use strict';

// # 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) {
  // Show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // Project configuration.
  grunt.initConfig({
    // Metadata
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed MIT */\n',
    // configurable paths
    yeoman: {
      bower: 'bower_components',
      src: 'src',
      test: 'spec',
      out: '.tmp',
      dist: 'dist',
      site: 'site'
    },
    coffee: {
      lib: {
        expand: true,
        cwd: '<%= yeoman.src %>/javascripts/',
        src: ['**/*.coffee'],
        dest: '<%= yeoman.out %>/javascripts/',
        ext: '.js'
      },
      test: {
        expand: true,
        cwd: 'spec',
        src: ['**/*.coffee'],
        dest: '<%= yeoman.out %>/spec/',
        ext: '.js'
      }
    },

    sass: {
      dist: {
        files: {
          '<%= yeoman.out %>/ttm-coffeescript-utilities.css': 'src/stylesheets/browser.scss'
        }
      }
    },

    concat: {
      options: {
        banner: '<%= banner %>',
        stripBanners: true,
      },
      dist: {
        src: [
          '<%= yeoman.out %>/**/*.js'
        ],
        dest: '<%= yeoman.dist %>/<%= pkg.name %>.js'
      }
    },

    // Put files not handled in other tasks here
    copy: {
      out: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.src %>',
          dest: '<%= yeoman.out %>',
          src: [
            '**/*.js',
          ]
        }]
      },
      spec: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.test %>',
          dest: '<%= yeoman.out %>/spec/',
          src: [
            '**/*.js',
          ]
        }]
      },
      styles: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.out %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '**/*.css',
          ]
        }]
      }
    },

    browserify: {
      dist: {
        src: '<%= yeoman.out %>/javascripts/browser.js',
        dest: '<%= yeoman.dist %>/<%= pkg.name %>.js'
      }
    },
    clean: {
      options: {
        // "no-write": true
      },
      all: {
        files: [{
          dot: true,
          src: [
            '<%= yeoman.out %>',
            '<%= yeoman.dist %>'
          ]
        }]
      }
    },
    jasmine: {
      specs: [
        '<%= yeoman.bower %>/jquery/jquery.js',
        '<%= yeoman.bower %>/underscore/underscore.js',
        '<%= yeoman.out %>/spec/support/jasmine-jquery.js',
        '<%= yeoman.dist %>/<%= pkg.name %>.js',
        '<%= yeoman.out %>/spec/support/spec_helpers.js',
        '<%= yeoman.out %>/spec/lib_spec.js',
        '<%= yeoman.out %>/spec/lib/**/*.js',
        '<%= yeoman.out %>/spec/math/**/*.js',
        '<%= yeoman.out %>/spec/widgets/**/*.js'
      ]
    },
    connect: {
      options: {
        port: 9000,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      serve: {
        options: {
          open: true,
          base: [
            '<%= yeoman.site %>',
            '<%= yeoman.dist %>',
            '<%= yeoman.bower %>'
          ]
        }
      }
    },

    watch: {
      coffee: {
        files: [
          '<%= yeoman.src %>/**/*.{coffee,scss}',
          '<%= yeoman.test %>/**/*.coffee',
        ],
        tasks: ['build'],
        options: {
          interrupt: false
        }
      }
    },

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

    uglify: {
      options: {
        banner: '<%= banner %>'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: '<%= yeoman.dist %>/<%= pkg.name %>.min.js'
      }
    },
    'bower-install': {
      app: {
        html: '<%= yeoman.src %>/index.html',
        ignorePath: '<%= yeoman.src %>/'
      }
    },
    rev: {
      dist: {
        files: {
          src: [
            '<%= yeoman.dist %>/scripts/{,*/}*.js',
            '<%= yeoman.dist %>/styles/{,*/}*.css',
            '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}',
            '<%= yeoman.dist %>/styles/fonts/{,*/}*.*'
          ]
        }
      }
    },
    useminPrepare: {
      options: {
        dest: '<%= yeoman.dist %>'
      },
      html: '<%= yeoman.src %>/index.html'
    },
    usemin: {
      options: {
        dirs: ['<%= yeoman.dist %>']
      },
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
    },
    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.src %>/images',
          src: '{,*/}*.{png,jpg,jpeg}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.src %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    cssmin: {
      // This task is pre-configured if you do not wish to use Usemin
      // blocks for your CSS. By default, the Usemin block from your
      // `index.html` will take care of minification, e.g.

      //     <!-- build:css({.tmp,app}) styles/main.css -->

      dist: {
        files: {
          '<%= yeoman.dist %>/styles/main.css': [
            '.tmp/styles/{,*/}*.css',
            '<%= yeoman.src %>/styles/{,*/}*.css'
          ]
        }
      }
    },
    htmlmin: {
      dist: {
        options: {
          /*removeCommentsFromCDATA: true,
          // https://github.com/yeoman/grunt-usemin/issues/44
          //collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeAttributeQuotes: true,
          removeRedundantAttributes: true,
          useShortDoctype: true,
          removeEmptyAttributes: true,
          removeOptionalTags: true*/
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.src %>',
          src: '*.html',
          dest: '<%= yeoman.dist %>'
        }]
      }
    },
    // Put files not handled in other tasks here
    // copy: {
    //   dist: {
    //     files: [{
    //       expand: true,
    //       dot: true,
    //       cwd: '<%= yeoman.src %>',
    //       dest: '<%= yeoman.dist %>',
    //       src: [
    //         '*.{ico,png,txt}',
    //         '.htaccess',
    //         'images/{,*/}*.{webp,gif}',
    //         'styles/fonts/{,*/}*.*'
    //       ]
    //     }]
    //   },
    //   styles: {
    //     expand: true,
    //     dot: true,
    //     cwd: '<%= yeoman.src %>/styles',
    //     dest: '.tmp/styles/',
    //     src: '{,*/}*.css'
    //   }
    // },
    concurrent: {
      server: [
        'compass',
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'compass',
        'copy:styles',
        'imagemin',
        'svgmin',
        'htmlmin'
      ]
    }

  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // grunt.loadNpmTasks('grunt-contrib-qunit');
  // grunt.loadNpmTasks('grunt-contrib-jshint');
  // grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('watch-serve', [
    'connect:serve', 'watch'
  ]);

  grunt.registerTask('build', [
    'clean',
    'coffee',
    'sass',
    'concat',
    'copy:out',
    'copy:spec',
    'browserify',
    'copy:styles',
  ]);

  grunt.registerTask('test', [
    'build',
    'jasmine'
  ]);

  grunt.registerTask('serve', ['build', 'connect:serve:keepalive']);

  grunt.registerTask('default', ['build']);

};
<commit_msg>removing unused code in gruntfile<commit_after>// Generated on 2013-10-21 using generator-webapp 0.4.3
'use strict';

// # 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) {
  // Show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // Project configuration.
  grunt.initConfig({
    // Metadata
    pkg: grunt.file.readJSON('package.json'),
    banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
      '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
      '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
      '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
      ' Licensed MIT */\n',
    // configurable paths
    yeoman: {
      bower: 'bower_components',
      src: 'src',
      test: 'spec',
      out: '.tmp',
      dist: 'dist',
      site: 'site'
    },
    coffee: {
      lib: {
        expand: true,
        cwd: '<%= yeoman.src %>/javascripts/',
        src: ['**/*.coffee'],
        dest: '<%= yeoman.out %>/javascripts/',
        ext: '.js'
      },
      test: {
        expand: true,
        cwd: 'spec',
        src: ['**/*.coffee'],
        dest: '<%= yeoman.out %>/spec/',
        ext: '.js'
      }
    },

    sass: {
      dist: {
        files: {
          '<%= yeoman.out %>/ttm-coffeescript-utilities.css': 'src/stylesheets/browser.scss'
        }
      }
    },

    concat: {
      options: {
        banner: '<%= banner %>',
        stripBanners: true,
      },
      dist: {
        src: [
          '<%= yeoman.out %>/**/*.js'
        ],
        dest: '<%= yeoman.dist %>/<%= pkg.name %>.js'
      }
    },

    // Put files not handled in other tasks here
    copy: {
      out: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.src %>',
          dest: '<%= yeoman.out %>',
          src: [
            '**/*.js',
          ]
        }]
      },
      spec: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.test %>',
          dest: '<%= yeoman.out %>/spec/',
          src: [
            '**/*.js',
          ]
        }]
      },
      styles: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.out %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '**/*.css',
          ]
        }]
      }
    },

    browserify: {
      dist: {
        src: '<%= yeoman.out %>/javascripts/browser.js',
        dest: '<%= yeoman.dist %>/<%= pkg.name %>.js'
      }
    },
    clean: {
      options: {
        // "no-write": true
      },
      all: {
        files: [{
          dot: true,
          src: [
            '<%= yeoman.out %>',
            '<%= yeoman.dist %>'
          ]
        }]
      }
    },
    jasmine: {
      specs: [
        '<%= yeoman.bower %>/jquery/jquery.js',
        '<%= yeoman.bower %>/underscore/underscore.js',
        '<%= yeoman.out %>/spec/support/jasmine-jquery.js',
        '<%= yeoman.dist %>/<%= pkg.name %>.js',
        '<%= yeoman.out %>/spec/support/spec_helpers.js',
        '<%= yeoman.out %>/spec/lib_spec.js',
        '<%= yeoman.out %>/spec/lib/**/*.js',
        '<%= yeoman.out %>/spec/math/**/*.js',
        '<%= yeoman.out %>/spec/widgets/**/*.js'
      ]
    },
    connect: {
      options: {
        port: 9000,
        // change this to '0.0.0.0' to access the server from outside
        hostname: 'localhost'
      },
      serve: {
        options: {
          open: true,
          base: [
            '<%= yeoman.site %>',
            '<%= yeoman.dist %>',
            '<%= yeoman.bower %>'
          ]
        }
      }
    },

    watch: {
      coffee: {
        files: [
          '<%= yeoman.src %>/**/*.{coffee,scss}',
          '<%= yeoman.test %>/**/*.coffee',
        ],
        tasks: ['build'],
        options: {
          interrupt: false
        }
      }
    },

  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-jasmine');
  grunt.loadNpmTasks('grunt-browserify');
  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // grunt.loadNpmTasks('grunt-contrib-qunit');
  // grunt.loadNpmTasks('grunt-contrib-jshint');
  // grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('watch-serve', [
    'connect:serve', 'watch'
  ]);

  grunt.registerTask('build', [
    'clean',
    'coffee',
    'sass',
    'concat',
    'copy:out',
    'copy:spec',
    'browserify',
    'copy:styles',
  ]);

  grunt.registerTask('test', [
    'build',
    'jasmine'
  ]);

  grunt.registerTask('serve', ['build', 'connect:serve:keepalive']);

  grunt.registerTask('default', ['build']);

};
<|endoftext|>


<commit_before>'use strict'
const AWS = require('aws-sdk');

class SQS {
	getQueueUrl(opts, cb) {
		console.log('FIXTURE :: executing function getQueueUrl');
		console.log('FIXTURE :: printing arguments', opts);
		if(opts['QueueName'] !== 'unknownQueue'){
			cb(undefined, {QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/12345678912/somequeue'})
		}
		cb(undefined, {QueueUrl: ''})

	}
	sendMessage(opts, cb) {
		console.log('executing function getQueueUrl')
		cb(undefined, {MessageId: '123456789'});
	}
}
const sqs = new SQS();

AWS.SQS = function () {
	return sqs;
};

module.exports = sqs;<commit_msg>fixed typo<commit_after>'use strict'
const AWS = require('aws-sdk');

class SQS {
	getQueueUrl(opts, cb) {
		console.log('FIXTURE :: executing function getQueueUrl');
		console.log('FIXTURE :: printing arguments', opts);
		if(opts['QueueName'] !== 'unknownQueue'){
			cb(undefined, {QueueUrl: 'https://sqs.eu-west-1.amazonaws.com/12345678912/somequeue'})
		}
		cb(undefined, {QueueUrl: ''})

	}
	sendMessage(opts, cb) {
		console.log('FIXTURE :: executing function getQueueUrl')
		cb(undefined, {MessageId: '123456789'});
	}
}
const sqs = new SQS();

AWS.SQS = function () {
	return sqs;
};

module.exports = sqs;<|endoftext|>


<commit_before>import React from "react";
import PropTypes from 'prop-types';
import customFetch from "../util/custom-fetch";
import GeoAreaAutosuggest from "./autosuggests/geoarea-autosuggest";
import LabelSection from "./label-section";
import PillInputButton from './pill-input-button';
import UserService from '../util/user-service';

import { i18n } from "../config/i18n-config"
import { t } from "@lingui/macro"

export default class SearchAppsForm extends React.Component {

    state = {
        languages: [],
        selectedLanguage: '',
        selectedOrganizationId: '',
        installStatus: '',
        geoArea: '',
        organizations: null,
        payment: {
            free: false,
            paid: false
        },
        audience: {
            publicbodies: false,
            citizens: false,
            companies: false

        }

    };

    constructor(props){
        super(props);
        this._userService = new UserService();

    }

    componentDidMount = async () =>  {
        this._initializeFilters(this.props.filters);
        await this.initializeLanguage();

        //organization input is just available when user is connected
        const userInfo = await this._userService.fetchUserInfos();
        if(userInfo){
            customFetch("/my/api/organization").then((organizations) => {
                organizations.unshift({name:i18n._(t`store.language.all`), id: ''});
                this.setState({organizations: organizations});
            });
        }
    };

    componentWillReceiveProps(nextProps){
        const {filters} =  nextProps;

        if(filters){
            this._initializeFilters(filters);
        }
    }

    _initializeFilters = (filters) => {
        let {audience, payment, geoArea,selectedOrganizationId, selectedLanguage} = this.state;

            for(let key in audience){
                audience[key] = filters.audience[key];
            }
            for(let key in payment){
                payment[key] = filters.payment[key];
            }
            selectedOrganizationId = filters.selectedOrganizationId;
            geoArea = filters.geoArea;
            selectedLanguage = filters.selectedLanguage;

            this.setState({
                audience,
                payment,
                selectedOrganizationId,
                geoArea,
                selectedLanguage
            });
    };

    initializeLanguage = async () => {
        const {config} = this.props;
        let languages = Object.assign([], config.languages);
        languages.unshift('all');
        if(this.props.filters.selectedLanguage === '') {
            this.setState({languages: languages, selectedLanguage: config.language});
            this.props.updateFilter(null, "selectedLanguage", config.language);
        }else{
            this.setState({languages: languages});
        }
    };

    resetFilters = () => {
        this.setState({
            selectedLanguage: '',
            selectedOrganizationId: '',
            geoArea: '',
            payment: {
                free: false,
                paid: false
            },
            audience: {
                publicbodies: false,
                citizens: false,
                companies: false

            }});
    };

    _handleLanguageClicked = (event) => {
        const selectedLanguage = event.target.value;
        this.setState({selectedLanguage: selectedLanguage});
        this.props.updateFilter(null, "selectedLanguage", selectedLanguage);
    };

    _handleGeoSelected = (event, value) => {
        this.props.updateFilter(null, "geoArea", value);
    };

    _handleGeoChange = (event, value) => {
        let {geoArea} = this.state;
        geoArea.name = value;
        this.setState({geoArea});
        if(value===''){
            this.props.updateFilter(null, "geoArea",  {name:'', ancestors: []});
        }
    };

    _handleOrganizationChange = (event) => {
        const selectedOrganizationId = event.target.value;
        this.setState({selectedOrganizationId: selectedOrganizationId});
        this.props.updateFilter(null, "selectedOrganizationId", selectedOrganizationId);
    };

    _handleInstallStatusChange = (event) => {
        const installStatus = event.target.value;
        this.setState({installStatus: installStatus});
        this.props.updateFilter(null, "installStatus", installStatus);
    };

    _handleOnPaymentChange = (event) => {
        const inputModified = event.target.name;
        let {payment} = this.state;
        payment[inputModified] = event.target.checked;
        this.setState({payment: payment});

        this.props.updateFilter("payment", inputModified, event.target.checked);
    };
    _handleAudienceChange = (event) => {
        const inputModified = event.target.name;
        let {audience} = this.state;
        audience[inputModified] = event.target.checked;
        this.setState({audience: audience});

        this.props.updateFilter("audience", inputModified, event.target.checked);
    };

    render() {
        const {languages, selectedLanguage, payment, audience, selectedOrganizationId, organizations, installStatus} = this.state;
        const languageComponents = languages.map(language =>
            <option key={language} value={language}>{i18n._(`store.language.${language}`)}</option>
        );
        const organizationComponents = organizations ? organizations.map(organization =>
            <option key={organization.id} value={organization.id}>{organization.name}</option>
        ) : null;

        return (

                {/*LANGUAGE*/}
                <LabelSection label={i18n._('store.languages-supported-by-applications')}>
                    <select id="language" className="form-control"
                            onChange={this._handleLanguageClicked}
                            value={selectedLanguage}>
                        {languageComponents}
                    </select>
                </LabelSection>
                {/*GEOAREA*/}
                <LabelSection label={i18n._('store.geoarea')}>
                    <GeoAreaAutosuggest name="geoSearch"
                                        countryUri=""
                                        endpoint="areas"
                                        onChange={this._handleGeoChange}
                                        onGeoAreaSelected={this._handleGeoSelected}
                                        value={this.state.geoArea.name}
                    />
                </LabelSection>
                {/*ORGANIZATION*/}
                {organizations &&
                    <LabelSection label={i18n._('organization.search.title')}>
                        <select id="organization" className="form-control"
                                onChange={this._handleOrganizationChange}
                                value={selectedOrganizationId}>
                            {organizationComponents}
                        </select>
                    </LabelSection>
                }
                {/*INSTALLED*/}
                {selectedOrganizationId &&
                    <LabelSection label={i18n._('my.apps.status')}>
                        <select id="installed" className="form-control"
                                onChange={this._handleInstallStatusChange}
                                value={installStatus}>
                            <option value={''}>{i18n._('all')}</option>
                            <option value={"installed"}>{i18n._('installed')}</option>
                            <option value={"not_installed"}>{i18n._('not-installed')}</option>
                        </select>
                    </LabelSection>
                }
                {/*MODE*/}
                <LabelSection label={i18n._('store.mode')}>
                    <PillInputButton label={i18n._('store.free')} id={"free-checkbox"}
                                checked={payment.free} name={'free'} onChange={this._handleOnPaymentChange}/>

                    <PillInputButton label={i18n._('store.paid')} id={"paid-checkbox"}
                                checked={payment.paid} name={'paid'} onChange={this._handleOnPaymentChange}/>

                </LabelSection>
                {/*AUDIENCE*/}
                <LabelSection label={i18n._('store.audience')}>
                    <PillInputButton label={i18n._('store.citizens')} id={"citizens-checkbox"}
                                checked={audience.citizens} name={'citizens'} onChange={this._handleAudienceChange}/>

                    <PillInputButton label={i18n._('store.publicbodies')} id={"publicbodies-checkbox"}
                                checked={audience.publicbodies} name={'publicbodies'} onChange={this._handleAudienceChange}/>

                    <PillInputButton label={i18n._('store.companies')} id={"companies-checkbox"}
                                checked={audience.companies} name={'companies'} onChange={this._handleAudienceChange}/>
                </LabelSection>

        )
    }

}

SearchAppsForm.propTypes = {};
<commit_msg>improvement: check the response is an object (#38)<commit_after>import React from "react";
import PropTypes from 'prop-types';
import customFetch from "../util/custom-fetch";
import GeoAreaAutosuggest from "./autosuggests/geoarea-autosuggest";
import LabelSection from "./label-section";
import PillInputButton from './pill-input-button';
import UserService from '../util/user-service';

import { i18n } from "../config/i18n-config"
import { t } from "@lingui/macro"

export default class SearchAppsForm extends React.Component {

    state = {
        languages: [],
        selectedLanguage: '',
        selectedOrganizationId: '',
        installStatus: '',
        geoArea: '',
        organizations: null,
        payment: {
            free: false,
            paid: false
        },
        audience: {
            publicbodies: false,
            citizens: false,
            companies: false

        }

    };

    constructor(props){
        super(props);
        this._userService = new UserService();

    }

    componentDidMount = async () =>  {
        this._initializeFilters(this.props.filters);
        await this.initializeLanguage();

        //organization input is just available when user is connected
        const userInfo = await this._userService.fetchUserInfos();
        if(userInfo && userInfo.name){
            customFetch("/my/api/organization").then((organizations) => {
                organizations.unshift({name:i18n._(t`store.language.all`), id: ''});
                this.setState({organizations: organizations});
            });
        }
    };

    componentWillReceiveProps(nextProps){
        const {filters} =  nextProps;

        if(filters){
            this._initializeFilters(filters);
        }
    }

    _initializeFilters = (filters) => {
        let {audience, payment, geoArea,selectedOrganizationId, selectedLanguage} = this.state;

            for(let key in audience){
                audience[key] = filters.audience[key];
            }
            for(let key in payment){
                payment[key] = filters.payment[key];
            }
            selectedOrganizationId = filters.selectedOrganizationId;
            geoArea = filters.geoArea;
            selectedLanguage = filters.selectedLanguage;

            this.setState({
                audience,
                payment,
                selectedOrganizationId,
                geoArea,
                selectedLanguage
            });
    };

    initializeLanguage = async () => {
        const {config} = this.props;
        let languages = Object.assign([], config.languages);
        languages.unshift('all');
        if(this.props.filters.selectedLanguage === '') {
            this.setState({languages: languages, selectedLanguage: config.language});
            this.props.updateFilter(null, "selectedLanguage", config.language);
        }else{
            this.setState({languages: languages});
        }
    };

    resetFilters = () => {
        this.setState({
            selectedLanguage: '',
            selectedOrganizationId: '',
            geoArea: '',
            payment: {
                free: false,
                paid: false
            },
            audience: {
                publicbodies: false,
                citizens: false,
                companies: false

            }});
    };

    _handleLanguageClicked = (event) => {
        const selectedLanguage = event.target.value;
        this.setState({selectedLanguage: selectedLanguage});
        this.props.updateFilter(null, "selectedLanguage", selectedLanguage);
    };

    _handleGeoSelected = (event, value) => {
        this.props.updateFilter(null, "geoArea", value);
    };

    _handleGeoChange = (event, value) => {
        let {geoArea} = this.state;
        geoArea.name = value;
        this.setState({geoArea});
        if(value===''){
            this.props.updateFilter(null, "geoArea",  {name:'', ancestors: []});
        }
    };

    _handleOrganizationChange = (event) => {
        const selectedOrganizationId = event.target.value;
        this.setState({selectedOrganizationId: selectedOrganizationId});
        this.props.updateFilter(null, "selectedOrganizationId", selectedOrganizationId);
    };

    _handleInstallStatusChange = (event) => {
        const installStatus = event.target.value;
        this.setState({installStatus: installStatus});
        this.props.updateFilter(null, "installStatus", installStatus);
    };

    _handleOnPaymentChange = (event) => {
        const inputModified = event.target.name;
        let {payment} = this.state;
        payment[inputModified] = event.target.checked;
        this.setState({payment: payment});

        this.props.updateFilter("payment", inputModified, event.target.checked);
    };
    _handleAudienceChange = (event) => {
        const inputModified = event.target.name;
        let {audience} = this.state;
        audience[inputModified] = event.target.checked;
        this.setState({audience: audience});

        this.props.updateFilter("audience", inputModified, event.target.checked);
    };

    render() {
        const {languages, selectedLanguage, payment, audience, selectedOrganizationId, organizations, installStatus} = this.state;
        const languageComponents = languages.map(language =>
            <option key={language} value={language}>{i18n._(`store.language.${language}`)}</option>
        );
        const organizationComponents = organizations ? organizations.map(organization =>
            <option key={organization.id} value={organization.id}>{organization.name}</option>
        ) : null;

        return (

                {/*LANGUAGE*/}
                <LabelSection label={i18n._('store.languages-supported-by-applications')}>
                    <select id="language" className="form-control"
                            onChange={this._handleLanguageClicked}
                            value={selectedLanguage}>
                        {languageComponents}
                    </select>
                </LabelSection>
                {/*GEOAREA*/}
                <LabelSection label={i18n._('store.geoarea')}>
                    <GeoAreaAutosuggest name="geoSearch"
                                        countryUri=""
                                        endpoint="areas"
                                        onChange={this._handleGeoChange}
                                        onGeoAreaSelected={this._handleGeoSelected}
                                        value={this.state.geoArea.name}
                    />
                </LabelSection>
                {/*ORGANIZATION*/}
                {organizations &&
                    <LabelSection label={i18n._('organization.search.title')}>
                        <select id="organization" className="form-control"
                                onChange={this._handleOrganizationChange}
                                value={selectedOrganizationId}>
                            {organizationComponents}
                        </select>
                    </LabelSection>
                }
                {/*INSTALLED*/}
                {selectedOrganizationId &&
                    <LabelSection label={i18n._('my.apps.status')}>
                        <select id="installed" className="form-control"
                                onChange={this._handleInstallStatusChange}
                                value={installStatus}>
                            <option value={''}>{i18n._('all')}</option>
                            <option value={"installed"}>{i18n._('installed')}</option>
                            <option value={"not_installed"}>{i18n._('not-installed')}</option>
                        </select>
                    </LabelSection>
                }
                {/*MODE*/}
                <LabelSection label={i18n._('store.mode')}>
                    <PillInputButton label={i18n._('store.free')} id={"free-checkbox"}
                                checked={payment.free} name={'free'} onChange={this._handleOnPaymentChange}/>

                    <PillInputButton label={i18n._('store.paid')} id={"paid-checkbox"}
                                checked={payment.paid} name={'paid'} onChange={this._handleOnPaymentChange}/>

                </LabelSection>
                {/*AUDIENCE*/}
                <LabelSection label={i18n._('store.audience')}>
                    <PillInputButton label={i18n._('store.citizens')} id={"citizens-checkbox"}
                                checked={audience.citizens} name={'citizens'} onChange={this._handleAudienceChange}/>

                    <PillInputButton label={i18n._('store.publicbodies')} id={"publicbodies-checkbox"}
                                checked={audience.publicbodies} name={'publicbodies'} onChange={this._handleAudienceChange}/>

                    <PillInputButton label={i18n._('store.companies')} id={"companies-checkbox"}
                                checked={audience.companies} name={'companies'} onChange={this._handleAudienceChange}/>
                </LabelSection>

        )
    }

}

SearchAppsForm.propTypes = {};
<|endoftext|>


<commit_before>/*
 * This file implements several helpers for fixing up full-page web apps on touchscreen
 * browsers:
 *
 * AndroidDetectWindowSize
 * EmulateDoubleClick
 * ResponsiveSingleClick
 * PreventDragScrolling
 *
 * Most of these are annoying hacks to work around the fact that WebKit on browsers was
 * designed with displaying scrolling webpages in mind, apparently without consideration
 * for full-screen applications: pages that should fill the screen at all times.  Most
 * of the browser mobile hacks no longer make sense: separate display viewports, touch
 * dragging, double-click zooming and their associated side-effects.
 */

/*
 * AndroidDetectWindowSize
 *
 * Implementing a full-page web app for Android is hard, because if you set the page to
 * "width: 100%; height: 100%;" it'll eat a big chunk of the screen with the address bar
 * which can't be scrolled off in that configuration.  We have to play games to figure out
 * the real size of the window, and set the body size to it explicitly.  This handler does
 * the following:
 *
 * - capture resize events
 * - cancel the resize event; we'll fire it again when we're done
 * - enable a large padding div, to ensure that we can scroll the window downward
 * - switch the body to overflow: auto
 * - window.scrollTo(0, 1) to scroll the address bar off screen, which increases the window
 *   size to the maximum
 * - wait a little while; unbelievably, window.scrollTo on Android animates rather than
 *   snapping to the position as all sane browsers do
 * - set the body to the size of the window
 * - hide the padding div and set the body back to overflow: hidden
 * - synthesize a new resize event to continue other event handlers that we originally cancelled
 *
 * resize will always be fired at least once as a result of constructing this class.
 *
 * This is only used on Android.
 */

function AndroidDetectWindowSize()
{
  /* This is shown to make sure we can scroll the address bar off. */
  this.padding = document.createElement("DIV");
  this.padding.setStyle({width: "5000px", height: "5000px"});
  this.padding.hide();
  document.documentElement.appendChild(this.padding);

  this.window_size = [0, 0];
  this.finish = this.finish.bind(this);
  this.event_onresize = this.event_onresize.bindAsEventListener(this);

  this.finish_timer = null;
  this.active = false;

  /* This will run a detection cycle, which will fire resize and then leave us capturing resize. */
  this.begin();
}

/* Return true if Android resize handling is needed. */
AndroidDetectWindowSize.required = function()
{
  // XXX: be more specific
  return navigator.userAgent.indexOf("Android") != -1;
}

/* Dispatch a resize event that we won't intercept, and which will continue on to regular
 * listeners.  This is used after we've completed processing. */
AndroidDetectWindowSize.prototype.dispatch_resize_event = function()
{
  debug.log("dispatch");
  var e = document.createEvent("Event");
  e.from_window_size_detection = true;
  e.initEvent("resize", true, true);
  document.documentElement.dispatchEvent(e);
}

AndroidDetectWindowSize.prototype.begin = function()
{
  if(this.active)
    return;

  var initial_window_size = this.current_window_size();
  if(this.window_size && initial_window_size[0] == this.window_size[0] && initial_window_size[1] == this.window_size[1])
  {
    debug.log("skipped");
    return;
  }

  debug.log("begin");
  document.body.setStyle({overflow: "auto"});
  this.window_size = initial_window_size;
  this.padding.show();
  this.active = true;
  window.removeEventListener("resize", this.event_onresize, true);

  if(this.finish_timer != null)
    this.finish_timer = window.clearTimeout(this.finish_timer);
  this.finish_timer = window.setTimeout(this.finish, 250);

  window.scrollTo(0, 1);
}

AndroidDetectWindowSize.prototype.end = function()
{
  if(!this.active)
    return;
  this.active = false;

  this.padding.hide();
  document.body.setStyle({overflow: "hidden"});
  window.addEventListener("resize", this.event_onresize, true);
}

AndroidDetectWindowSize.prototype.current_window_size = function()
{
  return [window.innerWidth, window.innerHeight];
}

AndroidDetectWindowSize.prototype.finish = function()
{
  if(!this.active)
    return;
  debug.log("finish()");
  this.end();

  this.window_size[0] = Math.max(this.window_size[0], window.innerWidth);
  this.window_size[1] = Math.max(this.window_size[1], window.innerHeight);

  /* If the orientation's been changed, start over. */
  var was_landscape = this.window_size[0] > this.window_size[1];
  var is_landscape = window.innerWidth > window.innerHeight;
  if(was_landscape != is_landscape)
  {
    debug.log("restart: orientation changed");
    this.end();
    this.begin();
    return;
  }

  // We need to fudge the height up a pixel, or in many cases we'll end up with a white line
  // at the bottom of the screen.  This seems to be sub-pixel rounding error.
  debug.log("new window size: " + this.window_size[0] + "x" + this.window_size[1]);
  document.body.setStyle({width: this.window_size[0] + "px", height: (this.window_size[1]+1) + "px"});

  this.dispatch_resize_event();
}

AndroidDetectWindowSize.prototype.event_onresize = function(e)
{
  /* Ignore events that we generated ourselves after completion. */
  if(e.from_window_size_detection)
  {
    debug.log("ignored");
    return;
  }

  debug.log("stopping resize event");
  e.stopPropagation();

  /* A resize event starts a new detection cycle, if we're not already in one. */
  if(!this.active)
  {
    debug.log("resize");
    this.begin();
  }
}

/*
 * Work around a bug on many touchscreen browsers: even when the page isn't
 * zoomable, dblclick is never fired.  We have to emulate it.
 *
 * This isn't an exact emulation of the event behavior:
 *
 * - It triggers from touchstart rather than mousedown.  The second mousedown
 *   of a double click isn't being fired reliably in Android's WebKit.
 *
 * - preventDefault on the triggering event should prevent a dblclick, but
 *   we can't find out if it's been called; there's nothing like Firefox's
 *   getPreventDefault.  We could mostly emulate this by overriding
 *   Event.preventDefault to set a flag that we can read.
 *
 * - The conditions for a double click won't match the ones of the platform.
 *
 * This is needed on Android and iPhone's WebKit.
 */

function EmulateDoubleClick()
{
  this.touchstart_event = this.touchstart_event.bindAsEventListener(this);
  this.last_click_event = null;

  window.addEventListener("touchstart", this.touchstart_event, false);
}

EmulateDoubleClick.prototype.touchstart_event = function(event)
{
  var this_click = event;
  var last_click  = this.last_click_event;

  this.last_click_event = this_click;
  if(last_click == null)
      return;

  /* Check that not too much time has passed. */
  var time_since_previous = this_click.timeStamp - last_click.timeStamp;
  if(time_since_previous > 500)
    return;

  /* Check that the clicks aren't too far apart. */
  var distance = Math.pow(this_click.screenX - last_click.screenX, 2) + Math.pow(this_click.screenY - last_click.screenY, 2);
  if(distance > 8)
    return;

  /* Make sure these attributes match in both clicks: */
  var properties_to_match = ["ctrlKey", "altKey", "shiftKey", "metaKey", "target", "button"];
  for(var i = 0; i < properties_to_match.length; ++i)
  {
    var name = properties_to_match[i]
    if(this_click[name] != last_click[name])
      return;
  }

  /* Synthesize a dblclick event. */
  var e = document.createEvent("MouseEvent");
  e.initMouseEvent("dblclick", true, true, window,
                     2,
                     this_click.screenX, this_click.screenY,
                     this_click.clientX, this_click.clientY,
                     this_click.ctrlKey, this_click.altKey,
                     this_click.shiftKey, this_click.metaKey,
                     this_click.button, null);

  this.last_click_event = null;
  this_click.target.dispatchEvent(e);
}

/*
 * Mobile WebKit has serious problems with the click event: it delays them for the
 * entire double-click timeout, and if a double-click happens it doesn't deliver the
 * click at all.  This makes clicks unresponsive, and it has this behavior even
 * when the page can't be zoomed, which means nothing happens at all.
 *
 * Generate click events from touchend events to bypass this mess.
 *
 * XXX: This needs to understand multitouch.
 */
ResponsiveSingleClick = function()
{
  this.click_event = this.click_event.bindAsEventListener(this);
  this.touchstart_event = this.touchstart_event.bindAsEventListener(this);
  this.touchend_event = this.touchend_event.bindAsEventListener(this);

  this.last_touch = null;

  window.addEventListener("touchstart", this.touchstart_event, false);
  window.addEventListener("touchend", this.touchend_event, false);

  /* This is a capturing listener, so we can intercept clicks before they're
   * delivered to anyone. */
  window.addEventListener("click", this.click_event, true);
}

ResponsiveSingleClick.prototype.touchstart_event = function(event)
{
  /* Watch out: in Android 2.1's browser, the event.touches array and the items inside
   * it are actually modified in-place when the user drags.  That means that we can't just
   * save the entire array for comparing in touchend. */
  var touch = event.touches.item(0);
  this.last_touch = [touch.screenX, touch.screenY];
}

ResponsiveSingleClick.prototype.touchend_event = function(event)
{
  var touch = event.changedTouches.item(0);
  var this_touch = [touch.screenX, touch.screenY];
  var last_touch = this.last_touch;

  /* Don't trigger a click if the point has moved too far. */
  var distance = distance_squared(this_touch[0], this_touch[1], last_touch[0], last_touch[1]);
  if(distance > 50)
    return;

  var e = document.createEvent("MouseEvent");
  e.initMouseEvent("click", true, true, window,
                     1,
                     event.screenX, event.screenY,
                     event.clientX, event.clientY,
                     event.ctrlKey, event.altKey,
                     event.shiftKey, event.metaKey,
                     0, /* touch clicks are always button 0 - maybe not for multitouch */
                     null);
  e.synthesized_click = true;

  /* If we dispatch the click immediately, EmulateDoubleClick won't receive a
   * touchstart for the next click.  Defer dispatching it until we return. */
  (function() { event.target.dispatchEvent(e); }).defer();
}

/* Capture and cancel all clicks except the ones we generate. */
ResponsiveSingleClick.prototype.click_event = function(event)
{
  if(!event.synthesized_click)
    event.stop();
}

/* Stop all touchmove events on the document, to prevent dragging the window around. */
PreventDragScrolling = function()
{
  Element.observe(document, "touchmove", function(event) {
    event.preventDefault();
  });
}

var InitializeFullScreenBrowserHandlers = function()
{
  /* These handlers deal with heavily browser-specific issues.  Only install them
   * on browsers that have been tested to need them. */
  if(navigator.userAgent.indexOf("Android") != -1 && navigator.userAgent.indexOf("WebKit") != -1)
  {
    new ResponsiveSingleClick();
    new EmulateDoubleClick();
  }
  else if((navigator.userAgent.indexOf("iPhone") != -1 || navigator.userAgent.indexOf("iPad") != -1)
      && navigator.userAgent.indexOf("WebKit") != -1)
  {
    new ResponsiveSingleClick();
    new EmulateDoubleClick();
  }

  PreventDragScrolling();
}

SwipeHandler = function(element)
{
  this.element = element;
  this.dragger = new DragElement(element, this.ondrag.bind(this), this.startdrag.bind(this));
}

SwipeHandler.prototype.startdrag = function()
{
  this.swiped_horizontal = false;
  this.swiped_vertical = false;
}

SwipeHandler.prototype.ondrag = function(e)
{
  if(!this.swiped_horizontal)
  {
    // XXX: need a guessed DPI
    if(Math.abs(e.aX) > 100)
    {
      this.element.fire("swipe:horizontal", {right: e.aX > 0});
      this.swiped_horizontal = true;
    }
  }

  if(!this.swiped_vertical)
  {
    if(Math.abs(e.aY) > 100)
    {
      this.element.fire("swipe:vertical", {down: e.aY > 0});
      this.swiped_vertical = true;
    }
  }
}

SwipeHandler.prototype.destroy = function()
{
  this.dragger.destroy();
}

<commit_msg>comment<commit_after>/*
 * This file implements several helpers for fixing up full-page web apps on touchscreen
 * browsers:
 *
 * AndroidDetectWindowSize
 * EmulateDoubleClick
 * ResponsiveSingleClick
 * PreventDragScrolling
 *
 * Most of these are annoying hacks to work around the fact that WebKit on browsers was
 * designed with displaying scrolling webpages in mind, apparently without consideration
 * for full-screen applications: pages that should fill the screen at all times.  Most
 * of the browser mobile hacks no longer make sense: separate display viewports, touch
 * dragging, double-click zooming and their associated side-effects.
 */

/*
 * AndroidDetectWindowSize
 *
 * Implementing a full-page web app for Android is hard, because if you set the page to
 * "width: 100%; height: 100%;" it'll eat a big chunk of the screen with the address bar
 * which can't be scrolled off in that configuration.  We have to play games to figure out
 * the real size of the window, and set the body size to it explicitly.  This handler does
 * the following:
 *
 * - capture resize events
 * - cancel the resize event; we'll fire it again when we're done
 * - enable a large padding div, to ensure that we can scroll the window downward
 * - switch the body to overflow: auto
 * - window.scrollTo(0, 1) to scroll the address bar off screen, which increases the window
 *   size to the maximum
 * - wait a little while; unbelievably, window.scrollTo on Android animates rather than
 *   snapping to the position as all sane browsers do
 * - set the body to the size of the window
 * - hide the padding div and set the body back to overflow: hidden
 * - synthesize a new resize event to continue other event handlers that we originally cancelled
 *
 * resize will always be fired at least once as a result of constructing this class.
 *
 * This is only used on Android.
 */

function AndroidDetectWindowSize()
{
  /* This is shown to make sure we can scroll the address bar off. */
  this.padding = document.createElement("DIV");
  this.padding.setStyle({width: "5000px", height: "5000px"});
  this.padding.hide();
  document.documentElement.appendChild(this.padding);

  this.window_size = [0, 0];
  this.finish = this.finish.bind(this);
  this.event_onresize = this.event_onresize.bindAsEventListener(this);

  this.finish_timer = null;
  this.active = false;

  /* This will run a detection cycle, which will fire resize and then leave us capturing resize. */
  this.begin();
}

/* Return true if Android resize handling is needed. */
AndroidDetectWindowSize.required = function()
{
  // XXX: be more specific
  return navigator.userAgent.indexOf("Android") != -1;
}

/* Dispatch a resize event that we won't intercept, and which will continue on to regular
 * listeners.  This is used after we've completed processing. */
AndroidDetectWindowSize.prototype.dispatch_resize_event = function()
{
  debug.log("dispatch");
  var e = document.createEvent("Event");
  e.from_window_size_detection = true;
  e.initEvent("resize", true, true);
  document.documentElement.dispatchEvent(e);
}

AndroidDetectWindowSize.prototype.begin = function()
{
  if(this.active)
    return;

  var initial_window_size = this.current_window_size();
  if(this.window_size && initial_window_size[0] == this.window_size[0] && initial_window_size[1] == this.window_size[1])
  {
    debug.log("skipped");
    return;
  }

  debug.log("begin");
  document.body.setStyle({overflow: "auto"});
  this.window_size = initial_window_size;
  this.padding.show();
  this.active = true;
  window.removeEventListener("resize", this.event_onresize, true);

  if(this.finish_timer != null)
    this.finish_timer = window.clearTimeout(this.finish_timer);
  this.finish_timer = window.setTimeout(this.finish, 250);

  window.scrollTo(0, 1);
}

AndroidDetectWindowSize.prototype.end = function()
{
  if(!this.active)
    return;
  this.active = false;

  this.padding.hide();
  document.body.setStyle({overflow: "hidden"});
  window.addEventListener("resize", this.event_onresize, true);
}

AndroidDetectWindowSize.prototype.current_window_size = function()
{
  return [window.innerWidth, window.innerHeight];
}

AndroidDetectWindowSize.prototype.finish = function()
{
  if(!this.active)
    return;
  debug.log("finish()");
  this.end();

  this.window_size[0] = Math.max(this.window_size[0], window.innerWidth);
  this.window_size[1] = Math.max(this.window_size[1], window.innerHeight);

  /* If the orientation's been changed, start over. */
  var was_landscape = this.window_size[0] > this.window_size[1];
  var is_landscape = window.innerWidth > window.innerHeight;
  if(was_landscape != is_landscape)
  {
    debug.log("restart: orientation changed");
    this.end();
    this.begin();
    return;
  }

  // We need to fudge the height up a pixel, or in many cases we'll end up with a white line
  // at the bottom of the screen.  This seems to be sub-pixel rounding error.
  debug.log("new window size: " + this.window_size[0] + "x" + this.window_size[1]);
  document.body.setStyle({width: this.window_size[0] + "px", height: (this.window_size[1]+1) + "px"});

  this.dispatch_resize_event();
}

AndroidDetectWindowSize.prototype.event_onresize = function(e)
{
  /* Ignore events that we generated ourselves after completion. */
  if(e.from_window_size_detection)
  {
    debug.log("ignored");
    return;
  }

  debug.log("stopping resize event");
  e.stopPropagation();

  /* A resize event starts a new detection cycle, if we're not already in one. */
  if(!this.active)
  {
    debug.log("resize");
    this.begin();
  }
}

/*
 * Work around a bug on many touchscreen browsers: even when the page isn't
 * zoomable, dblclick is never fired.  We have to emulate it.
 *
 * This isn't an exact emulation of the event behavior:
 *
 * - It triggers from touchstart rather than mousedown.  The second mousedown
 *   of a double click isn't being fired reliably in Android's WebKit.
 *
 * - preventDefault on the triggering event should prevent a dblclick, but
 *   we can't find out if it's been called; there's nothing like Firefox's
 *   getPreventDefault.  We could mostly emulate this by overriding
 *   Event.preventDefault to set a flag that we can read.
 *
 * - The conditions for a double click won't match the ones of the platform.
 *
 * This is needed on Android and iPhone's WebKit.
 *
 * Note that this triggers a minor bug on Android: after firing a dblclick event,
 * we no longer receive mousemove events until the touch is released, which means
 * PreventDragScrolling can't cancel dragging.
 */

function EmulateDoubleClick()
{
  this.touchstart_event = this.touchstart_event.bindAsEventListener(this);
  this.last_click_event = null;

  window.addEventListener("touchstart", this.touchstart_event, false);
}

EmulateDoubleClick.prototype.touchstart_event = function(event)
{
  var this_click = event;
  var last_click  = this.last_click_event;

  this.last_click_event = this_click;
  if(last_click == null)
      return;

  /* Check that not too much time has passed. */
  var time_since_previous = this_click.timeStamp - last_click.timeStamp;
  if(time_since_previous > 500)
    return;

  /* Check that the clicks aren't too far apart. */
  var distance = Math.pow(this_click.screenX - last_click.screenX, 2) + Math.pow(this_click.screenY - last_click.screenY, 2);
  if(distance > 8)
    return;

  /* Make sure these attributes match in both clicks: */
  var properties_to_match = ["ctrlKey", "altKey", "shiftKey", "metaKey", "target", "button"];
  for(var i = 0; i < properties_to_match.length; ++i)
  {
    var name = properties_to_match[i]
    if(this_click[name] != last_click[name])
      return;
  }

  /* Synthesize a dblclick event. */
  var e = document.createEvent("MouseEvent");
  e.initMouseEvent("dblclick", true, true, window,
                     2,
                     this_click.screenX, this_click.screenY,
                     this_click.clientX, this_click.clientY,
                     this_click.ctrlKey, this_click.altKey,
                     this_click.shiftKey, this_click.metaKey,
                     this_click.button, null);

  this.last_click_event = null;
  this_click.target.dispatchEvent(e);
}

/*
 * Mobile WebKit has serious problems with the click event: it delays them for the
 * entire double-click timeout, and if a double-click happens it doesn't deliver the
 * click at all.  This makes clicks unresponsive, and it has this behavior even
 * when the page can't be zoomed, which means nothing happens at all.
 *
 * Generate click events from touchend events to bypass this mess.
 *
 * XXX: This needs to understand multitouch.
 */
ResponsiveSingleClick = function()
{
  this.click_event = this.click_event.bindAsEventListener(this);
  this.touchstart_event = this.touchstart_event.bindAsEventListener(this);
  this.touchend_event = this.touchend_event.bindAsEventListener(this);

  this.last_touch = null;

  window.addEventListener("touchstart", this.touchstart_event, false);
  window.addEventListener("touchend", this.touchend_event, false);

  /* This is a capturing listener, so we can intercept clicks before they're
   * delivered to anyone. */
  window.addEventListener("click", this.click_event, true);
}

ResponsiveSingleClick.prototype.touchstart_event = function(event)
{
  /* Watch out: in Android 2.1's browser, the event.touches array and the items inside
   * it are actually modified in-place when the user drags.  That means that we can't just
   * save the entire array for comparing in touchend. */
  var touch = event.touches.item(0);
  this.last_touch = [touch.screenX, touch.screenY];
}

ResponsiveSingleClick.prototype.touchend_event = function(event)
{
  var touch = event.changedTouches.item(0);
  var this_touch = [touch.screenX, touch.screenY];
  var last_touch = this.last_touch;

  /* Don't trigger a click if the point has moved too far. */
  var distance = distance_squared(this_touch[0], this_touch[1], last_touch[0], last_touch[1]);
  if(distance > 50)
    return;

  var e = document.createEvent("MouseEvent");
  e.initMouseEvent("click", true, true, window,
                     1,
                     event.screenX, event.screenY,
                     event.clientX, event.clientY,
                     event.ctrlKey, event.altKey,
                     event.shiftKey, event.metaKey,
                     0, /* touch clicks are always button 0 - maybe not for multitouch */
                     null);
  e.synthesized_click = true;

  /* If we dispatch the click immediately, EmulateDoubleClick won't receive a
   * touchstart for the next click.  Defer dispatching it until we return. */
  (function() { event.target.dispatchEvent(e); }).defer();
}

/* Capture and cancel all clicks except the ones we generate. */
ResponsiveSingleClick.prototype.click_event = function(event)
{
  if(!event.synthesized_click)
    event.stop();
}

/* Stop all touchmove events on the document, to prevent dragging the window around. */
PreventDragScrolling = function()
{
  Element.observe(document, "touchmove", function(event) {
    event.preventDefault();
  });
}

var InitializeFullScreenBrowserHandlers = function()
{
  /* These handlers deal with heavily browser-specific issues.  Only install them
   * on browsers that have been tested to need them. */
  if(navigator.userAgent.indexOf("Android") != -1 && navigator.userAgent.indexOf("WebKit") != -1)
  {
    new ResponsiveSingleClick();
    new EmulateDoubleClick();
  }
  else if((navigator.userAgent.indexOf("iPhone") != -1 || navigator.userAgent.indexOf("iPad") != -1)
      && navigator.userAgent.indexOf("WebKit") != -1)
  {
    new ResponsiveSingleClick();
    new EmulateDoubleClick();
  }

  PreventDragScrolling();
}

SwipeHandler = function(element)
{
  this.element = element;
  this.dragger = new DragElement(element, this.ondrag.bind(this), this.startdrag.bind(this));
}

SwipeHandler.prototype.startdrag = function()
{
  this.swiped_horizontal = false;
  this.swiped_vertical = false;
}

SwipeHandler.prototype.ondrag = function(e)
{
  if(!this.swiped_horizontal)
  {
    // XXX: need a guessed DPI
    if(Math.abs(e.aX) > 100)
    {
      this.element.fire("swipe:horizontal", {right: e.aX > 0});
      this.swiped_horizontal = true;
    }
  }

  if(!this.swiped_vertical)
  {
    if(Math.abs(e.aY) > 100)
    {
      this.element.fire("swipe:vertical", {down: e.aY > 0});
      this.swiped_vertical = true;
    }
  }
}

SwipeHandler.prototype.destroy = function()
{
  this.dragger.destroy();
}

<|endoftext|>


<commit_before>import React from 'react'
import TestUtils from 'react-addons-test-utils'
import {Provider} from 'react-redux'
import {createStore} from 'redux'

import rootReducer from 'gModules/reducers.js'

import {GuesstimateRecorder} from 'lib/recorder'
window.recorder = new GuesstimateRecorder()

import {SpaceToolbar} from '../index'

const testStore = createStore(rootReducer)

describe('<SpaceToolbar>', () => {
  it ('renders the View menu', () => {
    const toolbar = TestUtils.renderIntoDocument(
      <Provider store={testStore}>
        <SpaceToolbar />
      </Provider>
    )
  })
})
<commit_msg>Actually Testing Something.<commit_after>import React from 'react'
import TestUtils from 'react-addons-test-utils'
import {Provider} from 'react-redux'
import {createStore} from 'redux'

import rootReducer from 'gModules/reducers.js'

import {GuesstimateRecorder} from 'lib/recorder'
window.recorder = new GuesstimateRecorder()

import {SpaceToolbar} from '../index'

const testStore = createStore(rootReducer)

describe('<SpaceToolbar>', () => {
  it ('renders the view form', () => {
    const toolbar = TestUtils.renderIntoDocument(
      <Provider store={testStore}>
        <SpaceToolbar />
      </Provider>
    )

    TestUtils.findRenderedDOMComponentWithClass(toolbar, 'SpaceShowToolbar')
  })
})
<|endoftext|>