<commit_before>/*

  API:

    var postgrator = require('postgrator');

    postgrator.setConfig({
        driver: 'pg', // or pg.js, mysql, mssql, tedious
        migrationDirectory: '',
        logProgress: true,
        schemaTable: '', // default is 'schemaversion'
        host: '',
        database: '',
        username: '',
        password: ''
    });

    postgrator.migrate(version, function (err, migrations) {
        // handle the error, and if you want end the connection
        postgrator.endConnection();
    });

  NOTES:

  If the table specified by config.schemaTable is not present, it will be created automatically!
  If no migration version is supplied, no migration is performed

  THINGS TO IMPLEMENT SOMEDAY (MAYBE)

  postgrator.migrate('max', callback);  // migrate to the latest migration available

================================================================= */

var fs = require('fs');
var crypto = require('crypto');
var createCommonClient = require('./lib/create-common-client.js');

var commonClient;
var currentVersion;
var targetVersion;
var migrations = []; // array of objects like: {version: n, action: 'do', direction: 'up', filename: '0001.up.sql'}

var config = {};

exports.config = config;

/*  Set Config
================================================================= */
exports.setConfig = function (configuration) {
    config = configuration;
    config.schemaTable = config.schemaTable || 'schemaversion';
    config.logProgress = config.logProgress != null ? config.logProgress : true;

    commonClient = createCommonClient(config);
};

/*  Migration Sorting Functions
================================================================= */
var sortMigrationsAsc = function (a,b) {
  if (a.version < b.version)
    return -1;
  if (a.version > b.version)
    return 1;
  return 0;
};

var sortMigrationsDesc = function (a, b) {
  if (a.version < b.version)
    return 1;
  if (a.version > b.version)
    return -1;
  return 0;
};

/*
  getMigrations()

  Internal function
  Reads the migration directory for all the migration files.
  It is SYNC out of laziness and simplicity

================================================================= */
var getMigrations = function () {
  migrations = [];
  var migrationFiles = fs.readdirSync(config.migrationDirectory);
  migrationFiles.forEach(function(file) {
    var m = file.split('.');
    var name = m.length >= 3 ? m.slice(2, m.length - 1).join('.') : file;
    if (m[m.length - 1] === 'sql') {
      migrations.push({
        version: Number(m[0]),
        direction: m[1],
        action: m[1],
        filename: file,
        name: name,
        md5: fileChecksum(config.migrationDirectory + "/" + file, config.newline)
      });
    }
  });
};

/*  runQuery
    connects the database driver if it is not currently connected.
    Executes an arbitrary sql query using the common client
================================================================= */
function runQuery (query, cb) {
  if (commonClient.connected) {
    commonClient.runQuery(query, cb);
  } else {
    // connect common client
    commonClient.createConnection(function (err) {
        if (err) cb(err);
        else {
            commonClient.connected = true;
            commonClient.runQuery(query, cb);
        }
    });
  }
}
exports.runQuery = runQuery;

/*  endConnection
    Ends the commonClient's connection to the database
================================================================= */
function endConnection (cb) {
    if (commonClient.connected) {
        commonClient.endConnection(function () {
            commonClient.connected = false;
            cb();
        });
    } else {
        cb();
    }
}
exports.endConnection = endConnection;

/*
  getCurrentVersion(callback)

  Internal & External function
  Gets the current version of the schema from the database.

================================================================= */
var getCurrentVersion = function (callback) {
  runQuery(commonClient.queries.getCurrentVersion, function(err, result) {
    if (err) { // means the table probably doesn't exist yet. To lazy to check.
      console.error('something went wrong getting the Current Version from the ' + config.schemaTable + ' table');
    } else {
      if (result.rows.length > 0) currentVersion = result.rows[0].version;
      else currentVersion = 0;
    }
    callback(err, currentVersion);
  });
};
exports.getCurrentVersion = getCurrentVersion;

/*
 getVersions(callback)

 Internal & External function
 Returns an object with the current applied version of the schema from
 the database and the max version of migration available.

 ================================================================= */
var getVersions = function (callback) {
  var versions = {};
  getMigrations()
  versions.max = Math.max.apply(null, migrations.map(function (migration) { return migration.version; }));
  getCurrentVersion(function(err, version) {
    if (err) {
      if (config.logProgress) {
        console.log('Error in postgrator{isLatestVersion}');
        console.log('Error:' + err)
      }
    } else {
      versions.current = version;
    }
    callback(err, versions);
  });
};
exports.getVersions = getVersions;

/*
  runMigrations(migrations, finishedCallback)

  Internal function
  Runs the migrations in the order provided, using a recursive kind of approach
  For each migration run:
    - the contents of the script is read (sync because I'm lazy)
    - script is run.
      if error, the callback is called and we don't run anything else
      if success, we then add/remove a record from the config.schemaTable to keep track of the migration we just ran
    - if all goes as planned, we run the next migration
    - once all migrations have been run, we call the callback.

================================================================= */
var runMigrations = function (migrations, currentVersion, targetVersion, finishedCallback) {
  var runNext = function (i) {
    var sql = fs.readFileSync((config.migrationDirectory + '/' + migrations[i].filename), 'utf8');
    if (migrations[i].md5Sql) {
      config.logProgress && console.log('verifying checksum of migration ' + migrations[i].filename);
      runQuery(migrations[i].md5Sql, function (err, result) {
        if (err) {
          config.logProgress && console.log('Error in runMigrations() while retrieving existing migrations');
          if (finishedCallback) {
            finishedCallback(err, migrations);
          }
        } else {
          if (result.rows[0].md5 && result.rows[0].md5 !== migrations[i].md5) {
            config.logProgress && console.log('Error in runMigrations() while verifying checksums of existing migrations');
            if (finishedCallback) {
              finishedCallback(new Error("For migration [" + migrations[i].version + "], expected MD5 checksum [" + migrations[i].md5 + "] but got [" + result.rows[0].md5 + "]"), migrations);
            }
          } else {
            i = i + 1;
            if (i < migrations.length) {
              runNext(i);
            } else {
              if (finishedCallback) {
                finishedCallback(null, migrations);
              }
            }
          }
        }
      });
    } else {
      config.logProgress && console.log('running ' + migrations[i].filename);
      runQuery(sql, function (err, result) {
        if (err) {
          config.logProgress && console.log('Error in runMigrations()');
          if (finishedCallback) {
            finishedCallback(err, migrations);
          }
        } else {
          // migration ran successfully
          // add version to config.schemaTable table.
          runQuery(migrations[i].schemaVersionSQL, function (err, result) {
            if (err) {
              // SQL to update config.schemaTable failed.
              if (config.logProgress) {
                console.log('error updating the ' + config.schemaTable + ' table');
                console.log(err);
              }
            } else {
              // config.schemaTable successfully recorded.
              // move on to next migration
              i = i + 1;
              if (i < migrations.length) {
                runNext(i);
              } else {
                // We are done running the migrations.
                // run the finished callback if supplied.
                if (finishedCallback) {
                  finishedCallback(null, migrations);
                }
              }
            }
          });
        }
      });
    }
  };
  runNext(0);
};

/*
  .getRelevantMigrations(currentVersion, targetVersion)

  returns an array of relevant migrations based on the target and current version passed.
  returned array is sorted in the order it needs to be run

================================================================= */
var getRelevantMigrations = function (currentVersion, targetVersion) {
  var relevantMigrations = [];
  if (targetVersion >= currentVersion) {
    // we are migrating up
    // get all up migrations > currentVersion and <= targetVersion
    config.logProgress && console.log('migrating up to ' + targetVersion);
    migrations.forEach(function(migration) {
      if (migration.action == 'do' && migration.version > 0 && migration.version <= currentVersion && (config.driver === 'pg' || config.driver === 'pg.js')) {
        migration.md5Sql = 'SELECT md5 FROM ' + config.schemaTable + ' WHERE version = ' + migration.version + ';';
        relevantMigrations.push(migration);
      }
      if (migration.action == 'do' && migration.version > currentVersion && migration.version <= targetVersion) {
        migration.schemaVersionSQL = config.driver === 'pg' || config.driver === 'pg.js' ? "INSERT INTO "+config.schemaTable+" (version, name, md5) VALUES (" + migration.version + ", '" + migration.name + "', '" + migration.md5 + "');" : "INSERT INTO " + config.schemaTable + " (version) VALUES (" + migration.version + ");";
        relevantMigrations.push(migration);
      }
    });
    relevantMigrations = relevantMigrations.sort(sortMigrationsAsc);
  } else if (targetVersion < currentVersion) {
    // we are going to migrate down
    config.logProgress && console.log('migrating down to ' + targetVersion);
    migrations.forEach(function(migration) {
      if (migration.action == 'undo' && migration.version <= currentVersion && migration.version > targetVersion) {
        migration.schemaVersionSQL = 'DELETE FROM ' + config.schemaTable + ' WHERE version = ' + migration.version + ';';
        relevantMigrations.push(migration);
      }
    });
    relevantMigrations = relevantMigrations.sort(sortMigrationsDesc);
  }
  return relevantMigrations;
};

