text: module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-autoprefixer');
    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-sass');
    grunt.loadNpmTasks('grunt-contrib-concat');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.loadNpmTasks('grunt-contrib-clean');
    grunt.loadNpmTasks('grunt-contrib-watch');
  
    var configBridge = grunt.file.readJSON('./bower_components/bootstrap/grunt/configBridge.json', { encoding: 'utf8' });
  
    // Project configuration.
    grunt.initConfig({
      pkg: grunt.file.readJSON('package.json'),
      builddir: '.',
      buildtheme: '',
      banner: '/*!\n' +
              ' * <%= pkg.name %> v<%= pkg.version %>\n' +
              ' * Homepage: <%= pkg.homepage %>\n' +
              ' * Copyright 2012-<%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
              ' * Licensed under <%= pkg.license %>\n' +
              ' * Based on Bootstrap\n' +
              '*/\n',
      swatch: {
        amelia:{}, cerulean:{}, cosmo:{}, cyborg:{}, darkly:{},
        flatly:{}, journal:{}, lumen:{}, paper:{}, readable:{},
        sandstone:{}, simplex:{}, slate:{}, spacelab:{}, superhero:{},
        united:{}, yeti:{}, custom:{}, remugio:{}
      },
      clean: {
        build: {
          src: ['*/build.less', '*/build.scss', '!global/build.less', '!global/build.scss']
        }
      },
      concat: {
        options: {
          banner: '<%= banner %>',
          stripBanners: false
        },
        dist: {
          src: [],
          dest: ''
        }
      },
      less: {
        dist: {
          options: {
            compress: false,
            strictMath: true
          },
          files: {}
        }
      },
      autoprefixer: {
        options: {
          browsers: configBridge.config.autoprefixerBrowsers
        },
        dist: {
          src: '*/bootstrap.css'
        }
      },
      watch: {
        files: ['*/variables.less', '*/bootswatch.less', '*/index.html'],
        tasks: 'build',
        options: {
          livereload: true,
          nospawn: true
        }
      },
      connect: {
        base: {
          options: {
            port: 3000,
            livereload: true,
            open: true
          }
        },
        keepalive: {
          options: {
            port: 3000,
            livereload: true,
            keepalive: true,
            open: true
          }
        }
      }
    });
  
    grunt.registerTask('none', function() {});
  
    grunt.registerTask('build', 'build a regular theme', function(theme, compress) {
      var theme = theme == undefined ? grunt.config('buildtheme') : theme;
      var compress = compress == undefined ? true : compress;
  
      var isValidTheme = grunt.file.exists(theme, 'variables.less') && grunt.file.exists(theme, 'bootswatch.less');
   
       // cancel the build (without failing) if this directory is not a valid theme
      if (!isValidTheme) {
        return;
      }
  
      var concatSrc;
      var concatDest;
      var lessDest;
      var lessSrc;
      var files = {};
      var dist = {};
      concatSrc = 'global/build.less';
      concatDest = theme + '/build.less';
      lessDest = '<%=builddir%>/' + theme + '/bootstrap.css';
      lessSrc = [ theme + '/' + 'build.less' ];
  
      dist = {src: concatSrc, dest: concatDest};
      grunt.config('concat.dist', dist);
      files = {}; files[lessDest] = lessSrc;
      grunt.config('less.dist.files', files);
      grunt.config('less.dist.options.compress', false);
  
      grunt.task.run(['concat', 'less:dist', 'prefix:' + lessDest, 'clean:build',
        compress ? 'compress:'+lessDest+':'+'<%=builddir%>/' + theme + '/bootstrap.min.css':'none']);
    });
  
  grunt.registerTask('build_scss', 'build a regular theme from scss', function(theme, compress) {
      var theme = theme == undefined ? grunt.config('buildtheme') : theme;
      var compress = compress == undefined ? true : compress;
  
      var isValidTheme = grunt.file.exists(theme, '_variables.scss') && grunt.file.exists(theme, '_bootswatch.scss');
  
       // cancel the build (without failing) if this directory is not a valid theme
      if (!isValidTheme) {
        return;
      }
      var concatSrc;
      var concatDest;
      var scssDest;
      var scssSrc;
      var files = {};
      var dist = {};
      concatSrc = 'global/build.scss';
      concatDest = theme + '/build.scss';
      scssDest = '<%=builddir%>/' + theme + '/bootstrap.css';
      scssSrc = [theme + '/' + 'build.scss'];
  
      dist = {src: concatSrc, dest: concatDest};
      grunt.config('concat.dist', dist);
      files = {};
      files[scssDest] = scssSrc;
      grunt.config('sass.dist.files', files);
      grunt.config('sass.dist.options.style', 'expanded');
      grunt.config('sass.dist.options.precision', 8);
      grunt.config('sass.dist.options.unix-newlines', true);
   
      grunt.task.run(['concat', 'sass:dist', 'prefix:' + scssDest, 'clean:build',
          compress ? 'compress_scss:' + scssDest + ':' + '<%=builddir%>/' + theme + '/bootstrap.min.css' : 'none']);
    });
    
    grunt.registerTask('prefix', 'autoprefix a generic css', function(fileSrc) {
      grunt.config('autoprefixer.dist.src', fileSrc);
      grunt.task.run('autoprefixer');
    });
  
    grunt.registerTask('compress', 'compress a generic css', function(fileSrc, fileDst) {
      var files = {}; files[fileDst] = fileSrc;
      grunt.log.writeln('compressing file ' + fileSrc);
  
      grunt.config('less.dist.files', files);
      grunt.config('less.dist.options.compress', true);
      grunt.task.run(['less:dist']);
    });
  
    grunt.registerTask('compress_scss', 'compress a generic css with sass', function(fileSrc, fileDst) {
      var files = {}; files[fileDst] = fileSrc;
      grunt.log.writeln('compressing file ' + fileSrc);
  
      grunt.config('sass.dist.files', files);
      grunt.config('sass.dist.options.style', 'compressed');
      grunt.task.run(['sass:dist']);
    });
  
    grunt.registerMultiTask('swatch', 'build a theme', function() {
      var t = this.target;
      grunt.task.run('build:'+t);
    });
  
    grunt.registerTask('swatch_scss', 'build a theme from scss ', function (theme) {
      var t = theme;
      if (!t) {
        for (var t in grunt.config('swatch')) {
          grunt.task.run('build_scss:' + t);
        }
      } else {
        grunt.task.run('build_scss:' + t);
      }
    });
  
    grunt.event.on('watch', function(action, filepath) {
      var path = require('path');
      var theme = path.dirname(filepath);
      grunt.config('buildtheme', theme);
    });
  
    /**
    * Regex borrowed form
    * https://gist.github.com/rosskevin/ddfe895091de2ca5f931
    * */
    grunt.registerTask('convert_less', 'Convert less to scss using regular expression', function () {
      var convertBaseDir = '';
      grunt.file.expand(convertBaseDir + '*/*.less').forEach(function (lessFile) {
        if (lessFile !=="global/build.less"){
          var srcContents = grunt.file.read(lessFile);
          var out = srcContents
                  // 1.  replace @ with $
                  .replace(/@(?!import|media|keyframes|-)/g, '$')
                  // 2.  replace mixins
                  .replace(/[\.#](?![0-9])([\w\-]*)\s*\((.*)\)\s*\{/g, '@mixin $1($2){')
                  // 3.  In LESS, bootstrap namespaces mixins, in SASS they are just prefixed e.g #gradient > .vertical-three-colors becomes @include gradient-vertical-three-colors
                  .replace(/[\.#](?![0-9])([\w\-]*)\s>\s\.(.*;)/g, '@include $1-$2')
                  // 4.  replace includes
                  .replace(/[\.#](?![0-9])([\w\-].*\(.*;)/g, '@include $1')
                  // 5.  replace no param mixin includes with empty parens
                  .replace(/@include\s([\w\-]*\s*);/g, '@include $1();')
                  // 6.  replace extends .class; @extend .class;
                  .replace(/(\.(?![0-9])([\w\-]+);)/g, '@extend $1')
                  // 7.  replace string literals
                  .replace(/~"(.*)"/g, '#{"$1"}')
                  // 8.  replace interpolation  ${var} > #{$var} 
                  .replace(/\$\{(.*)\}/g, '#{$$$1}')
                  // 9.  replace spin to adjust-hue (function name diff)
                  .replace(/spin\(/g, 'adjust-hue(')
                  // 10. replace bower and imports in build.scss
                  .replace(/bootstrap\/less\//g, 'bootstrap-sass-official/assets/stylesheets/')
                  .replace(/\.less/g, '')
                  // 11. replace icon-font-path value with conditional for asset helpers
                  .replace(/(\$icon-font-path:).*;/g, '$1 if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/");')
                  // 12. set bootswatch's web-font-path value as !default
                  .replace(/(\$web-font-path:.*);/g, '$1 !default;')
                  // 13. Remove the web-font mixin which breaks in libsass
                  .replace(/[\s\S][\s\S]@mixin web-font.*([\s\S]*?).*;([\s\S]*?)}([\s\S]*?)/,"")
                  // 14. Replace usage of the web-font mixin with variable interpolation
                  .replace(/@include web-font/,'@import url');
  
                  if (/\/variables.less$/.test(lessFile)) {
                  // 15. set default value of $bootstrap-sass-asset-helper to false
                    out = "$bootstrap-sass-asset-helper: false;\n" + out;
                  // 16. only assign variables if they haven't been previously set e.g. $var: #f00; > $var: #f00 !default;
                    out = out.replace(/^(\$.*);/gm, '$1 !default;');
                  }
  
          var baseDirRegex = new RegExp("^" + convertBaseDir, "g");
          var sassFile = lessFile.replace(baseDirRegex, '').replace(/\.less$/, '.scss').replace(/(bootswatch|variables)/, '_$1');
          grunt.file.write(sassFile, out);
          grunt.log.writeln('Converted less file:  ', lessFile, Array(27 - lessFile.length).join(' '),'> ', sassFile);
        }
      });
    });
  
    grunt.registerTask('server', 'connect:keepalive');
  
    grunt.registerTask('default', ['connect:base', 'watch']);
  };
  