text: /*
   * grunt-contrib-less
   * http://gruntjs.com/
   *
   * Copyright (c) 2013 Tyler Kellen, contributors
   * Licensed under the MIT license.
   */
  
  'use strict';
  
  module.exports = function(grunt) {
  
    // Internal lib.
    var contrib = require('grunt-lib-contrib').init(grunt);
  
    var path = require('path');
    var less = require('less');
  
    var lessOptions = {
      parse: ['paths', 'optimization', 'filename', 'strictImports', 'syncImport', 'dumpLineNumbers', 'relativeUrls', 'rootpath'],
      render: ['compress', 'cleancss', 'ieCompat', 'strictMath', 'strictUnits', 'sourceMap', 'sourceMapFilename', 'sourceMapBasepath', 'sourceMapRootpath']
    };
  
    grunt.registerMultiTask('less', 'Compile LESS files to CSS', function() {
      var done = this.async();
  
      var options = this.options();
      grunt.verbose.writeflags(options, 'Options');
  
      if (this.files.length < 1) {
        grunt.verbose.warn('Destination not written because no source files were provided.');
      }
  
      grunt.util.async.forEachSeries(this.files, function(f, nextFileObj) {
        var destFile = f.dest;
  
        var files = f.src.filter(function(filepath) {
          // Warn on and remove invalid source files (if nonull was set).
          if (!grunt.file.exists(filepath)) {
            grunt.log.warn('Source file "' + filepath + '" not found.');
            return false;
          } else {
            return true;
          }
        });
  
        if (files.length === 0) {
          if (f.src.length < 1) {
            grunt.log.warn('Destination not written because no source files were found.');
          }
  
          // No src files, goto next target. Warn would have been issued above.
          return nextFileObj();
        }
  
        var compiledMax = [], compiledMin = [];
        grunt.util.async.concatSeries(files, function(file, next) {
          compileLess(file, options, function(css, err) {
            if (!err) {
              if (css.max) {
                compiledMax.push(css.max);
              }
              compiledMin.push(css.min);
              next();
            } else {
              nextFileObj(err);
            }
          }, function (sourceMapContent) {
            grunt.file.write(options.sourceMapFilename, sourceMapContent);
            grunt.log.writeln('File ' + options.sourceMapFilename.cyan + ' created.');
          });
        }, function() {
          if (compiledMin.length < 1) {
            grunt.log.warn('Destination not written because compiled files were empty.');
          } else {
            var min = compiledMin.join(options.cleancss ? '' : grunt.util.normalizelf(grunt.util.linefeed));
            grunt.file.write(destFile, min);
            grunt.log.writeln('File ' + destFile.cyan + ' created.');
  
            // ...and report some size information.
            if (options.report) {
              contrib.minMaxInfo(min, compiledMax.join(grunt.util.normalizelf(grunt.util.linefeed)), options.report);
            }
          }
          nextFileObj();
        });
  
      }, done);
    });
  
    var compileLess = function(srcFile, options, callback, sourceMapCallback) {
      options = grunt.util._.extend({filename: srcFile}, options);
      options.paths = options.paths || [path.dirname(srcFile)];
  
      var css;
      var srcCode = grunt.file.read(srcFile);
  
      var parser = new less.Parser(grunt.util._.pick(options, lessOptions.parse));
  
      parser.parse(srcCode, function(parse_err, tree) {
        if (parse_err) {
          lessError(parse_err, srcFile);
          callback('',true);
        }
  
        // Load custom functions
        if (options.customFunctions) {
          Object.keys(options.customFunctions).forEach(function(name) {
            less.tree.functions[name.toLowerCase()] = function() {
              var args = [].slice.call(arguments);
              args.unshift(less);
              return new less.tree.Anonymous(options.customFunctions[name].apply(this, args));
            };
          });
        }
  
        var minifyOptions = grunt.util._.pick(options, lessOptions.render);
  
        if (minifyOptions.sourceMapFilename) {
          minifyOptions.writeSourceMap = sourceMapCallback;
        }
  
        try {
          css = minify(tree, minifyOptions);
          callback(css, null);
        } catch (e) {
          lessError(e, srcFile);
          callback(css, true);
        }
      });
    };
  
    var formatLessError = function(e) {
      var pos = '[' + 'L' + e.line + ':' + ('C' + e.column) + ']';
      return e.filename + ': ' + pos + ' ' + e.message;
    };
  
    var lessError = function(e, file) {
      var message = less.formatError ? less.formatError(e) : formatLessError(e);
  
      grunt.log.error(message);
      grunt.fail.warn('Error compiling ' + file);
    };
  
    var minify = function (tree, options) {
      var result = {
        min: tree.toCSS(options)
      };
      if (!grunt.util._.isEmpty(options)) {
        result.max = tree.toCSS();
      }
      return result;
    };
  };
  