/*
  .migrate(target, callback)

  Main method to move a schema to a particular version.
  A target must be specified, otherwise nothing is run.

  target - version to migrate to as string or number (will be handled as numbers internally)
  callback - callback to run after migrations have finished. function (err, migrations) {}

================================================================= */
function migrate (target, finishedCallback) {
  prep(function(err) {
    if (err) {
      if (finishedCallback) finishedCallback(err);
    }
    getMigrations();
    if (target && target === 'max') {
      targetVersion = Math.max.apply(null, migrations.map(function (migration) { return migration.version; }));
    } else if (target) {
      targetVersion = Number(target);
    }
    getCurrentVersion(function(err, currentVersion) {
      if (err) {
        config.logProgress && console.log('error getting current version');
        if (finishedCallback) finishedCallback(err);
      } else {
        config.logProgress && console.log('version of database is: ' + currentVersion);
        if (targetVersion === undefined) {
          config.logProgress && console.log('no target version supplied - no migrations performed');
        } else {
          var relevantMigrations = getRelevantMigrations(currentVersion, targetVersion);
          if (relevantMigrations.length > 0) {
            runMigrations(relevantMigrations, currentVersion, targetVersion, function(err, migrations) {
              finishedCallback(err, migrations);
            });
          } else {
            if (finishedCallback) finishedCallback(err);
          }
        }
      }
    }); // get current version
  }); // prep
}
exports.migrate = migrate;

/*
  .prep(callback)

  Creates the table required for Postgrator to keep track of which migrations have been run.

  callback - function called after schema version table is built. function (err, results) {}

================================================================= */
function prep (callback) {
  runQuery(commonClient.queries.checkTable, function(err, result) {
    if (err) {
      err.helpfulDescription = 'Prep() table CHECK query Failed';
      callback(err);
    } else {
      if (result.rows && result.rows.length > 0) {
        if (config.driver === 'pg' || config.driver === 'pg.js') {
          // config.schemaTable exists, does it have the md5 column? (PostgreSQL only)
          runQuery("SELECT column_name, data_type, character_maximum_length FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '" + config.schemaTable + "' AND column_name = 'md5';", function (err, result) {
            if (err) {
              err.helpfulDescription = 'Prep() table CHECK MD5 COLUMN query Failed';
              callback(err);
            } else {
              if (!result.rows || result.rows.length === 0) {
                // md5 column doesn't exist, add it
                runQuery("ALTER TABLE " + config.schemaTable + " ADD COLUMN md5 text DEFAULT '';", function (err, result) {
                  if (err) {
                    err.helpfulDescription = 'Prep() table ADD MD5 COLUMN query Failed';
                    callback(err);
                  } else {
                    callback();
                  }
                });
              } else {
                callback();
              }
            }
          });
        } else {
          callback();
        }
      } else {
        config.logProgress && console.log('table ' + config.schemaTable + ' does not exist - creating it.');
        runQuery(commonClient.queries.makeTable, function(err, result) {
          if (err) {
            err.helpfulDescription = 'Prep() table BUILD query Failed';
            callback(err);
          } else {
            callback();
          }
        });
      }
    }
  });
}

/*
 .fileChecksum(filename)

 Calculate checksum of file to detect changes to migrations that have already run.

 filename - calculate MD5 checksum of contents of this file

 ================================================================= */

function fileChecksum (filename, newline) {
  return checksum(fs.readFileSync(filename, 'utf8'), newline);
}

function checksum (str, nl) {
  if (nl) {
    var newline = require('newline');
    config.logProgress && console.log('Converting newline from: ', newline.detect(str), 'to:', nl);
    str = newline.set(str, nl);
  }
  return crypto.createHash('md5').update(str, 'utf8').digest('hex');
}
<commit_msg>Revert "added logProgress config var"<commit_after>/*

	API:

    var postgrator = require('postgrator');

    postgrator.setConfig({
        driver: 'pg', // or pg.js, mysql, mssql, tedious
        migrationDirectory: '',
        logProgress: true,
        schemaTable: '', // default is 'schemaversion'
        host: '',
        database: '',
        username: '',
        password: ''
    });

    postgrator.migrate(version, function (err, migrations) {
        // handle the error, and if you want end the connection
        postgrator.endConnection();
    });

	NOTES:

	If the table specified by config.schemaTable is not present, it will be created automatically!
	If no migration version is supplied, no migration is performed

	THINGS TO IMPLEMENT SOMEDAY (MAYBE)

	postgrator.migrate('max', callback); 	// migrate to the latest migration available

================================================================= */

var fs = require('fs');
var crypto = require('crypto');
var createCommonClient = require('./lib/create-common-client.js');

var commonClient;
var currentVersion;
var targetVersion;
var migrations = []; // array of objects like: {version: n, action: 'do', direction: 'up', filename: '0001.up.sql'}

var config = {};

exports.config = config;

/*  Set Config
================================================================= */
exports.setConfig = function (configuration) {
    config = configuration;
    config.schemaTable = config.schemaTable || 'schemaversion';

    commonClient = createCommonClient(config);
};

/*  Migration Sorting Functions
================================================================= */
var sortMigrationsAsc = function (a,b) {
	if (a.version < b.version)
		return -1;
	if (a.version > b.version)
		return 1;
	return 0;
};

var sortMigrationsDesc = function (a, b) {
	if (a.version < b.version)
		return 1;
	if (a.version > b.version)
		return -1;
	return 0;
};

/*
	getMigrations()

	Internal function
	Reads the migration directory for all the migration files.
	It is SYNC out of laziness and simplicity

================================================================= */
var getMigrations = function () {
	migrations = [];
	var migrationFiles = fs.readdirSync(config.migrationDirectory);
	migrationFiles.forEach(function(file) {
		var m = file.split('.');
		var name = m.length >= 3 ? m.slice(2, m.length - 1).join('.') : file;
		if (m[m.length - 1] === 'sql') {
			migrations.push({
				version: Number(m[0]),
				direction: m[1],
				action: m[1],
				filename: file,
				name: name,
				md5: fileChecksum(config.migrationDirectory + "/" + file, config.newline)
			});
		}
	});
};

/*  runQuery
    connects the database driver if it is not currently connected.
    Executes an arbitrary sql query using the common client
================================================================= */
function runQuery (query, cb) {
	if (commonClient.connected) {
		commonClient.runQuery(query, cb);
	} else {
		// connect common client
		commonClient.createConnection(function (err) {
		    if (err) cb(err);
		    else {
		        commonClient.connected = true;
		        commonClient.runQuery(query, cb);
		    }
		});
	}
}
exports.runQuery = runQuery;

/*  endConnection
    Ends the commonClient's connection to the database
================================================================= */
function endConnection (cb) {
    if (commonClient.connected) {
        commonClient.endConnection(function () {
            commonClient.connected = false;
            cb();
        });
    } else {
        cb();
    }
}
exports.endConnection = endConnection;

/*
	getCurrentVersion(callback)

	Internal & External function
	Gets the current version of the schema from the database.

================================================================= */
var getCurrentVersion = function (callback) {
	runQuery(commonClient.queries.getCurrentVersion, function(err, result) {
		if (err) { // means the table probably doesn't exist yet. To lazy to check.
			console.error('something went wrong getting the Current Version from the ' + config.schemaTable + ' table');
		} else {
			if (result.rows.length > 0) currentVersion = result.rows[0].version;
			else currentVersion = 0;
		}
		callback(err, currentVersion);
	});
};
exports.getCurrentVersion = getCurrentVersion;

