| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511 | /*! * Bootstrap's Gruntfile * http://getbootstrap.com * 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 generateGlyphiconsData = require('./grunt/bs-glyphicons-data-generator.js');  var BsLessdocParser = require('./grunt/bs-lessdoc-parser.js');  var getLessVarsData = function () {    var filePath = path.join(__dirname, 'less/variables.less');    var fileContent = fs.readFileSync(filePath, { encoding: 'utf8' });    var parser = new BsLessdocParser(fileContent);    return { sections: parser.parseFile() };  };  var generateRawFiles = require('./grunt/bs-raw-files-generator.js');  var generateCommonJSModule = require('./grunt/bs-commonjs-generator.js');  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 the <%= pkg.license %> license\n' +            ' */\n',    jqueryCheck: configBridge.config.jqueryCheck.join('\n'),    jqueryVersionCheck: configBridge.config.jqueryVersionCheck.join('\n'),    // Task configuration.    clean: {      dist: 'dist',      docs: 'docs/dist'    },    jshint: {      options: {        jshintrc: 'js/.jshintrc'      },      grunt: {        options: {          jshintrc: 'grunt/.jshintrc'        },        src: ['Gruntfile.js', 'package.js', 'grunt/*.js']      },      core: {        src: 'js/*.js'      },      test: {        options: {          jshintrc: 'js/tests/unit/.jshintrc'        },        src: 'js/tests/unit/*.js'      },      assets: {        src: ['docs/assets/js/src/*.js', 'docs/assets/js/*.js', '!docs/assets/js/*.min.js']      }    },    jscs: {      options: {        config: 'js/.jscsrc'      },      grunt: {        src: '<%= jshint.grunt.src %>'      },      core: {        src: '<%= jshint.core.src %>'      },      test: {        src: '<%= jshint.test.src %>'      },      assets: {        options: {          requireCamelCaseOrUpperCaseIdentifiers: null        },        src: '<%= jshint.assets.src %>'      }    },    concat: {      options: {        banner: '<%= banner %>\n<%= jqueryCheck %>\n<%= jqueryVersionCheck %>',        stripBanners: false      },      bootstrap: {        src: [          'js/transition.js',          'js/alert.js',          'js/button.js',          'js/carousel.js',          'js/collapse.js',          'js/dropdown.js',          'js/modal.js',          'js/tooltip.js',          'js/popover.js',          'js/scrollspy.js',          'js/tab.js',          'js/affix.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'      },      customize: {        src: configBridge.paths.customizerJs,        dest: 'docs/assets/js/customize.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'    },    less: {      compileCore: {        options: {          strictMath: true,          sourceMap: true,          outputSourceFiles: true,          sourceMapURL: '<%= pkg.name %>.css.map',          sourceMapFilename: 'dist/css/<%= pkg.name %>.css.map'        },        src: 'less/bootstrap.less',        dest: 'dist/css/<%= pkg.name %>.css'      },      compileTheme: {        options: {          strictMath: true,          sourceMap: true,          outputSourceFiles: true,          sourceMapURL: '<%= pkg.name %>-theme.css.map',          sourceMapFilename: 'dist/css/<%= pkg.name %>-theme.css.map'        },        src: 'less/theme.less',        dest: 'dist/css/<%= pkg.name %>-theme.css'      }    },    autoprefixer: {      options: {        browsers: configBridge.config.autoprefixerBrowsers      },      core: {        options: {          map: true        },        src: 'dist/css/<%= pkg.name %>.css'      },      theme: {        options: {          map: true        },        src: 'dist/css/<%= pkg.name %>-theme.css'      },      docs: {        src: ['docs/assets/css/src/docs.css']      },      examples: {        expand: true,        cwd: 'docs/examples/',        src: ['**/*.css'],        dest: 'docs/examples/'      }    },    csslint: {      options: {        csslintrc: 'less/.csslintrc'      },      dist: [        'dist/css/bootstrap.css',        'dist/css/bootstrap-theme.css'      ],      examples: [        'docs/examples/**/*.css'      ],      docs: {        options: {          ids: false,          'overqualified-elements': false        },        src: 'docs/assets/css/src/docs.css'      }    },    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: 'ie8',        keepSpecialComments: '*',        sourceMap: true,        sourceMapInlineSources: true,        advanced: false      },      minifyCore: {        src: 'dist/css/<%= pkg.name %>.css',        dest: 'dist/css/<%= pkg.name %>.min.css'      },      minifyTheme: {        src: 'dist/css/<%= pkg.name %>-theme.css',        dest: 'dist/css/<%= pkg.name %>-theme.min.css'      },      docs: {        src: [          'docs/assets/css/ie10-viewport-bug-workaround.css',          'docs/assets/css/src/pygments-manni.css',          'docs/assets/css/src/docs.css'        ],        dest: 'docs/assets/css/docs.min.css'      }    },    csscomb: {      options: {        config: 'less/.csscomb.json'      },      dist: {        expand: true,        cwd: 'dist/css/',        src: ['*.css', '!*.min.css'],        dest: 'dist/css/'      },      examples: {        expand: true,        cwd: 'docs/examples/',        src: '**/*.css',        dest: 'docs/examples/'      },      docs: {        src: 'docs/assets/css/src/docs.css',        dest: 'docs/assets/css/src/docs.css'      }    },    copy: {      fonts: {        expand: true,        src: 'fonts/**',        dest: 'dist/'      },      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'        }      }    },    htmlmin: {      dist: {        options: {          collapseBooleanAttributes: true,          collapseWhitespace: true,          conservativeCollapse: true,          decodeEntities: false,          minifyCSS: {            compatibility: 'ie8',            keepSpecialComments: 0          },          minifyJS: true,          minifyURLs: false,          processConditionalComments: true,          removeAttributeQuotes: true,          removeComments: true,          removeOptionalAttributes: true,          removeOptionalTags: true,          removeRedundantAttributes: true,          removeScriptTypeAttributes: true,          removeStyleLinkTypeAttributes: true,          removeTagWhitespace: false,          sortAttributes: true,          sortClassName: true        },        expand: true,        cwd: '_gh_pages',        dest: '_gh_pages',        src: [          '**/*.html',          '!examples/**/*.html'        ]      }    },    pug: {      options: {        pretty: true,        data: getLessVarsData      },      customizerVars: {        src: 'docs/_pug/customizer-variables.pug',        dest: 'docs/_includes/customizer-variables.html'      },      customizerNav: {        src: 'docs/_pug/customizer-nav.pug',        dest: 'docs/_includes/nav/customize.html'      }    },    htmllint: {      options: {        ignore: [          'Attribute "autocomplete" not allowed on element "button" at this point.',          'Attribute "autocomplete" is only allowed when the input type is "color", "date", "datetime", "datetime-local", "email", "hidden", "month", "number", "password", "range", "search", "tel", "text", "time", "url", or "week".',          'Element "img" is missing required attribute "src".'        ]      },      src: '_gh_pages/**/*.html'    },    watch: {      src: {        files: '<%= jshint.core.src %>',        tasks: ['jshint:core', 'qunit', 'concat']      },      test: {        files: '<%= jshint.test.src %>',        tasks: ['jshint:test', 'qunit']      },      less: {        files: 'less/**/*.less',        tasks: 'less'      }    },    'saucelabs-qunit': {      all: {        options: {          build: process.env.TRAVIS_JOB_ID,          throttled: 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: {      npmUpdate: {        command: 'npm update'      }    },    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' });  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', 'csslint:dist', 'test-js', 'docs']);  }  // Skip HTML validation if running a different subset of the test suite  if (runSubset('validate-html') &&      // Skip HTML5 validator on Travis 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('connect');    testSubtasks.push('saucelabs-qunit');  }  grunt.registerTask('test', testSubtasks);  grunt.registerTask('test-js', ['jshint:core', 'jshint:test', 'jshint:grunt', 'jscs:core', 'jscs:test', 'jscs:grunt', 'qunit']);  // JS distribution task.  grunt.registerTask('dist-js', ['concat', 'uglify:core', 'commonjs']);  // CSS distribution task.  grunt.registerTask('less-compile', ['less:compileCore', 'less:compileTheme']);  grunt.registerTask('dist-css', ['less-compile', 'autoprefixer:core', 'autoprefixer:theme', 'csscomb:dist', 'cssmin:minifyCore', 'cssmin:minifyTheme']);  // Full distribution task.  grunt.registerTask('dist', ['clean:dist', 'dist-css', 'copy:fonts', 'dist-js']);  // Default task.  grunt.registerTask('default', ['clean:dist', 'copy:fonts', 'test']);  grunt.registerTask('build-glyphicons-data', function () { generateGlyphiconsData.call(this, grunt); });  // task for building customizer  grunt.registerTask('build-customizer', ['build-customizer-html', 'build-raw-files']);  grunt.registerTask('build-customizer-html', 'pug');  grunt.registerTask('build-raw-files', 'Add scripts/less files to customizer.', function () {    var banner = grunt.template.process('<%= banner %>');    generateRawFiles(grunt, banner);  });  grunt.registerTask('commonjs', 'Generate CommonJS entrypoint module in dist dir.', function () {    var srcFiles = grunt.config.get('concat.bootstrap.src');    var destFilepath = 'dist/js/npm.js';    generateCommonJSModule(grunt, srcFiles, destFilepath);  });  // Docs task.  grunt.registerTask('docs-css', ['autoprefixer:docs', 'autoprefixer:examples', 'csscomb:docs', 'csscomb:examples', 'cssmin:docs']);  grunt.registerTask('lint-docs-css', ['csslint:docs', 'csslint:examples']);  grunt.registerTask('docs-js', ['uglify:docsJs', 'uglify:customize']);  grunt.registerTask('lint-docs-js', ['jshint:assets', 'jscs:assets']);  grunt.registerTask('docs', ['docs-css', 'lint-docs-css', 'docs-js', 'lint-docs-js', 'clean:docs', 'copy:docs', 'build-glyphicons-data', 'build-customizer']);  grunt.registerTask('docs-github', ['jekyll:github', 'htmlmin']);  grunt.registerTask('prep-release', ['dist', 'docs', 'docs-github', 'compress']);};
 |