/*
 getVersions(callback)

 Internal & External function
 Returns an object with the current applied version of the schema from
 the database and the max version of migration available.

 ================================================================= */
var getVersions = function (callback) {
	var versions = {};
	getMigrations()
	versions.max = Math.max.apply(null, migrations.map(function (migration) { return migration.version; }));
	getCurrentVersion(function(err, version) {
		if (err) {
			console.log('Error in postgrator{isLatestVersion}');
			console.log('Error:' + err)
		} else {
			versions.current = version;
		}
		callback(err, versions);
	});
};
exports.getVersions = getVersions;

/*
	runMigrations(migrations, finishedCallback)

	Internal function
	Runs the migrations in the order provided, using a recursive kind of approach
	For each migration run:
		- the contents of the script is read (sync because I'm lazy)
		- script is run.
			if error, the callback is called and we don't run anything else
			if success, we then add/remove a record from the config.schemaTable to keep track of the migration we just ran
		- if all goes as planned, we run the next migration
		- once all migrations have been run, we call the callback.

================================================================= */
var runMigrations = function (migrations, currentVersion, targetVersion, finishedCallback) {
	var runNext = function (i) {
		var sql = fs.readFileSync((config.migrationDirectory + '/' + migrations[i].filename), 'utf8');
		if (migrations[i].md5Sql) {
			console.log('verifying checksum of migration ' + migrations[i].filename);
			runQuery(migrations[i].md5Sql, function (err, result) {
				if (err) {
					console.log('Error in runMigrations() while retrieving existing migrations');
					if (finishedCallback) {
						finishedCallback(err, migrations);
					}
				} else {
					if (result.rows[0].md5 && result.rows[0].md5 !== migrations[i].md5) {
						console.log('Error in runMigrations() while verifying checksums of existing migrations');
						if (finishedCallback) {
							finishedCallback(new Error("For migration [" + migrations[i].version + "], expected MD5 checksum [" + migrations[i].md5 + "] but got [" + result.rows[0].md5 + "]"), migrations);
						}
					} else {
						i = i + 1;
						if (i < migrations.length) {
							runNext(i);
						} else {
							if (finishedCallback) {
								finishedCallback(null, migrations);
							}
						}
					}
				}
			});
		} else {
			console.log('running ' + migrations[i].filename);
			runQuery(sql, function (err, result) {
				if (err) {
					console.log('Error in runMigrations()');
					if (finishedCallback) {
						finishedCallback(err, migrations);
					}
				} else {
					// migration ran successfully
					// add version to config.schemaTable table.
					runQuery(migrations[i].schemaVersionSQL, function (err, result) {
						if (err) {
							// SQL to update config.schemaTable failed.
							console.log('error updating the ' + config.schemaTable + ' table');
							console.log(err);
						} else {
							// config.schemaTable successfully recorded.
							// move on to next migration
							i = i + 1;
							if (i < migrations.length) {
								runNext(i);
							} else {
								// We are done running the migrations.
								// run the finished callback if supplied.
								if (finishedCallback) {
									finishedCallback(null, migrations);
								}
							}
						}
					});
				}
			});
		}
	};
	runNext(0);
};

/*
	.getRelevantMigrations(currentVersion, targetVersion)

	returns an array of relevant migrations based on the target and current version passed.
	returned array is sorted in the order it needs to be run

================================================================= */
var getRelevantMigrations = function (currentVersion, targetVersion) {
	var relevantMigrations = [];
	if (targetVersion >= currentVersion) {
		// we are migrating up
		// get all up migrations > currentVersion and <= targetVersion
		console.log('migrating up to ' + targetVersion);
		migrations.forEach(function(migration) {
			if (migration.action == 'do' && migration.version > 0 && migration.version <= currentVersion && (config.driver === 'pg' || config.driver === 'pg.js')) {
				migration.md5Sql = 'SELECT md5 FROM ' + config.schemaTable + ' WHERE version = ' + migration.version + ';';
				relevantMigrations.push(migration);
			}
			if (migration.action == 'do' && migration.version > currentVersion && migration.version <= targetVersion) {
				migration.schemaVersionSQL = config.driver === 'pg' || config.driver === 'pg.js' ? "INSERT INTO "+config.schemaTable+" (version, name, md5) VALUES (" + migration.version + ", '" + migration.name + "', '" + migration.md5 + "');" : "INSERT INTO " + config.schemaTable + " (version) VALUES (" + migration.version + ");";
				relevantMigrations.push(migration);
			}
		});
		relevantMigrations = relevantMigrations.sort(sortMigrationsAsc);
	} else if (targetVersion < currentVersion) {
		// we are going to migrate down
		console.log('migrating down to ' + targetVersion);
		migrations.forEach(function(migration) {
			if (migration.action == 'undo' && migration.version <= currentVersion && migration.version > targetVersion) {
				migration.schemaVersionSQL = 'DELETE FROM ' + config.schemaTable + ' WHERE version = ' + migration.version + ';';
				relevantMigrations.push(migration);
			}
		});
		relevantMigrations = relevantMigrations.sort(sortMigrationsDesc);
	}
	return relevantMigrations;
};

/*
	.migrate(target, callback)

	Main method to move a schema to a particular version.
	A target must be specified, otherwise nothing is run.

	target - version to migrate to as string or number (will be handled as numbers internally)
	callback - callback to run after migrations have finished. function (err, migrations) {}

================================================================= */
function migrate (target, finishedCallback) {
	prep(function(err) {
		if (err) {
			if (finishedCallback) finishedCallback(err);
		}
		getMigrations();
		if (target && target === 'max') {
			targetVersion = Math.max.apply(null, migrations.map(function (migration) { return migration.version; }));
		} else if (target) {
			targetVersion = Number(target);
		}
		getCurrentVersion(function(err, currentVersion) {
			if (err) {
				console.log('error getting current version');
				if (finishedCallback) finishedCallback(err);
			} else {
				console.log('version of database is: ' + currentVersion);
				if (targetVersion === undefined) {
					console.log('no target version supplied - no migrations performed');
				} else {
					var relevantMigrations = getRelevantMigrations(currentVersion, targetVersion);
					if (relevantMigrations.length > 0) {
						runMigrations(relevantMigrations, currentVersion, targetVersion, function(err, migrations) {
							finishedCallback(err, migrations);
						});
					} else {
						if (finishedCallback) finishedCallback(err);
					}
				}
			}
		}); // get current version
	}); // prep
}
exports.migrate = migrate;

/*
	.prep(callback)

	Creates the table required for Postgrator to keep track of which migrations have been run.

	callback - function called after schema version table is built. function (err, results) {}

================================================================= */
function prep (callback) {
	runQuery(commonClient.queries.checkTable, function(err, result) {
		if (err) {
			err.helpfulDescription = 'Prep() table CHECK query Failed';
			callback(err);
		} else {
			if (result.rows && result.rows.length > 0) {
				if (config.driver === 'pg' || config.driver === 'pg.js') {
					// config.schemaTable exists, does it have the md5 column? (PostgreSQL only)
					runQuery("SELECT column_name, data_type, character_maximum_length FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = '" + config.schemaTable + "' AND column_name = 'md5';", function (err, result) {
						if (err) {
							err.helpfulDescription = 'Prep() table CHECK MD5 COLUMN query Failed';
							callback(err);
						} else {
							if (!result.rows || result.rows.length === 0) {
								// md5 column doesn't exist, add it
								runQuery("ALTER TABLE " + config.schemaTable + " ADD COLUMN md5 text DEFAULT '';", function (err, result) {
									if (err) {
										err.helpfulDescription = 'Prep() table ADD MD5 COLUMN query Failed';
										callback(err);
									} else {
										callback();
									}
								});
							} else {
								callback();
							}
						}
					});
				} else {
					callback();
				}
			} else {
				console.log('table ' + config.schemaTable + ' does not exist - creating it.');
				runQuery(commonClient.queries.makeTable, function(err, result) {
					if (err) {
						err.helpfulDescription = 'Prep() table BUILD query Failed';
						callback(err);
					} else {
						callback();
					}
				});
			}
		}
	});
}

/*
 .fileChecksum(filename)

 Calculate checksum of file to detect changes to migrations that have already run.

 filename - calculate MD5 checksum of contents of this file

 ================================================================= */

function fileChecksum (filename, newline) {
	return checksum(fs.readFileSync(filename, 'utf8'), newline);
}

function checksum (str, nl) {
  if (nl) {
    var newline = require('newline');
    console.log('Converting newline from: ', newline.detect(str), 'to:', nl);
    str = newline.set(str, nl);
  }
	return crypto.createHash('md5').update(str, 'utf8').digest('hex');
}
<|endoftext|>


<commit_before>/*jslint node: true */

var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
    'use strict';

    return connect.static(require('path').resolve(dir));
};

// # 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) {
    'use strict';

    require('load-grunt-tasks')(grunt);
    require('time-grunt')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    try {
        yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
    } catch (ignore) {}

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        autoprefixer: {
            options: ['last 1 version'],
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        connect: {
            options: {
                port: 9000,
                // Change this to '0.0.0.0' to access the server from outside.
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jslint: {
            all: {
                src: ['Gruntfile.js',
                    '<%= yeoman.app %>/scripts/{,*/}*.js',
                    '<%= yeoman.test %>/{,*/}*.js'],
                options: {
                    quotmark: true,
                    shebang: true
                }
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
         dist: {}
         },*/
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        compass: {
            dist: {
                options: {
                    sassDir: 'app/styles/sass',
                    cssDir: 'app/styles/css',
                }
            }
        },
        cssmin: {
            // By default, your `index.html` <!-- Usemin Block --> will take care of
            // minification. This option is pre-configured if you do not wish to use
            // Usemin blocks.
            // dist: {
            //   files: {
            //     '<%= yeoman.dist %>/styles/main.css': [
            //       '.tmp/styles/{,*/}*.css',
            //       '<%= yeoman.app %>/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.app %>',
                    src: ['*.html', 'views/*.html'],
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'bower_components/**/*',
                        'images/{,*/}*.{gif,webp}',
                        'styles/fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'copy:styles'
            ],
            test: [
                'copy:styles'
            ],
            dist: [
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        karma: {
            unit: {
                configFile: 'karma.conf.js',
                singleRun: true
            }
        },
        cdnify: {
            dist: {
                html: ['<%= yeoman.dist %>/*.html']
            }
        },
        ngmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>/scripts',
                    src: '*.js',
                    dest: '<%= yeoman.dist %>/scripts'
                }]
            }
        },
        uglify: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/scripts/scripts.js': [
                        '<%= yeoman.dist %>/scripts/scripts.js'
                    ]
                }
            }
        }
    });

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

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'karma'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'copy:dist',
        'cdnify',
        'ngmin',
        'compass',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

    grunt.registerTask('default', [
        'test',
        'build'
    ]);
};
<commit_msg>Add jslint as a test task<commit_after>/*jslint node: true */

var LIVERELOAD_PORT = 35729;
var lrSnippet = require('connect-livereload')({ port: LIVERELOAD_PORT });
var mountFolder = function (connect, dir) {
    'use strict';

    return connect.static(require('path').resolve(dir));
};

// # 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) {
    'use strict';

    require('load-grunt-tasks')(grunt);
    require('time-grunt')(grunt);

    // configurable paths
    var yeomanConfig = {
        app: 'app',
        dist: 'dist'
    };

    try {
        yeomanConfig.app = require('./bower.json').appPath || yeomanConfig.app;
    } catch (ignore) {}

    grunt.initConfig({
        yeoman: yeomanConfig,
        watch: {
            styles: {
                files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: LIVERELOAD_PORT
                },
                files: [
                    '<%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
                    '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
                ]
            }
        },
        autoprefixer: {
            options: ['last 1 version'],
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },
        connect: {
            options: {
                port: 9000,
                // Change this to '0.0.0.0' to access the server from outside.
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function (connect) {
                        return [
                            lrSnippet,
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, yeomanConfig.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, '.tmp'),
                            mountFolder(connect, 'test')
                        ];
                    }
                }
            },
            dist: {
                options: {
                    middleware: function (connect) {
                        return [
                            mountFolder(connect, yeomanConfig.dist)
                        ];
                    }
                }
            }
        },
        open: {
            server: {
                url: 'http://localhost:<%= connect.options.port %>'
            }
        },
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= yeoman.dist %>/*',
                        '!<%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },
        jslint: {
            all: {
                src: ['Gruntfile.js',
                    '<%= yeoman.app %>/scripts/{,*/}*.js',
                    '<%= yeoman.test %>/{,*/}*.js'],
                options: {
                    quotmark: true,
                    shebang: true
                }
            }
        },
        // not used since Uglify task does concat,
        // but still available if needed
        /*concat: {
         dist: {}
         },*/
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
                        '<%= yeoman.dist %>/styles/fonts/*'
                    ]
                }
            }
        },
        useminPrepare: {
            html: '<%= yeoman.app %>/index.html',
            options: {
                dest: '<%= yeoman.dist %>'
            }
        },
        usemin: {
            html: ['<%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
            options: {
                dirs: ['<%= yeoman.dist %>']
            }
        },
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.{png,jpg,jpeg}',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= yeoman.dist %>/images'
                }]
            }
        },
        compass: {
            dist: {
                options: {
                    sassDir: 'app/styles/sass',
                    cssDir: 'app/styles/css',
                }
            }
        },
        cssmin: {
            // By default, your `index.html` <!-- Usemin Block --> will take care of
            // minification. This option is pre-configured if you do not wish to use
            // Usemin blocks.
            // dist: {
            //   files: {
            //     '<%= yeoman.dist %>/styles/main.css': [
            //       '.tmp/styles/{,*/}*.css',
            //       '<%= yeoman.app %>/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.app %>',
                    src: ['*.html', 'views/*.html'],
                    dest: '<%= yeoman.dist %>'
                }]
            }
        },
        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= yeoman.app %>',
                    dest: '<%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'bower_components/**/*',
                        'images/{,*/}*.{gif,webp}',
                        'styles/fonts/*'
                    ]
                }, {
                    expand: true,
                    cwd: '.tmp/images',
                    dest: '<%= yeoman.dist %>/images',
                    src: [
                        'generated/*'
                    ]
                }]
            },
            styles: {
                expand: true,
                cwd: '<%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },
        concurrent: {
            server: [
                'copy:styles'
            ],
            test: [
                'copy:styles'
            ],
            dist: [
                'copy:styles',
                'imagemin',
                'svgmin',
                'htmlmin'
            ]
        },
        karma: {
            unit: {
                configFile: 'karma.conf.js',
                singleRun: true
            }
        },
        cdnify: {
            dist: {
                html: ['<%= yeoman.dist %>/*.html']
            }
        },
        ngmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= yeoman.dist %>/scripts',
                    src: '*.js',
                    dest: '<%= yeoman.dist %>/scripts'
                }]
            }
        },
        uglify: {
            dist: {
                files: {
                    '<%= yeoman.dist %>/scripts/scripts.js': [
                        '<%= yeoman.dist %>/scripts/scripts.js'
                    ]
                }
            }
        }
    });

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

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'open',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'jslint',
        'clean:server',
        'concurrent:test',
        'autoprefixer',
        'connect:test',
        'karma'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'copy:dist',
        'cdnify',
        'ngmin',
        'compass',
        'cssmin',
        'uglify',
        'rev',
        'usemin'
    ]);

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


<commit_before>/*jslint node: true */
'use strict';

var pkg = require('./package.json');
var AV_CONFIG_VERSION = '3.1.0';

//Using exclusion patterns slows down Grunt significantly
//instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**'
//this method is used to create a set of inclusive patterns for all subdirectories
//skipping node_modules, bower_components, dist, and any .dirs
//This enables users to create any directory structure they desire.
var createFolderGlobs = function(fileTypePatterns) {
  fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns];
  var ignore = ['node_modules','bower_components','dist','temp'];
  var fs = require('fs');
  return fs.readdirSync(process.cwd())
          .map(function(file){
            if (ignore.indexOf(file) !== -1 ||
                file.indexOf('.') === 0 ||
                !fs.lstatSync(file).isDirectory()) {
              return null;
            } else {
              return fileTypePatterns.map(function(pattern) {
                return file + '/**/' + pattern;
              });
            }
          })
          .filter(function(patterns){
            return patterns;
          })
          .concat(fileTypePatterns);
};

module.exports = function (grunt) {

  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // custom grunt task to check avConfig.js
  grunt.registerTask('check_config', function() {
    var fs = require('fs');
    var done = this.async();
    grunt.log.ok('Checking avConfig.js...');
    var conf = fs.readFile('avConfig.js', function(err, data) {
        if (err) {
            grunt.log.error('No avConfig.js file found');
            done(false);
        } else {
            var match = data.toString().match(/AV_CONFIG_VERSION = [\'\"]([\w\.]*)[\'\"];/);
            if (!match) {
                grunt.log.error('Invalid avConfig.js version');
            } else {
                var v = match[1];
                if (v === AV_CONFIG_VERSION) {
                    return done();
                } else {
                    grunt.log.error('Invalid avConfig.js version: ' + v);
                }
            }
            done(false);
        }
    });
  });

  // Project configuration.
  grunt.initConfig({
    connect: {
      main: {
        options: {
          port: 9001
        }
      }
    },
    watch: {
      main: {
        options: {
            livereload: true,
            livereloadOnError: false,
            spawn: false
        },
        files: [createFolderGlobs(['*.js','*.less','*.html']),'!_SpecRunner.html','!.grunt'],
        tasks: [] //all the tasks are run dynamically during the watch event handler
      }
    },
    jshint: {
      main: {
        options: {
            jshintrc: '.jshintrc'
        },
        src: createFolderGlobs('*.js')
      }
    },
    clean: {
      before:{
        src:['dist','temp']
      },
      after: {
        src:['temp']
      }
    },
    less: {
      production: {
        options: {
        },
        files: [{
          expand: true,
          src: ['bower_components/avCommon/themes/**/app.less'],
          dest: 'temp/',
          ext: '.css',
        }]
      }
    },
    autoprefixer: {
      options: {
        browsers: ['ie >= 8', 'ff > 4', 'last 8 versions']
      },
      main: {
        src: 'temp/bower_components/avCommon/themes/**/app.css'
      }
    },
    ngtemplates: {
      main: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        src: [createFolderGlobs('*.html'),'!index.html','!_SpecRunner.html'],
        dest: 'temp/templates.js'
      },
      common: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        cwd: 'bower_components/avCommon',
        src: ["avRegistration/**/*.html", "avUi/**/*.html" ],
        dest: 'temp/templates-common.js'
      }
    },
    copy: {
      main: {
        files: [
          {src: ['img/**'], dest: 'dist/'},
          {src: ['img/**'], dest: 'dist/'},
          {src: ['temp_data/**'], dest: 'dist/'},
          {
            expand: true,
            cwd:'bower_components/avCommon/themes',
            src: ['**/*.png'],
            dest: 'dist/themes/',
            ext: '.png',
            extDot: 'first'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/themes/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/themes/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/font-awesome/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          }
          //{src: ['bower_components/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'},
          //{src: ['bower_components/select2/*.png','bower_components/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true},
          //{src: ['bower_components/angular-mocks/angular-mocks.js'], dest: 'dist/'}
        ]
      }
    },
    dom_munger:{
      read: {
        options: {
          read:[
            {selector:'script[class="libnocompat"]',attribute:'src',writeto:'libnocompatjs'},
            {selector:'script[class="lib"]',attribute:'src',writeto:'libjs'},
            {selector:'script[class="app"]',attribute:'src',writeto:'appjs'},
            {selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
          ]
        },
        src: 'index.html'
      },
      update: {
        options: {
          remove: ['script[data-remove!="false"]','link[data-remove!="false"]'],
          append: [
            {selector:'body',html:'<!--[if lte IE 8]><script src="/admin/libcompat-v3.0.1.min.js"></script><![endif]--><!--[if gte IE 9]><script src="/admin/libnocompat-v3.0.1.min.js"></script><![endif]--><!--[if !IE]><!--><script src="/admin/libnocompat-v3.0.1.min.js"></script><!--<![endif]-->'},
            {selector:'body',html:'<!--All the source code of this program under copyright. Take a look at the license details at https://github.com/agoravoting/agora-core-view/blob/master/README.md -->'},
            {selector:'body',html:'<script src="/admin/lib-v3.0.1.min.js"></script>'},
            {selector:'body',html:'<script src="/admin/avConfig-v3.0.1.js"></script>'},
            {selector:'body',html:'<script src="/admin/avThemes-v3.0.1.js"></script>'},
            {selector:'body',html:'<script src="/admin/app-v3.0.1.min.js"></script>'},
            {selector:'body',html:'<script src="/admin/avPlugins-v3.0.1.js"></script>'},
            {selector:'head',html:'<link rel="stylesheet" id="theme" data-base="/admin/" href="/admin/themes/default/app.min.css">'}
          ]
        },
        src:'index.html',
        dest: 'dist/index.html'
      }
    },
    cssmin: {
      main: {
        files: [{
            expand: true,
            cwd:'temp/bower_components/avCommon/themes',
            src: ['**/app.css'],
            dest: 'dist/themes/',
            ext: '.min.css',
            extDot: 'first'
        }]
      },
    },
    concat: {
      main: {
        files: {
          'temp/libcompat.js': [
            'vendor/jquery.compat/jquery-1.11.1.js',
            'vendor/json3/json-v3.3.2.js',
            'vendor/crypto/unsupportedBrowser.js'
          ],
          'temp/libnocompat.js': ['<%= dom_munger.data.libnocompatjs %>'],
          'temp/lib.js': ['<%= dom_munger.data.libjs %>'],
          'temp/app.js': ['<%= dom_munger.data.appjs %>','<%= ngtemplates.main.dest %>','<%= ngtemplates.common.dest %>'],
          'dist/avConfig-v3.0.1.js': ['avConfig.js'],
          'dist/avThemes-v3.0.1.js': ['bower_components/avCommon/dist/avThemes-v3.0.1.js'],
          'dist/avPlugins-v3.0.1.js': ['plugins/**/*.js']
        }
      }
    },
    "merge-json": {
      main: {
        files: {
            "dist/locales/en.json": ["locales/en.json", "plugins/**/locales/en.json", "bower_components/avCommon/locales/en.json"],
            "dist/locales/es.json": ["locales/es.json", "plugins/**/locales/es.json", "bower_components/avCommon/locales/es.json"],
            "dist/locales/gl.json": ["locales/gl.json", "plugins/**/locales/gl.json", "bower_components/avCommon/locales/gl.json"],
            "dist/locales/ca.json": ["locales/ca.json", "plugins/**/locales/ca.json", "bower_components/avCommon/locales/ca.json"]
        }
      }
    },
    ngAnnotate: {
      main: {
        files: {
        'temp/app.js':['temp/app.js'],
        'temp/lib.js': ['temp/lib.js'],
        'temp/libnocompat.js': ['temp/libnocompat.js'],
        'temp/libcompat.js': ['temp/libcompat.js']
        }
      }
    },
    uglify: {
      main: {
        options:{
          mangle: false,
          compress: {},
          beautify: true
        },
        files: {
          'dist/app-v3.0.1.min.js': 'temp/app.js',
          'dist/lib-v3.0.1.min.js': 'temp/lib.js',
          'dist/libnocompat-v3.0.1.min.js': 'temp/libnocompat.js',
          'dist/libcompat-v3.0.1.min.js': 'temp/libcompat.js',
          'dist/avWidgets.min.js': 'avWidgets.js',

          "dist/locales/moment/es.js": "bower_components/moment/lang/es.js",
          "dist/locales/moment/gl.js": "bower_components/moment/lang/gl.js",
          "dist/locales/moment/ca.js": "bower_components/moment/lang/ca.js"
        }
      }
    },
    htmlmin: {
      main: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: false,
          removeEmptyAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        },
        files: {
          'dist/index.html': 'dist/index.html'
        }
      }
    },
    imagemin: {
      main:{
        files: [{
          expand: true, cwd:'dist/',
          src:['**/{*.png,*.jpg}'],
          dest: 'dist/'
        }]
      }
    },
    karma: {
      options: {
        frameworks: ['jasmine'],
        files: [  //this files data is also updated in the watch handler, if updated change there too
          '<%= dom_munger.data.libnocompatjs %>',
          '<%= dom_munger.data.libjs %>',
          'avConfig.js',
          'avThemes.js',
          'avWidgets.js',
          '<%= dom_munger.data.appjs %>',
          '<%= ngtemplates.main.dest %>',
          '<%= ngtemplates.common.dest %>',
          'bower_components/angular-mocks/angular-mocks.js',
          createFolderGlobs('*-spec.js')
        ],
        logLevel:'ERROR',
        reporters:['mocha'],
        autoWatch: false, //watching is handled by grunt-contrib-watch
        singleRun: true
      },
      all_tests: {
        browsers: ['PhantomJS','Chrome','Firefox']
      },
      during_watch: {
        browsers: ['PhantomJS']
      },
    },
    protractor: {
      options: {
        configFile: "node_modules/protractor/referenceConf.js", // Default config file
        keepAlive: true, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
        args: {
        // Arguments passed to the command
      }
    },
    //your_target: {   // Grunt requires at least one target to run so you can simply put 'all: {}' here too.
      all: {
      options: {
        configFile: "e2e.conf.js", // Target-specific config file
        args: {} // Target-specific arguments
      }
    },
  },

  });

  grunt.registerTask('build',['check_config', 'jshint','clean:before','less','autoprefixer','dom_munger','ngtemplates','cssmin','concat','merge-json','ngAnnotate','uglify','copy','htmlmin','imagemin','clean:after']);
  grunt.registerTask('serve', ['dom_munger:read','jshint','connect', 'watch']);
  grunt.registerTask('test',['dom_munger:read','karma:all_tests']);

  grunt.event.on('watch', function(action, filepath) {
    //https://github.com/gruntjs/grunt-contrib-watch/issues/156

    var tasksToRun = [];

    if ((filepath.lastIndexOf('.json') !== -1 && filepath.lastIndexOf('.json') === filepath.length - 5) ||
        (filepath.lastIndexOf('.js') !== -1 && filepath.lastIndexOf('.js') === filepath.length - 3)) {

      //lint the changed js file
      grunt.config('jshint.main.src', filepath);
      tasksToRun.push('jshint');

      //find the appropriate unit t est for the changed file
      var spec = filepath;
      if (filepath.lastIndexOf('-spec.js') === -1 || filepath.lastIndexOf('-spec.js') !== filepath.length - 8) {
        spec = filepath.substring(0,filepath.length - 3) + '-spec.js';
      }

      //if the spec exists then lets run it
      if (grunt.file.exists(spec)) {
        var files = [].concat(grunt.config('dom_munger.data.libnocompatjs'));
        files.concat(grunt.config('dom_munger.data.libjs'));
        files.push('bower_components/angular-mocks/angular-mocks.js');
        files.push('avConfig.js');
        files.push('avThemes.js');
        files.push('avWidgets.js');
        files.concat(grunt.config('dom_munger.data.appjs'));
        files.concat(grunt.config('ngtemplates.main.dest'));
        files.push(spec);
        grunt.config('karma.options.files', files);
        tasksToRun.push('karma:during_watch');
      }
    }

    //if index.html changed, we need to reread the <script> tags so our next run of karma
    //will have the correct environment
    if (filepath === 'index.html') {
      tasksToRun.push('dom_munger:read');
    }

    grunt.config('watch.main.tasks',tasksToRun);

  });

  grunt.loadNpmTasks('grunt-protractor-runner');
};
<commit_msg>Changed version of avConfig.<commit_after>/*jslint node: true */
'use strict';

var pkg = require('./package.json');
var AV_CONFIG_VERSION = '3.1.1';

//Using exclusion patterns slows down Grunt significantly
//instead of creating a set of patterns like '**/*.js' and '!**/node_modules/**'
//this method is used to create a set of inclusive patterns for all subdirectories
//skipping node_modules, bower_components, dist, and any .dirs
//This enables users to create any directory structure they desire.
var createFolderGlobs = function(fileTypePatterns) {
  fileTypePatterns = Array.isArray(fileTypePatterns) ? fileTypePatterns : [fileTypePatterns];
  var ignore = ['node_modules','bower_components','dist','temp'];
  var fs = require('fs');
  return fs.readdirSync(process.cwd())
          .map(function(file){
            if (ignore.indexOf(file) !== -1 ||
                file.indexOf('.') === 0 ||
                !fs.lstatSync(file).isDirectory()) {
              return null;
            } else {
              return fileTypePatterns.map(function(pattern) {
                return file + '/**/' + pattern;
              });
            }
          })
          .filter(function(patterns){
            return patterns;
          })
          .concat(fileTypePatterns);
};

module.exports = function (grunt) {

  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  // custom grunt task to check avConfig.js
  grunt.registerTask('check_config', function() {
    var fs = require('fs');
    var done = this.async();
    grunt.log.ok('Checking avConfig.js...');
    var conf = fs.readFile('avConfig.js', function(err, data) {
        if (err) {
            grunt.log.error('No avConfig.js file found');
            done(false);
        } else {
            var match = data.toString().match(/AV_CONFIG_VERSION = [\'\"]([\w\.]*)[\'\"];/);
            if (!match) {
                grunt.log.error('Invalid avConfig.js version');
            } else {
                var v = match[1];
                if (v === AV_CONFIG_VERSION) {
                    return done();
                } else {
                    grunt.log.error('Invalid avConfig.js version: ' + v);
                }
            }
            done(false);
        }
    });
  });

  // Project configuration.
  grunt.initConfig({
    connect: {
      main: {
        options: {
          port: 9001
        }
      }
    },
    watch: {
      main: {
        options: {
            livereload: true,
            livereloadOnError: false,
            spawn: false
        },
        files: [createFolderGlobs(['*.js','*.less','*.html']),'!_SpecRunner.html','!.grunt'],
        tasks: [] //all the tasks are run dynamically during the watch event handler
      }
    },
    jshint: {
      main: {
        options: {
            jshintrc: '.jshintrc'
        },
        src: createFolderGlobs('*.js')
      }
    },
    clean: {
      before:{
        src:['dist','temp']
      },
      after: {
        src:['temp']
      }
    },
    less: {
      production: {
        options: {
        },
        files: [{
          expand: true,
          src: ['bower_components/avCommon/themes/**/app.less'],
          dest: 'temp/',
          ext: '.css',
        }]
      }
    },
    autoprefixer: {
      options: {
        browsers: ['ie >= 8', 'ff > 4', 'last 8 versions']
      },
      main: {
        src: 'temp/bower_components/avCommon/themes/**/app.css'
      }
    },
    ngtemplates: {
      main: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        src: [createFolderGlobs('*.html'),'!index.html','!_SpecRunner.html'],
        dest: 'temp/templates.js'
      },
      common: {
        options: {
            module: pkg.name,
            htmlmin:'<%= htmlmin.main.options %>'
        },
        cwd: 'bower_components/avCommon',
        src: ["avRegistration/**/*.html", "avUi/**/*.html" ],
        dest: 'temp/templates-common.js'
      }
    },
    copy: {
      main: {
        files: [
          {src: ['img/**'], dest: 'dist/'},
          {src: ['img/**'], dest: 'dist/'},
          {src: ['temp_data/**'], dest: 'dist/'},
          {
            expand: true,
            cwd:'bower_components/avCommon/themes',
            src: ['**/*.png'],
            dest: 'dist/themes/',
            ext: '.png',
            extDot: 'first'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/themes/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/bootstrap/fonts/',
            src: ['**'],
            dest: 'dist/themes/fonts/'
          },
          {
            expand: true,
            cwd: 'bower_components/font-awesome/fonts/',
            src: ['**'],
            dest: 'dist/fonts/'
          }
          //{src: ['bower_components/angular-ui-utils/ui-utils-ieshiv.min.js'], dest: 'dist/'},
          //{src: ['bower_components/select2/*.png','bower_components/select2/*.gif'], dest:'dist/css/',flatten:true,expand:true},
          //{src: ['bower_components/angular-mocks/angular-mocks.js'], dest: 'dist/'}
        ]
      }
    },
    dom_munger:{
      read: {
        options: {
          read:[
            {selector:'script[class="libnocompat"]',attribute:'src',writeto:'libnocompatjs'},
            {selector:'script[class="lib"]',attribute:'src',writeto:'libjs'},
            {selector:'script[class="app"]',attribute:'src',writeto:'appjs'},
            {selector:'link[rel="stylesheet"][data-concat!="false"]',attribute:'href',writeto:'appcss'}
          ]
        },
        src: 'index.html'
      },
      update: {
        options: {
          remove: ['script[data-remove!="false"]','link[data-remove!="false"]'],
          append: [
            {selector:'body',html:'<!--[if lte IE 8]><script src="/admin/libcompat-v3.0.1.min.js"></script><![endif]--><!--[if gte IE 9]><script src="/admin/libnocompat-v3.0.1.min.js"></script><![endif]--><!--[if !IE]><!--><script src="/admin/libnocompat-v3.0.1.min.js"></script><!--<![endif]-->'},
            {selector:'body',html:'<!--All the source code of this program under copyright. Take a look at the license details at https://github.com/agoravoting/agora-core-view/blob/master/README.md -->'},
            {selector:'body',html:'<script src="/admin/lib-v3.0.1.min.js"></script>'},
            {selector:'body',html:'<script src="/admin/avConfig-v3.0.1.js"></script>'},
            {selector:'body',html:'<script src="/admin/avThemes-v3.0.1.js"></script>'},
            {selector:'body',html:'<script src="/admin/app-v3.0.1.min.js"></script>'},
            {selector:'body',html:'<script src="/admin/avPlugins-v3.0.1.js"></script>'},
            {selector:'head',html:'<link rel="stylesheet" id="theme" data-base="/admin/" href="/admin/themes/default/app.min.css">'}
          ]
        },
        src:'index.html',
        dest: 'dist/index.html'
      }
    },
    cssmin: {
      main: {
        files: [{
            expand: true,
            cwd:'temp/bower_components/avCommon/themes',
            src: ['**/app.css'],
            dest: 'dist/themes/',
            ext: '.min.css',
            extDot: 'first'
        }]
      },
    },
    concat: {
      main: {
        files: {
          'temp/libcompat.js': [
            'vendor/jquery.compat/jquery-1.11.1.js',
            'vendor/json3/json-v3.3.2.js',
            'vendor/crypto/unsupportedBrowser.js'
          ],
          'temp/libnocompat.js': ['<%= dom_munger.data.libnocompatjs %>'],
          'temp/lib.js': ['<%= dom_munger.data.libjs %>'],
          'temp/app.js': ['<%= dom_munger.data.appjs %>','<%= ngtemplates.main.dest %>','<%= ngtemplates.common.dest %>'],
          'dist/avConfig-v3.0.1.js': ['avConfig.js'],
          'dist/avThemes-v3.0.1.js': ['bower_components/avCommon/dist/avThemes-v3.0.1.js'],
          'dist/avPlugins-v3.0.1.js': ['plugins/**/*.js']
        }
      }
    },
    "merge-json": {
      main: {
        files: {
            "dist/locales/en.json": ["locales/en.json", "plugins/**/locales/en.json", "bower_components/avCommon/locales/en.json"],
            "dist/locales/es.json": ["locales/es.json", "plugins/**/locales/es.json", "bower_components/avCommon/locales/es.json"],
            "dist/locales/gl.json": ["locales/gl.json", "plugins/**/locales/gl.json", "bower_components/avCommon/locales/gl.json"],
            "dist/locales/ca.json": ["locales/ca.json", "plugins/**/locales/ca.json", "bower_components/avCommon/locales/ca.json"]
        }
      }
    },
    ngAnnotate: {
      main: {
        files: {
        'temp/app.js':['temp/app.js'],
        'temp/lib.js': ['temp/lib.js'],
        'temp/libnocompat.js': ['temp/libnocompat.js'],
        'temp/libcompat.js': ['temp/libcompat.js']
        }
      }
    },
    uglify: {
      main: {
        options:{
          mangle: false,
          compress: {},
          beautify: true
        },
        files: {
          'dist/app-v3.0.1.min.js': 'temp/app.js',
          'dist/lib-v3.0.1.min.js': 'temp/lib.js',
          'dist/libnocompat-v3.0.1.min.js': 'temp/libnocompat.js',
          'dist/libcompat-v3.0.1.min.js': 'temp/libcompat.js',
          'dist/avWidgets.min.js': 'avWidgets.js',

          "dist/locales/moment/es.js": "bower_components/moment/lang/es.js",
          "dist/locales/moment/gl.js": "bower_components/moment/lang/gl.js",
          "dist/locales/moment/ca.js": "bower_components/moment/lang/ca.js"
        }
      }
    },
    htmlmin: {
      main: {
        options: {
          collapseBooleanAttributes: true,
          collapseWhitespace: true,
          removeAttributeQuotes: false,
          removeComments: false,
          removeEmptyAttributes: true,
          removeScriptTypeAttributes: true,
          removeStyleLinkTypeAttributes: true
        },
        files: {
          'dist/index.html': 'dist/index.html'
        }
      }
    },
    imagemin: {
      main:{
        files: [{
          expand: true, cwd:'dist/',
          src:['**/{*.png,*.jpg}'],
          dest: 'dist/'
        }]
      }
    },
    karma: {
      options: {
        frameworks: ['jasmine'],
        files: [  //this files data is also updated in the watch handler, if updated change there too
          '<%= dom_munger.data.libnocompatjs %>',
          '<%= dom_munger.data.libjs %>',
          'avConfig.js',
          'avThemes.js',
          'avWidgets.js',
          '<%= dom_munger.data.appjs %>',
          '<%= ngtemplates.main.dest %>',
          '<%= ngtemplates.common.dest %>',
          'bower_components/angular-mocks/angular-mocks.js',
          createFolderGlobs('*-spec.js')
        ],
        logLevel:'ERROR',
        reporters:['mocha'],
        autoWatch: false, //watching is handled by grunt-contrib-watch
        singleRun: true
      },
      all_tests: {
        browsers: ['PhantomJS','Chrome','Firefox']
      },
      during_watch: {
        browsers: ['PhantomJS']
      },
    },
    protractor: {
      options: {
        configFile: "node_modules/protractor/referenceConf.js", // Default config file
        keepAlive: true, // If false, the grunt process stops when the test fails.
        noColor: false, // If true, protractor will not use colors in its output.
        args: {
        // Arguments passed to the command
      }
    },
    //your_target: {   // Grunt requires at least one target to run so you can simply put 'all: {}' here too.
      all: {
      options: {
        configFile: "e2e.conf.js", // Target-specific config file
        args: {} // Target-specific arguments
      }
    },
  },

  });

  grunt.registerTask('build',['check_config', 'jshint','clean:before','less','autoprefixer','dom_munger','ngtemplates','cssmin','concat','merge-json','ngAnnotate','uglify','copy','htmlmin','imagemin','clean:after']);
  grunt.registerTask('serve', ['dom_munger:read','jshint','connect', 'watch']);
  grunt.registerTask('test',['dom_munger:read','karma:all_tests']);

  grunt.event.on('watch', function(action, filepath) {
    //https://github.com/gruntjs/grunt-contrib-watch/issues/156

    var tasksToRun = [];

    if ((filepath.lastIndexOf('.json') !== -1 && filepath.lastIndexOf('.json') === filepath.length - 5) ||
        (filepath.lastIndexOf('.js') !== -1 && filepath.lastIndexOf('.js') === filepath.length - 3)) {

      //lint the changed js file
      grunt.config('jshint.main.src', filepath);
      tasksToRun.push('jshint');

      //find the appropriate unit t est for the changed file
      var spec = filepath;
      if (filepath.lastIndexOf('-spec.js') === -1 || filepath.lastIndexOf('-spec.js') !== filepath.length - 8) {
        spec = filepath.substring(0,filepath.length - 3) + '-spec.js';
      }

      //if the spec exists then lets run it
      if (grunt.file.exists(spec)) {
        var files = [].concat(grunt.config('dom_munger.data.libnocompatjs'));
        files.concat(grunt.config('dom_munger.data.libjs'));
        files.push('bower_components/angular-mocks/angular-mocks.js');
        files.push('avConfig.js');
        files.push('avThemes.js');
        files.push('avWidgets.js');
        files.concat(grunt.config('dom_munger.data.appjs'));
        files.concat(grunt.config('ngtemplates.main.dest'));
        files.push(spec);
        grunt.config('karma.options.files', files);
        tasksToRun.push('karma:during_watch');
      }
    }

    //if index.html changed, we need to reread the <script> tags so our next run of karma
    //will have the correct environment
    if (filepath === 'index.html') {
      tasksToRun.push('dom_munger:read');
    }

    grunt.config('watch.main.tasks',tasksToRun);

  });

  grunt.loadNpmTasks('grunt-protractor-runner');
};
<|endoftext|>


<commit_before>var buildTmpl = require("./dev/build-templates");

module.exports = function (grunt) {

  // Strip comments from JsHint JSON files.
  var _jshint = function (name) {
    if (!grunt.file.exists(name)) { return "{}"; }
    return JSON.parse(grunt.file.read(name).replace(/\/\/.*\n/g, ""));
  };

  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),

    jshint: {
      client: {
        options: _jshint("dev/jshint-client.json"),
        files: {
          src: [
            "notes/app/js/fixtures/**/*.js",
            "notes/test/js/spec/**/*.js",
            "notes/app/js/app/**/*.js",
            "notes-rest/test/js/spec-rest/**/*.js",
            "notes-rest/app/js/app-rest/**/*.js",
            "chapters/*/test/js/spec/**/*.js",
            "doc/**/*.js"
          ]
        }
      },
      server: {
        options: _jshint("dev/jshint-server.json"),
        files: {
          src: [
            "Gruntfile.js",
            "notes-rest/server.js"
          ]
        }
      }
    },

    jade: {
      docs: {
        options: {
          pretty: true
        },
        files: {
          "index.html": ["doc/index.jade"],
          "todo.html":  ["doc/todo.jade"]
        }
      }
    },

    watch: {
      docs: {
        files: [
          "doc/**/*.jade",
          "**/*.md"
        ],
        tasks: [
          "jade"
        ],
        options: {
          spawn: false,
          atBegin: true
        }
      }
    }
  });

  // Dependencies
  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-jade");
  grunt.loadNpmTasks("grunt-contrib-watch");

  // Internal Tasks.
  grunt.registerTask("build:tmpl", function () {
    var tmpl = buildTmpl("./notes/app/templates.html");
    grunt.file.write(
      "./notes/app/js/app/templates/templates.js",
      tmpl);
  });

  // Wrapper Tasks.
  grunt.registerTask("build",   ["jade:docs"]);
  grunt.registerTask("check",   ["jshint"]);
  grunt.registerTask("default", ["build", "check"]);
};
<commit_msg>Add template building to watch.<commit_after>var buildTmpl = require("./dev/build-templates");

module.exports = function (grunt) {

  // Strip comments from JsHint JSON files.
  var _jshint = function (name) {
    if (!grunt.file.exists(name)) { return "{}"; }
    return JSON.parse(grunt.file.read(name).replace(/\/\/.*\n/g, ""));
  };

  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),

    jshint: {
      client: {
        options: _jshint("dev/jshint-client.json"),
        files: {
          src: [
            "notes/app/js/fixtures/**/*.js",
            "notes/test/js/spec/**/*.js",
            "notes/app/js/app/**/*.js",
            "notes-rest/test/js/spec-rest/**/*.js",
            "notes-rest/app/js/app-rest/**/*.js",
            "chapters/*/test/js/spec/**/*.js",
            "doc/**/*.js"
          ]
        }
      },
      server: {
        options: _jshint("dev/jshint-server.json"),
        files: {
          src: [
            "Gruntfile.js",
            "notes-rest/server.js"
          ]
        }
      }
    },

    jade: {
      docs: {
        options: {
          pretty: true
        },
        files: {
          "index.html": ["doc/index.jade"],
          "todo.html":  ["doc/todo.jade"]
        }
      }
    },

    watch: {
      options: {
        spawn: false,
        atBegin: true
      },
      tmpl: {
        files: [
          "./notes/app/templates.html"
        ],
        tasks: ["build:tmpl"]
      },
      docs: {
        files: [
          "doc/**/*.jade",
          "**/*.md"
        ],
        tasks: ["jade"]
      }
    }
  });

  // Dependencies
  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-jade");
  grunt.loadNpmTasks("grunt-contrib-watch");

  // Internal Tasks.
  grunt.registerTask("build:tmpl", function () {
    var tmpl = buildTmpl("./notes/app/templates.html");
    grunt.file.write(
      "./notes/app/js/app/templates/templates.js",
      tmpl);
  });

  // Wrapper Tasks.
  grunt.registerTask("build",   ["build:tmpl", "jade:docs"]);
  grunt.registerTask("check",   ["jshint"]);
  grunt.registerTask("default", ["build", "check"]);
};
<|endoftext|>


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

    grunt.loadNpmTasks('grunt-contrib-jst');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-typescript');

    grunt.initConfig({
        jst: {
            options: {
                processName: function(filepath) {
                    return filepath.replace(/^views\//, '')
                        .replace(/\.(.*)$/, '')
                        .replace(/\//g, '-');
                }
            },
            compile: {
                files: {
                    'js/download-jst.js' : 'views/download/image-container.ejs',
                    'js/option-jst.js': 'views/option/*.ejs',
                }
            }
        },

        typescript: {
            download: {
                src: ['js/download.ts'],
                dest: 'js/download.js',
                options: { ignoreTypeCheck: true }
            },
            'pick-url': {
                src: ['js/pick-url.ts'],
                dest: 'js/pick-url.js',
                options: { ignoreTypeCheck: true }
            },
            option: {
                src: ['js/options.ts'],
                dest: 'js/options.js',
                options: { ignoreTypeCheck: true }
            }
        },

        watch: {
            jst: {
                files: ['views/**/*.ejs'],
                tasks: ['jst']
            },
            'typescript-download': {
                files: ['js/download.ts'],
                tasks: ['typescript:download']
            },
            'typescript-url': {
                files: ['js/pick-url.ts'],
                tasks: ['typescript:pick-url']
            },
            'typescript-option': {
                files: ['js/options.ts'],
                tasks: ['typescript:option']
            },
        }
    });

    grunt.task.registerTask('default', ['jst', 'typescript', 'watch']);
};
<commit_msg>refine grunt<commit_after>module.exports = function(grunt) {
    'use strict';

    grunt.loadNpmTasks('grunt-contrib-jst');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-typescript');

    grunt.initConfig({
        jst: {
            options: {
                processName: function(filepath) {
                    return filepath.replace(/^views\//, '')
                        .replace(/\.(.*)$/, '')
                        .replace(/\//g, '-');
                }
            },
            compile: {
                files: {
                    'js/download-jst.js' : 'views/download/image-container.ejs',
                    'js/option-jst.js': 'views/option/*.ejs',
                }
            }
        },

        typescript: {
            download: {
                src: ['js/download.ts'],
                dest: 'js/download.js',
                options: { ignoreTypeCheck: true }
            },
            'pick-url': {
                src: ['js/pick-url.ts'],
                dest: 'js/pick-url.js',
                options: { ignoreTypeCheck: true }
            },
            option: {
                src: ['js/options.ts'],
                dest: 'js/options.js',
                options: { ignoreTypeCheck: true }
            },
            background: {
                src: ['js/background.ts'],
                dest: 'js/background.js',
                options: { ignoreTypeCheck: true }
            }
        },

        watch: {
            jst: {
                files: ['views/**/*.ejs'],
                tasks: ['jst']
            },
            'typescript-download': {
                files: ['js/download.ts'],
                tasks: ['typescript:download']
            },
            'typescript-url': {
                files: ['js/pick-url.ts'],
                tasks: ['typescript:pick-url']
            },
            'typescript-option': {
                files: ['js/options.ts'],
                tasks: ['typescript:option']
            },
            'typescript-background': {
                files: ['js/background.ts'],
                tasks: ['typescript:background']
            },
        }
    });

    grunt.task.registerTask('default', ['jst', 'typescript', 'watch']);
};
<|endoftext|>