<commit_before>/// <reference path="../../typings/node/node.d.ts"/>
'use strict';
var exec = require('child_process').exec;
var path = require('path');
var https = require('https')
var unzip = require('unzip');
var Promise = require('lie');
var fs = require('fs');
var url = require('url');
var promiseUtilities = require('./promiseUtilities.js');
var fileNotFoundCode = "ENOENT";

function getEtagFileLocation(depotName, branchName, directoryToInstallIn) {
    return path.join(directoryToInstallIn, "etag-" + depotName + "-" + branchName);
}

function getEtagFromEtagFile(depotName, branchName, directoryToInstallIn) {
    var etagFileLocation = getEtagFileLocation(depotName, branchName, directoryToInstallIn);
    return promiseUtilities.readFilePromise(etagFileLocation)
    .catch(function(err) {
        if (err.code != fileNotFoundCode) {
            return Promise.reject(err);
        } else {
            return Promise.resolve();
        }
    });
}

function returnEtagFromResponse(httpResponse) {
    // The etag value is returned with quotes but when we set the header it adds its
    // own quotes so we need to strip those quotes here
    return httpResponse && httpResponse.headers && httpResponse.headers.etag &&
                httpResponse.headers.etag.substring(1, httpResponse.headers.etag.length - 1);
}

function writeToEtagFile(depotName, branchName, directoryToInstallIn, httpResponse) {
    var etag = returnEtagFromResponse(httpResponse);

    if (etag == null) {
        return Promise.reject("Did not get ETag header, something is wrong because Github always sends one!");
    }

    var etagFileLocation = getEtagFileLocation(depotName, branchName, directoryToInstallIn);
    return promiseUtilities.writeFilePromise(etagFileLocation, etag);
}

function getGitHubZipUrlObject(projectName, depotName, branchName) {
    return url.parse("https://codeload.github.com/" + projectName + "/" + depotName + "/zip/" + branchName);
}

/**
 * This method is a hack because I'm having trouble getting GitHub to respect if-none-match headers. So instead
 * I'm doing a HEAD request and manually checking if the etags match.
 */
function doGitHubEtagsMatch(projectName, depotName, branchName, directoryToInstallIn) {
    return getEtagFromEtagFile(depotName, branchName, directoryToInstallIn)
    .then(function(etagFromFile) {
        if (!etagFromFile) {
            return false;
        }

        return promiseUtilities.httpRequestPromise("HEAD", getGitHubZipUrlObject(projectName, depotName, branchName))
        .then(function(res) {
            var etagFromHeadRequest = returnEtagFromResponse(res);
            return etagFromFile == etagFromHeadRequest;
        });
    });
}

function createGitHubZipResponse(depotName, branchName, directoryToInstallIn, directoryUpdated) {
    return {
        unzipedDirectory: path.join(directoryToInstallIn, depotName + "-" + branchName),
        directoryUpdated: directoryUpdated
    };
}

function installGitHubZip(projectName, depotName, branchName, directoryToInstallIn) {
    var gitHubZipUrlObject = getGitHubZipUrlObject(projectName, depotName, branchName);

    return doGitHubEtagsMatch(projectName, depotName, branchName, directoryToInstallIn)
    .then(function(doTheEtagsMatch) {
        if (doTheEtagsMatch) {
            return createGitHubZipResponse(depotName, branchName, directoryToInstallIn, false);
        }
        return promiseUtilities.httpRequestPromise("GET", gitHubZipUrlObject)
        .then(function(res) {
            return new Promise(function(resolve, reject) {
                res.pipe(unzip.Extract({ path: directoryToInstallIn}))
                .on('close', function() {
                    resolve();
                }).on('error', function(e) {
                   reject("Could not extract zip file " + gitHubZipUrlObject.href + ", error was " + e);
                });
            }).then(function() {
                return writeToEtagFile(depotName, branchName, directoryToInstallIn, res);
            }).then(function() {
                return createGitHubZipResponse(depotName, branchName, directoryToInstallIn, true);
            });
        });
    });
}

function updatePluginXMLJXCoreLocation(thaliCordovaPluginDirectory, jxCoreDirectory) {
    var pluginFileLocation = path.join(thaliCordovaPluginDirectory, "plugin.xml");
    return promiseUtilities.readFilePromise(pluginFileLocation).then(function(data) {
        // BUBUG: This is fragile, a single character change and the replace won't work! We should really do a
        // proper XML parse and output.
        var replaceDependency =
            data.replace("<dependency id=\"io.jxcore.node\" url=\"https://github.com/jxcore/jxcore-cordova\" />",
                         "<dependency id=\"io.jxcore.node\" subdir=\"" + jxCoreDirectory + "\" />");
       return promiseUtilities.writeFilePromise(pluginFileLocation, replaceDependency);
    }).then(function() {
        return thaliCordovaPluginDirectory;
    });
}

function uninstallPluginsIfNecessary(weAddedPluginsFile, appRootDirectory) {
    return promiseUtilities.readFilePromise(weAddedPluginsFile).catch(function(err) {
        if (err.code != fileNotFoundCode) {
            return Promise.reject(err);
        }
        return false;
    }).then(function(doWeNeedToUninstall) {
        if (!doWeNeedToUninstall) {
            return;
        }

        return promiseUtilities.execPromise('cordova plugin remove org.thaliproject.p2p', appRootDirectory);
    })
}

module.exports = function(callBack) {
   //get the app root folder from app/www/jxcore/node_modules/thali
    var appRootDirectory = path.join(__dirname, '../../../../../');
    var thaliDontCheckIn = path.join(appRootDirectory, "thaliDontCheckIn" );

    var jxcoreFolder = path.join(appRootDirectory, 'www/jxcore' );
    if(!(path.basename(jxcoreFolder) == 'jxcore')) {
        callBack('Could not locate JXCore folder. Exiting the thali plugin installation..', null);
    }

    var thaliProjectName = "thaliproject";
    var thaliDepotName = "Thali_CordovaPlugin";
    var thaliBranchName = "story_0_yarong";
    var getThaliCordovaPluginZip = installGitHubZip(thaliProjectName, thaliDepotName, thaliBranchName, thaliDontCheckIn);

    var jxCoreProjectName = "jxcore";
    var jxCoreDepotName = "jxcore-cordova";
    var jxCoreBranchName = "0.0.3-dev";
    var getJxCoreCordovaPluginZip = Promise.resolve(); //installGitHubZip(jxCoreProjectName, jxCoreDepotName, jxCoreBranchName, thaliDontCheckIn);

    Promise.all([getThaliCordovaPluginZip, getJxCoreCordovaPluginZip])
    .then(function(results) {
        var thaliCordovaPluginUnZipResult = results[0];
        //var jxCoreUnZipResult = results[1];

        if (thaliCordovaPluginUnZipResult.directoryUpdated) {//|| jxCoreUnZipResult.directoryUpdated) {
            var weAddedPluginsFile = path.join(thaliDontCheckIn, "weAddedPlugins");
            return uninstallPluginsIfNecessary(weAddedPluginsFile, appRootDirectory)
            .then(function() {
                // return updatePluginXMLJXCoreLocation(thaliCordovaPluginUnZipResult.unzipedDirectory,
                //                                     jxCoreUnZipResult.unzipedDirectory);
                return thaliCordovaPluginUnZipResult.unzipedDirectory;
            }).then(function(thaliCordovaPluginDirectory) {
                return promiseUtilities.execPromise('cordova plugin add ' + thaliCordovaPluginDirectory, appRootDirectory);
            }).then(function() {
                return promiseUtilities.execPromise('jx npm install', path.join(appRootDirectory, "plugins/org.thaliproject.p2p/scripts"));
            }).then(function() {
                return promiseUtilities.writeFilePromise(weAddedPluginsFile, "yes");
            });
        }
    }).then(function() {
        callBack(null, null);
        return;
    }).catch(function(error) {
        callBack(error, null);
        return;
    });
}
<commit_msg>Move over promiseUtilities<commit_after>/// <reference path="../../typings/node/node.d.ts"/>
'use strict';
var exec = require('child_process').exec;
var path = require('path');
var https = require('https')
var unzip = require('unzip');
var Promise = require('lie');
var fs = require('fs');
var url = require('url');
var promiseUtilities = require('./promiseUtilities.js');
var fileNotFoundCode = "ENOENT";

function getEtagFileLocation(depotName, branchName, directoryToInstallIn) {
    return path.join(directoryToInstallIn, "etag-" + depotName + "-" + branchName);
}

function getEtagFromEtagFile(depotName, branchName, directoryToInstallIn) {
    var etagFileLocation = getEtagFileLocation(depotName, branchName, directoryToInstallIn);
    return promiseUtilities.readFilePromise(etagFileLocation)
    .catch(function(err) {
        if (err.code != fileNotFoundCode) {
            return Promise.reject(err);
        } else {
            return Promise.resolve();
        }
    });
}

function returnEtagFromResponse(httpResponse) {
    // The etag value is returned with quotes but when we set the header it adds its
    // own quotes so we need to strip those quotes here
    return httpResponse && httpResponse.headers && httpResponse.headers.etag &&
                httpResponse.headers.etag.substring(1, httpResponse.headers.etag.length - 1);
}

function writeToEtagFile(depotName, branchName, directoryToInstallIn, httpResponse) {
    var etag = returnEtagFromResponse(httpResponse);

    if (etag == null) {
        return Promise.reject("Did not get ETag header, something is wrong because Github always sends one!");
    }

    var etagFileLocation = getEtagFileLocation(depotName, branchName, directoryToInstallIn);
    return promiseUtilities.writeFilePromise(etagFileLocation, etag);
}

function getGitHubZipUrlObject(projectName, depotName, branchName) {
    return url.parse("https://codeload.github.com/" + projectName + "/" + depotName + "/zip/" + branchName);
}

/**
 * This method is a hack because I'm having trouble getting GitHub to respect if-none-match headers. So instead
 * I'm doing a HEAD request and manually checking if the etags match.
 */
function doGitHubEtagsMatch(projectName, depotName, branchName, directoryToInstallIn) {
    return getEtagFromEtagFile(depotName, branchName, directoryToInstallIn)
    .then(function(etagFromFile) {
        if (!etagFromFile) {
            return false;
        }

        return promiseUtilities.httpRequestPromise("HEAD", getGitHubZipUrlObject(projectName, depotName, branchName))
        .then(function(res) {
            var etagFromHeadRequest = returnEtagFromResponse(res);
            return etagFromFile == etagFromHeadRequest;
        });
    });
}

function createGitHubZipResponse(depotName, branchName, directoryToInstallIn, directoryUpdated) {
    return {
        unzipedDirectory: path.join(directoryToInstallIn, depotName + "-" + branchName),
        directoryUpdated: directoryUpdated
    };
}

function installGitHubZip(projectName, depotName, branchName, directoryToInstallIn) {
    var gitHubZipUrlObject = getGitHubZipUrlObject(projectName, depotName, branchName);

    return doGitHubEtagsMatch(projectName, depotName, branchName, directoryToInstallIn)
    .then(function(doTheEtagsMatch) {
        if (doTheEtagsMatch) {
            return createGitHubZipResponse(depotName, branchName, directoryToInstallIn, false);
        }
        return promiseUtilities.httpRequestPromise("GET", gitHubZipUrlObject)
        .then(function(res) {
            return new Promise(function(resolve, reject) {
                res.pipe(unzip.Extract({ path: directoryToInstallIn}))
                .on('close', function() {
                    resolve();
                }).on('error', function(e) {
                   reject("Could not extract zip file " + gitHubZipUrlObject.href + ", error was " + e);
                });
            }).then(function() {
                return writeToEtagFile(depotName, branchName, directoryToInstallIn, res);
            }).then(function() {
                return createGitHubZipResponse(depotName, branchName, directoryToInstallIn, true);
            });
        });
    });
}

function updatePluginXMLJXCoreLocation(thaliCordovaPluginDirectory, jxCoreDirectory) {
    var pluginFileLocation = path.join(thaliCordovaPluginDirectory, "plugin.xml");
    return promiseUtilities.readFilePromise(pluginFileLocation).then(function(data) {
        // BUBUG: This is fragile, a single character change and the replace won't work! We should really do a
        // proper XML parse and output.
        var replaceDependency =
            data.replace("<dependency id=\"io.jxcore.node\" url=\"https://github.com/jxcore/jxcore-cordova\" />",
                         "<dependency id=\"io.jxcore.node\" subdir=\"" + jxCoreDirectory + "\" />");
       return promiseUtilities.writeFilePromise(pluginFileLocation, replaceDependency);
    }).then(function() {
        return thaliCordovaPluginDirectory;
    });
}

function uninstallPluginsIfNecessary(weAddedPluginsFile, appRootDirectory) {
    return promiseUtilities.readFilePromise(weAddedPluginsFile).catch(function(err) {
        if (err.code != fileNotFoundCode) {
            return Promise.reject(err);
        }
        return false;
    }).then(function(doWeNeedToUninstall) {
        if (!doWeNeedToUninstall) {
            return;
        }

        return promiseUtilities.execPromise('cordova plugin remove org.thaliproject.p2p', appRootDirectory);
    })
}

module.exports = function(callBack) {
   //get the app root folder from app/www/jxcore/node_modules/thali
    var appRootDirectory = path.join(__dirname, '../../../../../');
    var thaliDontCheckIn = path.join(appRootDirectory, "thaliDontCheckIn" );
    var appScriptsFolder = path.join(appRootDirectory, "plugins/org.thaliproject.p2p/scripts");

    var jxcoreFolder = path.join(appRootDirectory, 'www/jxcore' );
    if(!(path.basename(jxcoreFolder) == 'jxcore')) {
        callBack('Could not locate JXCore folder. Exiting the thali plugin installation..', null);
    }

    var thaliProjectName = "thaliproject";
    var thaliDepotName = "Thali_CordovaPlugin";
    var thaliBranchName = "story_0_yarong";
    var getThaliCordovaPluginZip = installGitHubZip(thaliProjectName, thaliDepotName, thaliBranchName, thaliDontCheckIn);

    var jxCoreProjectName = "jxcore";
    var jxCoreDepotName = "jxcore-cordova";
    var jxCoreBranchName = "0.0.3-dev";
    var getJxCoreCordovaPluginZip = Promise.resolve(); //installGitHubZip(jxCoreProjectName, jxCoreDepotName, jxCoreBranchName, thaliDontCheckIn);

    Promise.all([getThaliCordovaPluginZip, getJxCoreCordovaPluginZip])
    .then(function(results) {
        var thaliCordovaPluginUnZipResult = results[0];
        //var jxCoreUnZipResult = results[1];

        if (thaliCordovaPluginUnZipResult.directoryUpdated) {//|| jxCoreUnZipResult.directoryUpdated) {
            var weAddedPluginsFile = path.join(thaliDontCheckIn, "weAddedPlugins");
            return uninstallPluginsIfNecessary(weAddedPluginsFile, appRootDirectory)
            .then(function() {
                // return updatePluginXMLJXCoreLocation(thaliCordovaPluginUnZipResult.unzipedDirectory,
                //                                     jxCoreUnZipResult.unzipedDirectory);
                return thaliCordovaPluginUnZipResult.unzipedDirectory;
            }).then(function(thaliCordovaPluginDirectory) {
                return promiseUtilities.execPromise('cordova plugin add ' + thaliCordovaPluginDirectory, appRootDirectory);
            }).then(function() {
                // Unfortunately using a require from the scripts directory to the thali directory doesn't work at run time, the
                // dependency on lie fails. So I'll just copy the file over.
                return promiseUtilities.overwriteFilePromise(path.join(jxcoreFolder, "node_modules/thali/install/promiseUtilities.js"),
                    path.join(appScriptsFolder, 'promiseUtilities.js'));
            }).then(function() {
                return promiseUtilities.execPromise('jx npm install', appScriptsFolder);
            }).then(function() {
                return promiseUtilities.writeFilePromise(weAddedPluginsFile, "yes");
            });
        }
    }).then(function() {
        callBack(null, null);
        return;
    }).catch(function(error) {
        callBack(error, null);
        return;
    });
}
<|endoftext|>


<commit_before>var npm = require('npm');
var moment = require('moment');
var semver = require('semver');
var JSON2 = require('JSON2');

function Package(name, version) {
	this.name = name;
	this.version = version;
	this.deps = {};
	this.expires = moment().add(Package.TTL);
}

Package.TTL = moment.duration({days: 1});

var dependencies = {};

/**
 * Get the dependency graph for a given NPM dependency name and version.
 *
 * Must be executed in `npm.load()` callback.
 *
 * @param depName Package name
 * @param version
 * @param callback
 */
function getDependencyGraph(depName, version, callback) {

	dependencies[depName] = dependencies[depName] || {};

	var dep = dependencies[depName][version];

	if(dep) {

		if(dep.expires > new Date()) {
			callback(null, dep);
			return;
		}

		dep.deps = {};
		dep.expires = moment().add(Package.TTL);

	} else {
		dep = dependencies[depName][version] = new Package(depName, version);
	}

	process.nextTick(function() {

		npm.commands.view([depName + '@' + version, 'dependencies'], function(err, data) {

			if(err) {
				callback(err);
				return;
			}

			var depDeps = data[version] ? data[version].dependencies ? data[version].dependencies : {} : {},
				depDepNames = depDeps ? Object.keys(depDeps) : [];

			// No dependencies?
			if(depDepNames.length === 0) {
				callback(null, dep);
				return;
			}

			var got = 0;

			depDepNames.forEach(function(depDepName) {

				var depDepRange = depDeps[depDepName];

				latestSatisfying(depDepName, depDepRange, function(err, depDepVersion) {

					if(err) {
						callback(err);
						return;
					}

					// There should be a version that satisfies!
					// But...
					// The range could be a tag, or a git repo
					if(!depDepVersion) {

						// Add a dummy package with the range as it's version
						dep.deps[depDepName] = new Package(depDepName, depDepRange);

						got++;

						if(got == depDepNames.length) {
							dependencies[depName][version] = dep;
							callback(null, dep);
						}

					} else {

						getDependencyGraph(depDepName, depDepVersion, function(err, depDep) {

							if(err) {
								callback(err);
								return;
							}

							dep.deps[depDepName] = depDep;

							got++;

							if(got == depDepNames.length) {
								dependencies[depName][version] = dep;
								callback(null, dep);
							}
						});
					}

				}); // npm
			});
		});
	});
}

/**
 * Get the latest version for the passed dependency name that satisfies the passed range.
 *
 * Must be executed in `npm.load()` callback.
 *
 * @param depName
 * @param range
 * @param callback
 */
function latestSatisfying(depName, range, callback) {

	npm.commands.view([depName, 'versions'], function(err, data) {

		if(err) {
			callback(err);
			return;
		}

		if(range == 'latest') {
			range = '';
		}

		// Get the most recent version that satisfies the range
		var version = semver.maxSatisfying(data[Object.keys(data)[0]].versions, range);

		callback(null, version);
	});
}

// Cache of projects we have cached dependencies for
var projects = {};

/**
 * Get dependency graph for a non-NPM project
 *
 * @param name
 * @param version
 * @param deps
 * @param {Function<Error, Object>} callback Second parameter is decycled dependency graph
 * @see https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
 */
module.exports.getProjectDependencyGraph = function(name, version, deps, callback) {

	projects[name] = projects[name] || {};

	var project = projects[name][version];

	if(project) {

		if(project.expires > new Date()) {
			console.log('Using cached project dependency graph', name, version);
			callback(null, JSON2.decycle(project));
			return;
		}

		project.deps = {};
		project.expires = moment().add(Package.TTL);

	} else {
		project = projects[name][version] = new Package(name, version);
	}

	npm.load({}, function(err) {

		if(err) {
			callback(err);
			return;
		}

		var depNames = Object.keys(deps),
			done = 0;

		depNames.forEach(function(depName) {

			var range = deps[depName];

			latestSatisfying(depName, range, function(err, version) {

				if(err) {
					callback(err);
					return;
				}

				// There should be a version that satisfies!
				// But...
				// The range could be a tag, or a git repo
				if(!version) {

					// Add a dummy package with the range as it's version
					project.deps[depName] = new Package(depName, range);

					done++;

					if(done == depNames.length) {
						callback(null, JSON2.decycle(project));
					}

				} else {

					getDependencyGraph(depName, version, function(err, dep) {

						if(err) {
							callback(err);
							return;
						}

						project.deps[depName] = dep;

						done++;

						if(done == depNames.length) {
							callback(null, JSON2.decycle(project));
						}
					});
				}
			});
		});

	});
};

/**
 * Set the TTL for cached packages.
 *
 * @param {moment.duration} duration Time period the packages will be cacched for, expressed as a moment.duration.
 */
module.exports.setCacheDuration = function(duration) {
	Package.TTL = duration;
};<commit_msg>Fixes #22 - More of a work around. JSON2.decycle removes function references from objects so the toJSON function added to the cyclic objects no longer exists, hence the expires property never gets omitted when converted to JSON. https://github.com/douglascrockford/JSON-js/pull/50 would solve the problem but hasn't been pulled/merged yet. For now, manually strip out the expires property from the result.<commit_after>var npm = require('npm');
var moment = require('moment');
var semver = require('semver');
var JSON2 = require('JSON2');

function Package(name, version) {
	this.name = name;
	this.version = version;
	this.deps = {};
	this.expires = moment().add(Package.TTL);
}

Package.TTL = moment.duration({days: 1});

/**
 * Recursively removes the expires property from a decycled Package.
 *
 * Necessary until https://github.com/douglascrockford/JSON-js/pull/50 is pulled or a better solution becomes available.
 *
 * @param {Object} decycledPkg A decycled Package
 * @return {Object} decycledPkg
 */
function deleteExpires(decycledPkg) {

	delete decycledPkg.expires;

	Object.keys(decycledPkg.deps).forEach(function(depName) {
		// Delete expires from this dependency if it isn't a decycle reference
		if(!decycledPkg.deps[depName]['$ref']) {
			deleteExpires(decycledPkg.deps[depName]);
		}
	});

	return decycledPkg;
}

var dependencies = {};

/**
 * Get the dependency graph for a given NPM dependency name and version.
 *
 * Must be executed in `npm.load()` callback.
 *
 * @param depName Package name
 * @param version
 * @param callback
 */
function getDependencyGraph(depName, version, callback) {

	dependencies[depName] = dependencies[depName] || {};

	var dep = dependencies[depName][version];

	if(dep) {

		if(dep.expires > new Date()) {
			callback(null, dep);
			return;
		}

		dep.deps = {};
		dep.expires = moment().add(Package.TTL);

	} else {
		dep = dependencies[depName][version] = new Package(depName, version);
	}

	process.nextTick(function() {

		npm.commands.view([depName + '@' + version, 'dependencies'], function(err, data) {

			if(err) {
				callback(err);
				return;
			}

			var depDeps = data[version] ? data[version].dependencies ? data[version].dependencies : {} : {},
				depDepNames = depDeps ? Object.keys(depDeps) : [];

			// No dependencies?
			if(depDepNames.length === 0) {
				callback(null, dep);
				return;
			}

			var got = 0;

			depDepNames.forEach(function(depDepName) {

				var depDepRange = depDeps[depDepName];

				latestSatisfying(depDepName, depDepRange, function(err, depDepVersion) {

					if(err) {
						callback(err);
						return;
					}

					// There should be a version that satisfies!
					// But...
					// The range could be a tag, or a git repo
					if(!depDepVersion) {

						// Add a dummy package with the range as it's version
						dep.deps[depDepName] = new Package(depDepName, depDepRange);

						got++;

						if(got == depDepNames.length) {
							dependencies[depName][version] = dep;
							callback(null, dep);
						}

					} else {

						getDependencyGraph(depDepName, depDepVersion, function(err, depDep) {

							if(err) {
								callback(err);
								return;
							}

							dep.deps[depDepName] = depDep;

							got++;

							if(got == depDepNames.length) {
								dependencies[depName][version] = dep;
								callback(null, dep);
							}
						});
					}

				}); // npm
			});
		});
	});
}

/**
 * Get the latest version for the passed dependency name that satisfies the passed range.
 *
 * Must be executed in `npm.load()` callback.
 *
 * @param depName
 * @param range
 * @param callback
 */
function latestSatisfying(depName, range, callback) {

	npm.commands.view([depName, 'versions'], function(err, data) {

		if(err) {
			callback(err);
			return;
		}

		if(range == 'latest') {
			range = '';
		}

		// Get the most recent version that satisfies the range
		var version = semver.maxSatisfying(data[Object.keys(data)[0]].versions, range);

		callback(null, version);
	});
}

// Cache of projects we have cached dependencies for
var projects = {};

/**
 * Get dependency graph for a non-NPM project
 *
 * @param name
 * @param version
 * @param deps
 * @param {Function<Error, Object>} callback Second parameter is decycled dependency graph
 * @see https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
 */
module.exports.getProjectDependencyGraph = function(name, version, deps, callback) {

	projects[name] = projects[name] || {};

	var project = projects[name][version];

	if(project) {

		if(project.expires > new Date()) {
			console.log('Using cached project dependency graph', name, version);
			callback(null, deleteExpires(JSON2.decycle(project)));
			return;
		}

		project.deps = {};
		project.expires = moment().add(Package.TTL);

	} else {
		project = projects[name][version] = new Package(name, version);
	}

	npm.load({}, function(err) {

		if(err) {
			callback(err);
			return;
		}

		var depNames = Object.keys(deps),
			done = 0;

		depNames.forEach(function(depName) {

			var range = deps[depName];

			latestSatisfying(depName, range, function(err, version) {

				if(err) {
					callback(err);
					return;
				}

				// There should be a version that satisfies!
				// But...
				// The range could be a tag, or a git repo
				if(!version) {

					// Add a dummy package with the range as it's version
					project.deps[depName] = new Package(depName, range);

					done++;

					if(done == depNames.length) {
						callback(null, JSON2.decycle(project));
					}

				} else {

					getDependencyGraph(depName, version, function(err, dep) {

						if(err) {
							callback(err);
							return;
						}

						project.deps[depName] = dep;

						done++;

						if(done == depNames.length) {
							callback(null, deleteExpires(JSON2.decycle(project)));
						}
					});
				}
			});
		});

	});
};

/**
 * Set the TTL for cached packages.
 *
 * @param {moment.duration} duration Time period the packages will be cacched for, expressed as a moment.duration.
 */
module.exports.setCacheDuration = function(duration) {
	Package.TTL = duration;
};<|endoftext|>


<commit_before>module.exports = function(context) {

  // Variables
  var container = null,
      confirmButton = null,
      table = null,
      dirty = false,
      rowIndex = {};

  // API functions
  function init(selection, cb) {
    container = selection;
    confirmButton = cb;
    table = new Handsontable(container.node(), {
      rowHeaders: true,
      stretchH: 'all',
      autoColumnSize: false,
      contextMenu: {
        items: {
          'col_left': {
            callback: function(key, selection) {
              var addedColumn = selection.start.col;
              this.alter('insert_col', addedColumn);
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newColumnMsg);
              if (columnName) {
                headers[addedColumn] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
            }
          },
          'col_right': {
            callback: function(key, selection) {
              var addedColumn = selection.start.col + 1;
              this.alter('insert_col', addedColumn);
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newColumnMsg);
              if (columnName) {
                headers[addedColumn] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
            }
          },
          'rename_column': {
            name: context.texts.renameColumn,
            callback: function(key, selection) {
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newNameColumnMsg + ' ' + headers[selection.start.col]);
              if (columnName) {
                headers[selection.start.col] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
              onDataChanged();
            },
            disabled: function() {
              var selected = this.getSelected();

              if (!selected || selected[0] < 0) {
                return true;
              }

              var entireRowSelection = [selected[0], 0, selected[0], this.countCols() - 1];
              var rowSelected = entireRowSelection.join(',') == selected.join(',');

              return selected[1] < 0 || this.countCols() >= this.getSettings().maxCols || rowSelected;
            },
          },
          'clear_column': {},
          'remove_col': {},
          'undo': {},
          'redo': {},
          'make_read_only': {}
        }
      }
    });
    Handsontable.hooks.add('afterChange', onDataChanged, table);
  }

  function show() {
    container.style({display: 'block'});
    updateTable();
  }

  function hide() {
    confirmChanges();
    container.style({display: 'none'});
  }

  function destroy() {
    Handsontable.hooks.remove('afterChange', onDataChanged, table);
    table.destroy();
    table = null;
    confirmButton = null;
    container = null;
  }

  function getTitle() {
    return "table";
  }

  function confirmChanges() {
    if (isDirty()) {
      var geojson = context.data.get('map'),
          tableData = table.getData(),
          headers = table.getSettings().colHeaders;

      geojson.features.forEach(function(feature, i) {
        var row = tableData[i];
        row.forEach(function(col, j) {
          feature.properties[headers[j]] = col;
        });
      });

      context.data.set('map', geojson);
      markClean();
    }
  }

  function updateTable() {
    var rawData = context.data.get('map'),
        data = rawData.features.map(function(f) { return f.properties }),
        headers = getHeaders(data),
        tableData = [];

    rowIndex = {};

    data.forEach(function(it, i) {
      var row = headers.map(function(header) { return it[header] || '' });
      tableData.push(row);
      rowIndex[it['SHEET']] = i;
    });

    table.updateSettings({
      colHeaders: headers,
      data: tableData
    });
    markClean();
  }

  function getHeaders(data) {
    var headers = new Set();
    data.forEach(function(it) {
      for (var key in it) {
        headers.add(key);
      }
    });
    if (headers.size == 0) {
      return ['TITLE', 'SCALE', 'SHEET'];
    } else {
      return Array.from(headers);
    }
  }

  function selectLayer(layer) {
    table.selectCell(rowIndex[layer], 0);
  }

  function onDataChanged() {
    markDirty();
  }

  function markDirty() {
    dirty = true;
    confirmButton.classed('disabled', false);
  }

  function markClean() {
    dirty = false;
    confirmButton.classed('disabled', true);
  }

  function isDirty() {
    return dirty;
  }

  return {
    init: init,
    show: show,
    hide: hide,
    destroy: destroy,
    getTitle: getTitle,
    confirmChanges: confirmChanges,
    selectLayer: selectLayer
  };
};
<commit_msg>Fix null value when new column is added.<commit_after>module.exports = function(context) {

  // Variables
  var container = null,
      confirmButton = null,
      table = null,
      dirty = false,
      rowIndex = {};

  // API functions
  function init(selection, cb) {
    container = selection;
    confirmButton = cb;
    table = new Handsontable(container.node(), {
      rowHeaders: true,
      stretchH: 'all',
      autoColumnSize: false,
      contextMenu: {
        items: {
          'col_left': {
            callback: function(key, selection) {
              var addedColumn = selection.start.col;
              this.alter('insert_col', addedColumn);
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newColumnMsg);
              if (columnName) {
                headers[addedColumn] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
            }
          },
          'col_right': {
            callback: function(key, selection) {
              var addedColumn = selection.start.col + 1;
              this.alter('insert_col', addedColumn);
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newColumnMsg);
              if (columnName) {
                headers[addedColumn] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
            }
          },
          'rename_column': {
            name: context.texts.renameColumn,
            callback: function(key, selection) {
              var headers = this.getSettings().colHeaders;
              var columnName = window.prompt(context.texts.newNameColumnMsg + ' ' + headers[selection.start.col]);
              if (columnName) {
                headers[selection.start.col] = columnName;
              }
              this.updateSettings({ colHeaders: headers });
              onDataChanged();
            },
            disabled: function() {
              var selected = this.getSelected();

              if (!selected || selected[0] < 0) {
                return true;
              }

              var entireRowSelection = [selected[0], 0, selected[0], this.countCols() - 1];
              var rowSelected = entireRowSelection.join(',') == selected.join(',');

              return selected[1] < 0 || this.countCols() >= this.getSettings().maxCols || rowSelected;
            },
          },
          'clear_column': {},
          'remove_col': {},
          'undo': {},
          'redo': {},
          'make_read_only': {}
        }
      }
    });
    Handsontable.hooks.add('afterChange', onDataChanged, table);
  }

  function show() {
    container.style({display: 'block'});
    updateTable();
  }

  function hide() {
    confirmChanges();
    container.style({display: 'none'});
  }

  function destroy() {
    Handsontable.hooks.remove('afterChange', onDataChanged, table);
    table.destroy();
    table = null;
    confirmButton = null;
    container = null;
  }

  function getTitle() {
    return "table";
  }

  function confirmChanges() {
    if (isDirty()) {
      var geojson = context.data.get('map'),
          tableData = table.getData(),
          headers = table.getSettings().colHeaders;

      geojson.features.forEach(function(feature, i) {
        var row = tableData[i];
        row.forEach(function(col, j) {
          if (col === null) {
            col = '';
          }
          feature.properties[headers[j]] = col;
        });
      });

      context.data.set('map', geojson);
      markClean();
    }
  }

  function updateTable() {
    var rawData = context.data.get('map'),
        data = rawData.features.map(function(f) { return f.properties }),
        headers = getHeaders(data),
        tableData = [];

    rowIndex = {};

    data.forEach(function(it, i) {
      var row = headers.map(function(header) { return it[header] || '' });
      tableData.push(row);
      rowIndex[it['SHEET']] = i;
    });

    table.updateSettings({
      colHeaders: headers,
      data: tableData
    });
    markClean();
  }

  function getHeaders(data) {
    var headers = new Set();
    data.forEach(function(it) {
      for (var key in it) {
        headers.add(key);
      }
    });
    if (headers.size == 0) {
      return ['TITLE', 'SCALE', 'SHEET'];
    } else {
      return Array.from(headers);
    }
  }

  function selectLayer(layer) {
    table.selectCell(rowIndex[layer], 0);
  }

  function onDataChanged() {
    markDirty();
  }

  function markDirty() {
    dirty = true;
    confirmButton.classed('disabled', false);
  }

  function markClean() {
    dirty = false;
    confirmButton.classed('disabled', true);
  }

  function isDirty() {
    return dirty;
  }

  return {
    init: init,
    show: show,
    hide: hide,
    destroy: destroy,
    getTitle: getTitle,
    confirmChanges: confirmChanges,
    selectLayer: selectLayer
  };
};
<|endoftext|>


<commit_before>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
        ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' + "\n" +
        ' * License: <%= _.pluck(pkg.licenses, "type").join(", ") %> (<%= _.pluck(pkg.licenses, "url").join(", ") %>)' + "\n" +
        ' */'
    },
    shell: {
      debug_ios: {
        command: 'cordova build ios && cordova emulate ios',
        stdout: true
      },
      debug_android: {
        command: 'cordova build android && cordova emulate android',
        stdout: true
      }
    },
    lint: {
      files: ['grunt.js', 'src/**/*.js']
    },
    concat: {
      dist: {
        src: [
          '<banner:meta.banner>',
          'src/models/*.js',
          'src/collections/*.js',
          'src/views/*.js',
          'src/app.js'
        ],
        dest: 'www/js/<%= pkg.name %>.js'
      }
    },
    min: {
      dist: {
        src: [
          '<banner:meta.banner>',
          '<config:concat.dist.dest>'
        ],
        dest: 'www/js/<%= pkg.name %>.min.js'
      }
    },
    compass: {
      dev: {
        src: 'www/css/scss',
        dest: 'www/css/themes',
        outputstyle: 'expanded',
        linecomments: true,
        require: [
          'www/components/compass-recipes/lib/compass-recipes'
        ]
      },
      prod: {
        src: 'www/css/scss',
        dest: 'www/css/themes/min',
        outputstyle: 'compressed',
        linecomments: false,
        forcecompile: true,
        require: [
          'www/components/compass-recipes/lib/compass-recipes'
        ]
      }
    },
    jasmine : {
      specs : 'www/spec/**/*.js',
      template : 'www/spec/SpecRunner.html'
    },
    'jasmine-server' : {
      browser : true
    },
    watch: {
      files: [
        '<config:lint.files>',
        'www/spec/**/*.js',
        'www/css/**/*.scss'
      ],
      tasks: 'lint concat jasmine compass:dev'
    },
    jshint: {
      options: {
      eqeqeq: false,
      laxbreak: true,
      undef: true,
      newcap: true,
      noarg: true,
      strict: false,
      trailing: true,
      onecase: true,
      boss: true,
      eqnull: true,
      onevar: false,
      evil: true,
      regexdash: true,
      browser: true,
      wsh: true,
      sub: true
    },
      globals: {}
    },
    uglify: {}
  });

  grunt.loadNpmTasks('grunt-compass');
  grunt.loadNpmTasks('grunt-jasmine-runner');
  grunt.loadNpmTasks('grunt-shell');

  // Default task.
  grunt.registerTask('default', 'lint concat compass:dev jasmine');
  // Custom tasks
  grunt.registerTask('test', 'lint concat jasmine');
  grunt.registerTask('debug_ios', 'lint concat jasmine compass:dev shell:debug_ios');
  grunt.registerTask('debug_android', 'lint concat jasmine compass:dev shell:debug_android');

};
<commit_msg>Script include order matters, changes grunt concat to use file names instead of wildcards<commit_after>/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
        '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
        ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;' + "\n" +
        ' * License: <%= _.pluck(pkg.licenses, "type").join(", ") %> (<%= _.pluck(pkg.licenses, "url").join(", ") %>)' + "\n" +
        ' */'
    },
    shell: {
      debug_ios: {
        command: 'cordova build ios && cordova emulate ios',
        stdout: true
      },
      debug_android: {
        command: 'cordova build android && cordova emulate android',
        stdout: true
      }
    },
    lint: {
      files: ['grunt.js', 'src/**/*.js']
    },
    concat: {
      dist: {
        src: [
          '<banner:meta.banner>',
          'src/models/AccountModel.js',
          'src/models/FileModel.js',
          'src/models/FolderModel.js',
          'src/models/DeviceModel.js',
          'src/models/SpiderOakFolderModel.js',
          'src/models/MyShareRoomsModel.js',
          'src/collections/FoldersCollection.js',
          'src/collections/FilesCollection.js',
          'src/collections/DevicesCollection.js',
          'src/views/FolderView.js',
          'src/views/LoginView.js',
          'src/views/MainView.js',
          'src/views/MenuSheetView.js',
          'src/app.js'
        ],
        dest: 'www/js/<%= pkg.name %>.js'
      }
    },
    min: {
      dist: {
        src: [
          '<banner:meta.banner>',
          '<config:concat.dist.dest>'
        ],
        dest: 'www/js/<%= pkg.name %>.min.js'
      }
    },
    compass: {
      dev: {
        src: 'www/css/scss',
        dest: 'www/css/themes',
        outputstyle: 'expanded',
        linecomments: true,
        require: [
          'www/components/compass-recipes/lib/compass-recipes'
        ]
      },
      prod: {
        src: 'www/css/scss',
        dest: 'www/css/themes/min',
        outputstyle: 'compressed',
        linecomments: false,
        forcecompile: true,
        require: [
          'www/components/compass-recipes/lib/compass-recipes'
        ]
      }
    },
    jasmine : {
      specs : 'www/spec/**/*.js',
      template : 'www/spec/SpecRunner.html'
    },
    'jasmine-server' : {
      browser : true
    },
    watch: {
      files: [
        '<config:lint.files>',
        'www/spec/**/*.js',
        'www/css/**/*.scss'
      ],
      tasks: 'lint concat jasmine compass:dev'
    },
    jshint: {
      options: {
      eqeqeq: false,
      laxbreak: true,
      undef: true,
      newcap: true,
      noarg: true,
      strict: false,
      trailing: true,
      onecase: true,
      boss: true,
      eqnull: true,
      onevar: false,
      evil: true,
      regexdash: true,
      browser: true,
      wsh: true,
      sub: true
    },
      globals: {}
    },
    uglify: {}
  });

  grunt.loadNpmTasks('grunt-compass');
  grunt.loadNpmTasks('grunt-jasmine-runner');
  grunt.loadNpmTasks('grunt-shell');

  // Default task.
  grunt.registerTask('default', 'lint concat compass:dev jasmine');
  // Custom tasks
  grunt.registerTask('test', 'lint concat jasmine');
  grunt.registerTask('debug_ios', 'lint concat jasmine compass:dev shell:debug_ios');
  grunt.registerTask('debug_android', 'lint concat jasmine compass:dev shell:debug_android');

};
<|endoftext|>


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

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',

    test: {
      files: ['test/**/*.js']
    },

    lint: {
      files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
    },

    watch: {
      files: '<config:lint.files>',
      tasks: 'default'
    },

    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        node: true,
        es5: true
      },
      globals: {
        exports: true
      }
    },

    server: {
      app: {
        src: './lib/server.js',
        port: 8000,
        watch: './lib/letternode.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-hustler');

  // Default task.
  grunt.registerTask('default', 'lint test');

  grunt.registerTask('dev', 'lint test watch');

};<commit_msg>Add all javascript sources to server-watcher.<commit_after>module.exports = function(grunt) {
  "use strict";

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',

    test: {
      files: ['test/**/*.js']
    },

    lint: {
      files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
    },

    watch: {
      files: '<config:lint.files>',
      tasks: 'default'
    },

    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        node: true,
        es5: true
      },
      globals: {
        exports: true
      }
    },

    server: {
      app: {
        src: './lib/server.js',
        port: 8000,
        watch: './lib/**/*.js'
      }
    }
  });

  grunt.loadNpmTasks('grunt-hustler');

  // Default task.
  grunt.registerTask('default', 'lint test');

  grunt.registerTask('dev', 'lint test watch');

};<|endoftext|>


<commit_before>module.exports = function (grunt) {

	grunt.initConfig({
		min: {
			dist: {
				src: ['src/MultiSet.js'],
				dest: 'bin/multiset.js',
			},
		},
	});

	grunt.registerTask('default', 'min');

};
<commit_msg>Adding a copyright banner to the minification output.<commit_after>module.exports = function (grunt) {

	grunt.initConfig({
		meta: {
			version: '1.0',
			banner: '/*! MultiSet.js - v<%= meta.version %> - ' +
				'<%= grunt.template.today("yyyy-mm-dd") %>\n' +
				'* http://www.canvace.com/\n' +
				'* Copyright (c) <%= grunt.template.today("yyyy") %> ' +
				'Canvace Srl */',
		},

		min: {
			dist: {
        			src: ['<banner:meta.banner>',
				      '<file_strip_banner:src/MultiSet.js>'],
				dest: 'bin/multiset.js',
			},
		},
	});

	grunt.registerTask('default', 'min');

};
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn;
var fs= require('fs');

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    test: {
      files: ['test/**/*.js']
    },
    lint: {
      files: [
        'grunt.js',
        'lib/**/*.js',
        'test/**/*.js',
        'blog/lib/**/*.js',
        'blog/test/**/*.js',
        'planner/lib/**/*.js',
        'planner/test/**/*.js'
      ]
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        node: true
      },
      globals: {
        exports: true
      }
    },
    soy: {
      root: {
        outputPathFormat: 'public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'lib/',
        src: ['lib/**/*.soy']
      },
      planner: {
        outputPathFormat: 'planner/public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'planner/lib/',
        src: ['planner/lib/**/*.soy']
      },
      blog: {
        outputPathFormat: 'blog/public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'blog/lib/',
        src: ['blog/lib/**/*.soy']
      }
    },
    less : {
      root : {
        files: { "public/*.css": "lib/**/*.less" }
      },
      blog : {
        files: { "blog/public/*.css": "blog/lib/**/*.less" }
      },
      planner : {
        files: { "planner/public/*.css": "planner/lib/**/*.less" }
      }
    },
    browserify: {
      all : {
        src : ['./blog/lib/markdown'],
        dest : 'blog/public/js/markdown.js'
      }
    },
    clean : {
      soyRoot : 'public/js/soy/',
      soyPlanner : 'planner/public/js/soy',
      soyBlog : 'blog/public/js/soy'
    },
    watch : {
      soy : {
        files : [
          '<config:soy.root.src>',
          '<config:soy.blog.src>',
          '<config:soy.planner.src>'
        ],
        tasks : 'soy'
      },
      less : {
        files : [
          "lib/**/*.less",
          "blog/lib/**/*.less",
          "planner/lib/**/*.less"
        ],
        tasks : 'less'
      },
      lint : {
        files: '<config:lint.files>',
        tasks: 'default'
      }
    },
    'heroku-deploy' : {
      production : {}
    }
  });

  grunt.loadNpmTasks('grunt-soy');
  grunt.loadNpmTasks('grunt-clean');
  grunt.loadNpmTasks('grunt-heroku-deploy');
  grunt.loadNpmTasks('grunt-contrib-less');

  grunt.registerMultiTask('browserify', 'Run browserify on a file', function() {
    //var inputs = grunt.file.expandFiles(this.file.src);
    var inputs = grunt.config(['browserify', this.target, 'src']);
    var output = grunt.config(['browserify', this.target, 'dest']);
    fs.writeFile(output, require('browserify')({
      exports: ['require'],
      require: inputs
    }).bundle(), this.async());
  });

  grunt.registerTask('run', 'Run noiregrets.com locally', function() {
    require('./web');
    this.async();
  });

  grunt.registerTask('devmode', 'Set NODE_ENV=development', function() {
    process.env.NODE_ENV='development';
  });

  // Default task.
  grunt.registerTask('default', 'clean lint soy less browserify test devmode run');

};<commit_msg>Try the grunt buildpack<commit_after>var spawn = require('child_process').spawn;
var fs= require('fs');

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: '<json:package.json>',
    test: {
      files: ['test/**/*.js']
    },
    lint: {
      files: [
        'grunt.js',
        'lib/**/*.js',
        'test/**/*.js',
        'blog/lib/**/*.js',
        'blog/test/**/*.js',
        'planner/lib/**/*.js',
        'planner/test/**/*.js'
      ]
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        boss: true,
        eqnull: true,
        node: true
      },
      globals: {
        exports: true
      }
    },
    soy: {
      root: {
        outputPathFormat: 'public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'lib/',
        src: ['lib/**/*.soy']
      },
      planner: {
        outputPathFormat: 'planner/public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'planner/lib/',
        src: ['planner/lib/**/*.soy']
      },
      blog: {
        outputPathFormat: 'blog/public/js/soy/{INPUT_DIRECTORY}/{INPUT_FILE_NAME_NO_EXT}.js',
        inputPrefix: 'blog/lib/',
        src: ['blog/lib/**/*.soy']
      }
    },
    less : {
      root : {
        files: { "public/*.css": "lib/**/*.less" }
      },
      blog : {
        files: { "blog/public/*.css": "blog/lib/**/*.less" }
      },
      planner : {
        files: { "planner/public/*.css": "planner/lib/**/*.less" }
      }
    },
    browserify: {
      all : {
        src : ['./blog/lib/markdown'],
        dest : 'blog/public/js/markdown.js'
      }
    },
    clean : {
      soyRoot : 'public/js/soy/',
      soyPlanner : 'planner/public/js/soy',
      soyBlog : 'blog/public/js/soy'
    },
    watch : {
      soy : {
        files : [
          '<config:soy.root.src>',
          '<config:soy.blog.src>',
          '<config:soy.planner.src>'
        ],
        tasks : 'soy'
      },
      less : {
        files : [
          "lib/**/*.less",
          "blog/lib/**/*.less",
          "planner/lib/**/*.less"
        ],
        tasks : 'less'
      },
      lint : {
        files: '<config:lint.files>',
        tasks: 'default'
      }
    },
    'heroku-deploy' : {
      production : {}
    }
  });

  grunt.loadNpmTasks('grunt-soy');
  grunt.loadNpmTasks('grunt-clean');
  grunt.loadNpmTasks('grunt-heroku-deploy');
  grunt.loadNpmTasks('grunt-contrib-less');

  grunt.registerMultiTask('browserify', 'Run browserify on a file', function() {
    //var inputs = grunt.file.expandFiles(this.file.src);
    var inputs = grunt.config(['browserify', this.target, 'src']);
    var output = grunt.config(['browserify', this.target, 'dest']);
    fs.writeFile(output, require('browserify')({
      exports: ['require'],
      require: inputs
    }).bundle(), this.async());
  });

  grunt.registerTask('run', 'Run noiregrets.com locally', function() {
    require('./web');
    this.async();
  });

  grunt.registerTask('devmode', 'Set NODE_ENV=development', function() {
    process.env.NODE_ENV='development';
  });

  // Default task.
  grunt.registerTask('default', 'clean lint soy less browserify test devmode run');
  grunt.registerTask('heroku', 'clean soy less browserify');
};<|endoftext|>


<commit_before>/*global config:true, file:true, task:true, module: true */

var files = {
  0: [
    'libs/jquery-1.6.1.js',
    'libs/underscore.js'
  ],
  1: [
    'libs/jquery.scrollTo-1.4.2.js',
    'libs/jquery.hoverintent.js'
  ],
  2: [
    'src/js/core.js'
  ],
  3: [
    'src/js/models/fact.js',
    'src/js/views/ballooney_thingy.js',
    'src/js/views/balloon.js',
    'src/js/views/prepare.js'
  ],
  4: [
    'src/js/replace.js',
    'src/js/annotate.js',
    'src/js/highlight.js',
    'src/js/scrollto.js',
    'src/js/loaded_message.js',
    'src/js/search.js',
    'src/js/create.js',
    'src/js/modal.js',
    'src/js/lib/indicator.js'
  ],
  5: [
    'src/js/xdm.js'
  ],
  6: [
    'src/js/last.js'
  ]
};

var loaderFiles = {
  0: [
    'libs/easyXDM.js'
  ],
  1: [
    'src/js/loader.js'
  ]
};

module.exports = function(grunt){

  function createFactlinkConcatObject(filesObj) {
    return grunt.utils._.flatten(["<banner>", grunt.utils._(filesObj).map(function(fileGroup) {
      return fileGroup.map(function(file) {
        return "<file_strip_banner:" + file + ">";
      });
    })], true);
  }

  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! \n' +
      ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - \n' +
      '<%= pkg.homepage ? " * " + pkg.homepage + "\n" : "" %>' +
      ' *\n' +
      ' * Date: <%= grunt.template.today("m/d/yyyy") %>\n' +
      ' *\n' +
      ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
      ' */'
    },
    concat: {
      'dist/factlink.js': createFactlinkConcatObject(files),
      'dist/factlink.start_annotating.js': '<file_strip_banner:src/js/chrome/start_annotating.js>',
      'dist/factlink.stop_annotating.js': '<file_strip_banner:src/js/chrome/stop_annotating.js>',
      'dist/factlink.start_highlighting.js': '<file_strip_banner:src/js/chrome/start_highlighting.js>',
      'dist/factlink.stop_highlighting.js': '<file_strip_banner:src/js/chrome/stop_highlighting.js>',
      'dist/easyXDM/easyXDM.js': '<file_strip_banner:libs/easyXDM.js>',
      'dist/loader.js': createFactlinkConcatObject(loaderFiles)
    },
    copy: {
      'dist/server/css/basic.css': 'dist/css/basic.css',

      'dist/server/images/arrow.png': 'src/images/arrow.png',
      'dist/server/images/arrows-bottom.png': 'src/images/arrows-bottom.png',
      'dist/server/images/arrows-top.png': 'src/images/arrows-top.png',
      'dist/server/images/bookmark.gif': 'src/images/bookmark.gif',
      'dist/server/images/logo-small.png': 'src/images/logo-small.png',

      'dist/images/arrow.png': 'src/images/arrow.png',
      'dist/images/arrows-bottom.png': 'src/images/arrows-bottom.png',
      'dist/images/arrows-top.png': 'src/images/arrows-top.png',
      'dist/images/bookmark.gif': 'src/images/bookmark.gif',
      'dist/images/logo-small.png': 'src/images/logo-small.png'
    },
    less: {
      basic: {
        src: 'src/css/basic.less',
        dest: 'dist/css/basic.css',
        options: {
          yuicompress: true
        }
      }
    },
    min: {
      'dist/server/factlink.min.js': ['<banner>', 'dist/factlink.js'],
      'dist/server/factlink.start_annotating.min.js': ['<banner>', 'dist/factlink.start_annotating.js'],
      'dist/server/factlink.stop_annotating.min.js': ['<banner>', 'dist/factlink.stop_annotating.js'],
      'dist/server/factlink.start_highlighting.min.js': ['<banner>', 'dist/factlink.start_highlighting.js'],
      'dist/server/factlink.stop_highlighting.min.js': ['<banner>', 'dist/factlink.stop_highlighting.js'],
      'dist/server/easyXDM/easyXDM.min.js': ['<banner>', 'dist/easyXDM/easyXDM.js'], // for the server
      'dist/easyXDM/easyXDM.min.js': ['<banner>', 'dist/easyXDM/easyXDM.js'],        // for local
      'dist/loader.min.js': ['<banner>', 'dist/loader.js']
    },
    qunit: {
      files: ['test/*.html']
    },
    lint: {
      files: ['grunt.js', 'src/js/**/*.js', 'test/**/*.js']
    },
    watch: {
      files: ['src/js/**/*', 'test/**/*', 'grunt.js'],
      tasks: 'lint qunit concat less copy'
    },
    jshint: {
      options: {
        // Settings
        "passfail"      : false,  // Stop on first error.
        "maxerr"        : 100,    // Maximum error before stopping.

        // Predefined globals whom JSHint will ignore.
        "browser"       : true,   // Standard browser globals e.g. `window`, `document`.

        "node"          : false,
        "rhino"         : false,
        "couch"         : false,
        "wsh"           : false,   // Windows Scripting Host.

        "jquery"        : true,
        "prototypejs"   : false,
        "mootools"      : false,
        "dojo"          : false,

        "predef"        : [  // Custom globals.
          "Factlink", "FactlinkConfig", "escape"
        ],

        // Development.
        "debug"         : false,  // Allow debugger statements e.g. browser breakpoints.
        "devel"         : true,   // Allow developments statements e.g. `console.log();`.

        // ECMAScript 5.
        "es5"           : true,   // Allow ECMAScript 5 syntax.
        "strict"        : false,  // Require `use strict` pragma  in every file.
        "globalstrict"  : false,  // Allow global "use strict" (also enables 'strict').

        // The Good Parts.
        "asi"           : false,  // Tolerate Automatic Semicolon Insertion (no semicolons).
        "laxbreak"      : true,   // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
        "bitwise"       : true,   // Prohibit bitwise operators (&, |, ^, etc.).
        "boss"          : true,   // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
        "curly"         : true,   // Require {} for every new block or scope.
        "eqeqeq"        : true,   // Require triple equals i.e. `===`.
        "eqnull"        : false,  // Tolerate use of `== null`.
        "evil"          : false,  // Tolerate use of `eval`.
        "expr"          : false,  // Tolerate `ExpressionStatement` as Programs.
        "forin"         : false,  // Tolerate `for in` loops without `hasOwnPrototype`.
        "immed"         : false,  // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
        "latedef"       : true,   // Prohipit variable use before definition.
        "loopfunc"      : true,   // Allow functions to be defined within loops.
        "noarg"         : true,   // Prohibit use of `arguments.caller` and `arguments.callee`.
        "regexp"        : false,  // Prohibit `.` and `[^...]` in regular expressions.
        "regexdash"     : false,  // Tolerate unescaped last dash i.e. `[-...]`.
        "scripturl"     : true,   // Tolerate script-targeted URLs.
        "shadow"        : false,  // Allows re-define variables later in code e.g. `var x=1; x=2;`.
        "supernew"      : false,  // Tolerate `new function () { ... };` and `new Object;`.
        "undef"         : true,   // Require all non-global variables be declared before they are used.

        // Personal styling preferences.
        "newcap"        : true,   // Require capitalization of all constructor functions e.g. `new F()`.
        "noempty"       : true,   // Prohibit use of empty blocks.
        "nonew"         : true,   // Prohibit use of constructors for side-effects.
        "nomen"         : false,  // Prohibit use of initial or trailing underbars in names.
        "onevar"        : false,  // Allow only one `var` statement per function.
        "plusplus"      : false,  // Prohibit use of `++` & `--`.
        "sub"           : false,  // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
        "trailing"      : true,   // Prohibit trailing whitespaces.
        "white"         : false,  // Check against strict whitespace and indentation rules.
        "indent"        : 2       // Specify indentation spacing
      }
    },
    uglify: {}
  });

  // Default task.
  grunt.registerTask('default', 'lint qunit concat min copy');
  grunt.registerTask('server', 'concat min copy');

  grunt.registerMultiTask('copy', 'copy files', function () {
    grunt.file.copy(this.data,this.target);
  });

  grunt.loadNpmTasks('grunt-less');
};
<commit_msg>Rename factlink.js -> factlink.core.js and loader.js -> factlink.js<commit_after>/*global config:true, file:true, task:true, module: true */

var files = {
  0: [
    'libs/jquery-1.6.1.js',
    'libs/underscore.js'
  ],
  1: [
    'libs/jquery.scrollTo-1.4.2.js',
    'libs/jquery.hoverintent.js'
  ],
  2: [
    'src/js/core.js'
  ],
  3: [
    'src/js/models/fact.js',
    'src/js/views/ballooney_thingy.js',
    'src/js/views/balloon.js',
    'src/js/views/prepare.js'
  ],
  4: [
    'src/js/replace.js',
    'src/js/annotate.js',
    'src/js/highlight.js',
    'src/js/scrollto.js',
    'src/js/loaded_message.js',
    'src/js/search.js',
    'src/js/create.js',
    'src/js/modal.js',
    'src/js/lib/indicator.js'
  ],
  5: [
    'src/js/xdm.js'
  ],
  6: [
    'src/js/last.js'
  ]
};

var loaderFiles = {
  0: [
    'libs/easyXDM.js'
  ],
  1: [
    'src/js/loader.js'
  ]
};

module.exports = function(grunt){

  function createFactlinkConcatObject(filesObj) {
    return grunt.utils._.flatten(["<banner>", grunt.utils._(filesObj).map(function(fileGroup) {
      return fileGroup.map(function(file) {
        return "<file_strip_banner:" + file + ">";
      });
    })], true);
  }

  grunt.initConfig({
    pkg: '<json:package.json>',
    meta: {
      banner: '/*! \n' +
      ' * <%= pkg.title || pkg.name %> - v<%= pkg.version %> - \n' +
      '<%= pkg.homepage ? " * " + pkg.homepage + "\n" : "" %>' +
      ' *\n' +
      ' * Date: <%= grunt.template.today("m/d/yyyy") %>\n' +
      ' *\n' +
      ' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
      ' */'
    },
    concat: {
      'dist/factlink.core.js': createFactlinkConcatObject(files),
      'dist/factlink.start_annotating.js': '<file_strip_banner:src/js/chrome/start_annotating.js>',
      'dist/factlink.stop_annotating.js': '<file_strip_banner:src/js/chrome/stop_annotating.js>',
      'dist/factlink.start_highlighting.js': '<file_strip_banner:src/js/chrome/start_highlighting.js>',
      'dist/factlink.stop_highlighting.js': '<file_strip_banner:src/js/chrome/stop_highlighting.js>',
      'dist/easyXDM/easyXDM.js': '<file_strip_banner:libs/easyXDM.js>',
      'dist/factlink.js': createFactlinkConcatObject(loaderFiles)
    },
    copy: {
      'dist/server/css/basic.css': 'dist/css/basic.css',

      'dist/server/images/arrow.png': 'src/images/arrow.png',
      'dist/server/images/arrows-bottom.png': 'src/images/arrows-bottom.png',
      'dist/server/images/arrows-top.png': 'src/images/arrows-top.png',
      'dist/server/images/bookmark.gif': 'src/images/bookmark.gif',
      'dist/server/images/logo-small.png': 'src/images/logo-small.png',

      'dist/images/arrow.png': 'src/images/arrow.png',
      'dist/images/arrows-bottom.png': 'src/images/arrows-bottom.png',
      'dist/images/arrows-top.png': 'src/images/arrows-top.png',
      'dist/images/bookmark.gif': 'src/images/bookmark.gif',
      'dist/images/logo-small.png': 'src/images/logo-small.png'
    },
    less: {
      basic: {
        src: 'src/css/basic.less',
        dest: 'dist/css/basic.css',
        options: {
          yuicompress: true
        }
      }
    },
    min: {
      'dist/server/factlink.core.min.js': ['<banner>', 'dist/factlink.core.js'],
      'dist/server/factlink.start_annotating.min.js': ['<banner>', 'dist/factlink.start_annotating.js'],
      'dist/server/factlink.stop_annotating.min.js': ['<banner>', 'dist/factlink.stop_annotating.js'],
      'dist/server/factlink.start_highlighting.min.js': ['<banner>', 'dist/factlink.start_highlighting.js'],
      'dist/server/factlink.stop_highlighting.min.js': ['<banner>', 'dist/factlink.stop_highlighting.js'],
      'dist/server/easyXDM/easyXDM.min.js': ['<banner>', 'dist/easyXDM/easyXDM.js'], // for the server
      'dist/easyXDM/easyXDM.min.js': ['<banner>', 'dist/easyXDM/easyXDM.js'],        // for local
      'dist/factlink.min.js': ['<banner>', 'dist/factlink.js']
    },
    qunit: {
      files: ['test/*.html']
    },
    lint: {
      files: ['grunt.js', 'src/js/**/*.js', 'test/**/*.js']
    },
    watch: {
      files: ['src/js/**/*', 'test/**/*', 'grunt.js'],
      tasks: 'lint qunit concat less copy'
    },
    jshint: {
      options: {
        // Settings
        "passfail"      : false,  // Stop on first error.
        "maxerr"        : 100,    // Maximum error before stopping.

        // Predefined globals whom JSHint will ignore.
        "browser"       : true,   // Standard browser globals e.g. `window`, `document`.

        "node"          : false,
        "rhino"         : false,
        "couch"         : false,
        "wsh"           : false,   // Windows Scripting Host.

        "jquery"        : true,
        "prototypejs"   : false,
        "mootools"      : false,
        "dojo"          : false,

        "predef"        : [  // Custom globals.
          "Factlink", "FactlinkConfig", "escape"
        ],

        // Development.
        "debug"         : false,  // Allow debugger statements e.g. browser breakpoints.
        "devel"         : true,   // Allow developments statements e.g. `console.log();`.

        // ECMAScript 5.
        "es5"           : true,   // Allow ECMAScript 5 syntax.
        "strict"        : false,  // Require `use strict` pragma  in every file.
        "globalstrict"  : false,  // Allow global "use strict" (also enables 'strict').

        // The Good Parts.
        "asi"           : false,  // Tolerate Automatic Semicolon Insertion (no semicolons).
        "laxbreak"      : true,   // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
        "bitwise"       : true,   // Prohibit bitwise operators (&, |, ^, etc.).
        "boss"          : true,   // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
        "curly"         : true,   // Require {} for every new block or scope.
        "eqeqeq"        : true,   // Require triple equals i.e. `===`.
        "eqnull"        : false,  // Tolerate use of `== null`.
        "evil"          : false,  // Tolerate use of `eval`.
        "expr"          : false,  // Tolerate `ExpressionStatement` as Programs.
        "forin"         : false,  // Tolerate `for in` loops without `hasOwnPrototype`.
        "immed"         : false,  // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
        "latedef"       : true,   // Prohipit variable use before definition.
        "loopfunc"      : true,   // Allow functions to be defined within loops.
        "noarg"         : true,   // Prohibit use of `arguments.caller` and `arguments.callee`.
        "regexp"        : false,  // Prohibit `.` and `[^...]` in regular expressions.
        "regexdash"     : false,  // Tolerate unescaped last dash i.e. `[-...]`.
        "scripturl"     : true,   // Tolerate script-targeted URLs.
        "shadow"        : false,  // Allows re-define variables later in code e.g. `var x=1; x=2;`.
        "supernew"      : false,  // Tolerate `new function () { ... };` and `new Object;`.
        "undef"         : true,   // Require all non-global variables be declared before they are used.

        // Personal styling preferences.
        "newcap"        : true,   // Require capitalization of all constructor functions e.g. `new F()`.
        "noempty"       : true,   // Prohibit use of empty blocks.
        "nonew"         : true,   // Prohibit use of constructors for side-effects.
        "nomen"         : false,  // Prohibit use of initial or trailing underbars in names.
        "onevar"        : false,  // Allow only one `var` statement per function.
        "plusplus"      : false,  // Prohibit use of `++` & `--`.
        "sub"           : false,  // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
        "trailing"      : true,   // Prohibit trailing whitespaces.
        "white"         : false,  // Check against strict whitespace and indentation rules.
        "indent"        : 2       // Specify indentation spacing
      }
    },
    uglify: {}
  });

  // Default task.
  grunt.registerTask('default', 'lint qunit concat min copy');
  grunt.registerTask('server', 'concat min copy');

  grunt.registerMultiTask('copy', 'copy files', function () {
    grunt.file.copy(this.data,this.target);
  });

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


<commit_before>module.exports = function (grunt) {

	grunt.initConfig({

		meta: {
			banner: '// ' + grunt.file.read('LICENSE').split("\n")[0]
		},

		coffee: {
			compile: {
				files: {
					'lib/x18n.js': ['lib/observable.coffee', 'lib/x18n.coffee'],
					'spec/spec.js': 'spec/spec.coffee'
				}
			}
		},

		concat: {
			dist: {
				src: ['<banner>', 'lib/x18n.js'],
				dest: 'lib/x18n.js'
			}
		}

	});

	grunt.loadNpmTasks('grunt-contrib-coffee');

};<commit_msg>add release task<commit_after>module.exports = function (grunt) {

	grunt.initConfig({

		meta: {
			banner: '// ' + grunt.file.read('LICENSE').split("\n")[0]
		},

		coffee: {
			compile: {
				files: {
					'lib/x18n.js': ['lib/observable.coffee', 'lib/x18n.coffee'],
					'spec/spec.js': 'spec/spec.coffee'
				}
			}
		},

		concat: {
			dist: {
				src: ['<banner>', 'lib/x18n.js'],
				dest: 'lib/x18n.js'
			}
		}

	});

	grunt.loadNpmTasks('grunt-contrib-coffee');

	grunt.registerTask('release', 'coffee concat');
	grunt.registerTask('default', 'release');

};<|endoftext|>


<commit_before>// prototype implementation for the chatbox
// this allows you to extend and modify features without relying on its inner workings

// there are many other ways of doing this, but i wanted to avoid run-at document-start for this one
// so instead of preventing the original chatbox proto from loading we simply inject our own after it's done

var Chatbox = window.Chatbox = function(tid, params) {
  this.tid = tid;
  this.params = params;
  if (this.params == '' || this.params == undefined) {
    this.params = {
      archives: 0
    };
  }
  this.archives = this.params.archives;
  this.avatar = this.params.avatar;
  this.nolisten = !!parseInt(this.params.archives);
  this.actionsUrl = '/chatbox/actions.forum';
  this.messages = [];
  this.users = [];
  this.listenParams = {};
};
Chatbox.prototype.init = function(noget) {
  this.get();
  if (this.connected) {
    this.initListening();
  }
  this.format();
};
Chatbox.prototype.connect = function() {
  var data = this.params;
  data.method = 'connect';
  data.tid = this.tid;
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    data: data,
    dataType: 'json',
    cache: false,
    success: function(response) {
      self.connected = response.connected;
      self.listenParams = response.listenParams;
      self.refresh(response);
      self.listen();
    }
  });
};
Chatbox.prototype.disconnect = function() {
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    dataType: 'json',
    data: {
      method: 'disconnect',
      tid: this.tid
    },
    cache: false,
    success: function(response) {
      self.connected = response.connected;
      self.refresh(response);
    }
  });
};
Chatbox.prototype.initListening = function() {
  if (this.initTries >= 3) {
    this.disconnect();
  } else {
    var self = this;
    if (this.connected) {
      $.ajax({
        url: this.actionsUrl,
        type: 'get',
        dataType: 'json',
        data: {
          method: 'initListening',
          tid: this.tid,
          archives: this.archives
        },
        cache: false,
        success: function(response) {
          self.listenParams = response;
          self.listen();
        },
        error: function() {
          if (!self.initTries) {
            self.initTries = 1;
          } else {
            self.initTries++;
          }
          self.initListening();
        }
      });
    }
  }
};
Chatbox.prototype.listen = function() {
  var self = this;
  if (this.connected && !this.nolisten) {
    if (!this.listenParams.url || !this.listenParams.lastModified) {
      this.initListening();
      return;
    }
    this.listenParams.tag = Math.floor(Math.random() * 100000);
    $.ajax({
      url: this.listenParams.url,
      timeout: 35000,
      type: 'get',
      data: "tag=" + window.escape(this.listenParams.tag) + "&time=" + window.escape(this.listenParams.lastModified),
      dataType: 'text',
      crossDomain: true,
      cache: false,
      success: function(response, txt, xhr) {
        self.listenParams.lastModified = xhr.getResponseHeader('Last-Modified');
        if (response) {
          self.get();
        }
        self.listen();
      },
      error: function(xhr, status, error) {
        if (xhr.status == 403 || xhr.status == 404 || xhr.status == 0) {
          self.initListening();
        } else {
          self.disconnect();
        }
      }
    })
  }
};
Chatbox.prototype.get = function() {
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    dataType: 'json',
    data: {
      method: 'get',
      tid: this.tid,
      archives: this.archives
    },
    cache: false,
    success: function(response) {
      if (response) {
        self.refresh(response);
      }
    }
  });
};
Chatbox.prototype.refresh = function(data) {
  if (data.error) {
    $("body").html(data.error);
  } else {
    if (this.connected && !this.archives) {
      $("#chatbox_footer").css('display', 'block');
      $("#chatbox_messenger_form").css('display', 'block');
      $("#chatbox_messenger_form").css('visibility', 'visible');
    } else {
      $("#chatbox_footer").css('display', 'none');
      $("#chatbox_messenger_form").css('display', 'none');
      $("#chatbox_messenger_form").css('visibility', 'hidden');
    }
    if (this.connected) {
      $("#chatbox_display_archives").show();
      $("#chatbox_option_co").hide();
      $("#chatbox_option_disco, #chatbox_footer").show();
      $(".format-message").each(function() {
        var name = $(this).attr('name');
        var value = my_getcookie('CB_' + name);
        $(this).prop('checked', parseInt(value) ? true : false);
      });
      this.format();
      if (data.lastModified) {
        this.listenParams.lastModified = data.lastModified;
      }
    } else {
      $("#chatbox_option_co").show();
      $("#chatbox_option_disco, #chatbox_footer").hide();
      $("#chatbox_display_archives").hide();
    }
    if (data.users) {
      this.users = [];
      $(".online-users, .away-users").empty();
      $(".member-title").hide();
      for (var i in data.users) {
        var user = data.users[i];
        this.users[user.id] = user;
        var username = "" + (user.admin ? "@ " : "") + "" + user.username + "" + "";
        var list = user.online ? '.online-users' : '.away-users';
        $(list).append('
' + username + '
');
      }
      if (!$(".online-users").is(':empty')) {
        $(".member-title.online").show();
      }
      if (!$(".away-users").is(':empty')) {
        $(".member-title.away").show();
      }
    }
    if (data.messages) {
      var scroll = !this.messages || this.messages.length != data.messages.length;
      this.messages = data.messages;
      $("#chatbox").empty();
      if (this.messages) {
        var content = "";
        for (var j = 0; j < this.messages.length; j++) {
          var message = this.messages[j];
          var html = "
" + "[" + message.datetime + "]";
          if (message.userId == -10) {
            html += "" + "" + " " + message.msg + "" + "" + "";
          } else {
            html += "";
            if (this.avatar) {
              html += "	";
            }
            html += "	" + " " + (message.user.admin ? "@ " : "") + "" + message.username + " : " + "" + "" + "" + message.msg + "" + "";
          }
          html += "
";
          content += html;
        }
        $("#chatbox").append(content);
        if (scroll) {
          $("#chatbox")[0].scrollTop = $("#chatbox").prop('scrollHeight') * 2;
        }
      }
    }
  }
};
Chatbox.prototype.send = function(params) {
  var self = this;
  if (!params) {
    params = $("form[name='post']").serialize();
  }
  var data = params + "&method=send&archives=" + this.archives;
  $("#message").val('').focus();
  $.ajax({
    url: this.actionsUrl,
    type: 'post',
    data: data,
    dataType: 'json',
    cache: false,
    success: function(response) {
      self.refresh(response);
    }
  });
};
Chatbox.prototype.format = function() {
  var input = $("#message");
  input.css('font-weight', parseInt(my_getcookie('CB_bold')) ? 'bold' : 'normal');
  input.css('font-style', parseInt(my_getcookie('CB_italic')) ? 'italic' : 'normal');
  input.css('text-decoration', (parseInt(my_getcookie('CB_underline')) ? 'underline ' : '') + (parseInt(my_getcookie('CB_strike')) ? 'line-through' : ''));
  color = my_getcookie('CB_color');
  if (color) {
    $("#scolor").val(color);
    $("#divcolor-preview").css('background-color', color);
    input.css('color', color);
  } else if (this.defaultColor) {
    $("#scolor").val(this.defaultColor);
    $("#divcolor-preview").css('background-color', this.defaultColor);
    input.css('color', this.defaultColor);
  }
};
Chatbox.unban = function(user, id) {
  $("#message").val('/unban ' + user);
  window.chatbox.send();
  $("[id='" + id + "']", this.banPopup).hide();
};
Chatbox.prototype.openHelpPage = function() {
  this.helpPopup = window.open('/chatbox/help.forum', 'help-chatbox', 'toolbar=no,menubar=no,personalbar=no,width=600,height=300,scrollbars=yes,resizable=yes');
};
Chatbox.prototype.openBanlistPage = function() {
  this.banPopup = window.open('/chatbox/banlist.forum?' + new Date().getTime(), 'banlist', 'toolbar=no,menubar=no,personalbar=no,width=450,height=300,scrollbars=yes,resizable=yes');
};
jQuery().ready(function() {
  $(".format-message").change(function() {
    var name = $(this).attr('name');
    my_setcookie('CB_' + name, $(this).is(':checked') ? 1 : 0);
    window.chatbox.format();
  });
  $("#divcolor").on('click', function(event) {
    var scrX = (event.screenX - 270);
    var scrY = (event.screenY - 270);
    window.open('/chatbox/selectcolor', 'color', 'toolbar=no,menubar=no,personalbar=no,width=250,height=200,scrollbars=no,resizable=yes,left=' + scrX + ',top=' + scrY);
  });
  $("#divsmilies").click(function(event) {
    var scrX = (event.screenX - 270);
    var scrY = (event.screenY - 380);
    window.open('/post.forum?mode=smilies', 'chatbox_smilies', 'toolbar=no,menubar=no,personalbar=no,width=350,height=300,scrollbars=yes,resizable=yes,left=' + scrX + ',top=' + scrY);
  });
  $("form[name='post']").submit(function() {
    var message = $("#message").val().trim();
    if (message != "") {
      switch (message) {
        case '/banlist':
          window.chatbox.openBanlistPage();
          break;
        case '/help':
          window.chatbox.openHelpPage();
          break;
        default:
          window.chatbox.send();
          break;
      }
      $("#message").val("").focus();
    } else {
      $("#message").focus();
    }
    return false;
  });
  $("body").on('click', '.chatbox-username', function(event) {
    $("#message").val($("#message").val() + $(this).text()).focus();
  });
  $("#chatbox_members").on('contextmenu', '.chatbox-user-username', function(event) {
    var userId = $(this).data('user');
    var user = chatbox.users[userId];
    var me = chatbox.users[chatbox.userId];
    showMenu(event, user, me);
    return false;
  });
});
if (!String.prototype.trim) {
  String.prototype.trim = function() {
    return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
  }
}
<commit_msg>document and finish chatbox-proto impl<commit_after>// prototype implementation for the chatbox
// this allows you to extend and modify features without relying on its inner workings

// there are many other ways of doing this, but i wanted to avoid run-at document-start for this one
// so instead of preventing the original chatbox proto from loading we simply inject our own after it's done
// the downside of that is that you MUST use @grant none and it might load a teeny bit slower

// internal (added) features are prefixed with a _ (underscore)
// daily reminder that you must explicitly assign a property to `window` for it to be exported from grease to the page

var Chatbox = window.Chatbox = function(tid, params) {
  this.tid = tid;
  this.params = params;
  if (this.params == '' || this.params == undefined) {
    this.params = {
      archives: 0
    };
  }
  this.archives = this.params.archives;
  this.avatar = this.params.avatar;
  this.nolisten = !!parseInt(this.params.archives);
  this.actionsUrl = '/chatbox/actions.forum';
  this.messages = [];
  this.users = [];
  this.listenParams = {};
  //
  this._hansen = true;
};
function _cloneChatbox(base) {
  /*
  reference model:

  var chatbox = new Chatbox("<my secret session ID>", {
    "archives": 0,
    "avatar": 0
  });
  chatbox.userId = 2548;
  chatbox.connected = false;
  chatbox.defaultColor = '#dfdfdf';
  if (document.location.href.indexOf('chatbox', 1) == -1) {
    $('#divcolor').css('display', 'none');
    $('#divsmilies').css('display', 'none');
  }
  chatbox.init();
  */

  var chatbox = window.chatbox = new Chatbox(base.tid, base.params);
  chatbox.userId = base.userId;
  chatbox.connected = base.connected;
  chatbox.defaultColor = base.defaultColor;

  chatbox.init();
}
Chatbox.prototype.init = function(noget) {
  this.get();
  if (this.connected) {
    this.initListening();
  }
  this.format();
};
Chatbox.prototype.connect = function() {
  var data = this.params;
  data.method = 'connect';
  data.tid = this.tid;
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    data: data,
    dataType: 'json',
    cache: false,
    success: function(response) {
      self.connected = response.connected;
      self.listenParams = response.listenParams;
      self.refresh(response);
      self.listen();
    }
  });
};
Chatbox.prototype.disconnect = function() {
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    dataType: 'json',
    data: {
      method: 'disconnect',
      tid: this.tid
    },
    cache: false,
    success: function(response) {
      self.connected = response.connected;
      self.refresh(response);
    }
  });
};
Chatbox.prototype.initListening = function() {
  if (this.initTries >= 3) {
    this.disconnect();
  } else {
    var self = this;
    if (this.connected) {
      $.ajax({
        url: this.actionsUrl,
        type: 'get',
        dataType: 'json',
        data: {
          method: 'initListening',
          tid: this.tid,
          archives: this.archives
        },
        cache: false,
        success: function(response) {
          self.listenParams = response;
          self.listen();
        },
        error: function() {
          if (!self.initTries) {
            self.initTries = 1;
          } else {
            self.initTries++;
          }
          self.initListening();
        }
      });
    }
  }
};
Chatbox.prototype.listen = function() {
  var self = this;
  if (this.connected && !this.nolisten) {
    if (!this.listenParams.url || !this.listenParams.lastModified) {
      this.initListening();
      return;
    }
    this.listenParams.tag = Math.floor(Math.random() * 100000);
    $.ajax({
      url: this.listenParams.url,
      timeout: 35000,
      type: 'get',
      data: "tag=" + window.escape(this.listenParams.tag) + "&time=" + window.escape(this.listenParams.lastModified),
      dataType: 'text',
      crossDomain: true,
      cache: false,
      success: function(response, txt, xhr) {
        self.listenParams.lastModified = xhr.getResponseHeader('Last-Modified');
        if (response) {
          self.get();
        }
        self.listen();
      },
      error: function(xhr, status, error) {
        if (xhr.status == 403 || xhr.status == 404 || xhr.status == 0) {
          self.initListening();
        } else {
          self.disconnect();
        }
      }
    })
  }
};
Chatbox.prototype.get = function() {
  var self = this;
  $.ajax({
    url: this.actionsUrl,
    type: 'get',
    dataType: 'json',
    data: {
      method: 'get',
      tid: this.tid,
      archives: this.archives
    },
    cache: false,
    success: function(response) {
      if (response) {
        self.refresh(response);
      }
    }
  });
};
Chatbox.prototype.refresh = function(data) {
  if (data.error) {
    $("body").html(data.error);
  } else {
    if (this.connected && !this.archives) {
      $("#chatbox_footer").css('display', 'block');
      $("#chatbox_messenger_form").css('display', 'block');
      $("#chatbox_messenger_form").css('visibility', 'visible');
    } else {
      $("#chatbox_footer").css('display', 'none');
      $("#chatbox_messenger_form").css('display', 'none');
      $("#chatbox_messenger_form").css('visibility', 'hidden');
    }
    if (this.connected) {
      $("#chatbox_display_archives").show();
      $("#chatbox_option_co").hide();
      $("#chatbox_option_disco, #chatbox_footer").show();
      $(".format-message").each(function() {
        var name = $(this).attr('name');
        var value = my_getcookie('CB_' + name);
        $(this).prop('checked', parseInt(value) ? true : false);
      });
      this.format();
      if (data.lastModified) {
        this.listenParams.lastModified = data.lastModified;
      }
    } else {
      $("#chatbox_option_co").show();
      $("#chatbox_option_disco, #chatbox_footer").hide();
      $("#chatbox_display_archives").hide();
    }
    if (data.users) {
      this.users = [];
      $(".online-users, .away-users").empty();
      $(".member-title").hide();
      for (var i in data.users) {
        var user = data.users[i];
        this.users[user.id] = user;
        var username = "" + (user.admin ? "@ " : "") + "" + user.username + "" + "";
        var list = user.online ? '.online-users' : '.away-users';
        $(list).append('
' + username + '
');
      }
      if (!$(".online-users").is(':empty')) {
        $(".member-title.online").show();
      }
      if (!$(".away-users").is(':empty')) {
        $(".member-title.away").show();
      }
    }
    if (data.messages) {
      var scroll = !this.messages || this.messages.length != data.messages.length;
      this.messages = data.messages;
      $("#chatbox").empty();
      if (this.messages) {
        var content = "";
        for (var j = 0; j < this.messages.length; j++) {
          var message = this.messages[j];
          var html = "
" + "[" + message.datetime + "]";
          if (message.userId == -10) {
            html += "" + "" + " " + message.msg + "" + "" + "";
          } else {
            html += "";
            if (this.avatar) {
              html += "	";
            }
            html += "	" + " " + (message.user.admin ? "@ " : "") + "" + message.username + " : " + "" + "" + "" + message.msg + "" + "";
          }
          html += "
";
          content += html;
        }
        $("#chatbox").append(content);
        if (scroll) {
          $("#chatbox")[0].scrollTop = $("#chatbox").prop('scrollHeight') * 2;
        }
      }
    }
  }
};
Chatbox.prototype.send = function(params) {
  var self = this;
  if (!params) {
    params = $("form[name='post']").serialize();
  }
  var data = params + "&method=send&archives=" + this.archives;
  $("#message").val('').focus();
  $.ajax({
    url: this.actionsUrl,
    type: 'post',
    data: data,
    dataType: 'json',
    cache: false,
    success: function(response) {
      self.refresh(response);
    }
  });
};
Chatbox.prototype.format = function() {
  var input = $("#message");
  input.css('font-weight', parseInt(my_getcookie('CB_bold')) ? 'bold' : 'normal');
  input.css('font-style', parseInt(my_getcookie('CB_italic')) ? 'italic' : 'normal');
  input.css('text-decoration', (parseInt(my_getcookie('CB_underline')) ? 'underline ' : '') + (parseInt(my_getcookie('CB_strike')) ? 'line-through' : ''));
  color = my_getcookie('CB_color');
  if (color) {
    $("#scolor").val(color);
    $("#divcolor-preview").css('background-color', color);
    input.css('color', color);
  } else if (this.defaultColor) {
    $("#scolor").val(this.defaultColor);
    $("#divcolor-preview").css('background-color', this.defaultColor);
    input.css('color', this.defaultColor);
  }
};
Chatbox.unban = function(user, id) {
  $("#message").val('/unban ' + user);
  window.chatbox.send();
  $("[id='" + id + "']", this.banPopup).hide();
};
Chatbox.prototype.openHelpPage = function() {
  this.helpPopup = window.open('/chatbox/help.forum', 'help-chatbox', 'toolbar=no,menubar=no,personalbar=no,width=600,height=300,scrollbars=yes,resizable=yes');
};
Chatbox.prototype.openBanlistPage = function() {
  this.banPopup = window.open('/chatbox/banlist.forum?' + new Date().getTime(), 'banlist', 'toolbar=no,menubar=no,personalbar=no,width=450,height=300,scrollbars=yes,resizable=yes');
};
jQuery().ready(function() {
  $(".format-message").change(function() {
    var name = $(this).attr('name');
    my_setcookie('CB_' + name, $(this).is(':checked') ? 1 : 0);
    window.chatbox.format();
  });
  $("#divcolor").on('click', function(event) {
    var scrX = (event.screenX - 270);
    var scrY = (event.screenY - 270);
    window.open('/chatbox/selectcolor', 'color', 'toolbar=no,menubar=no,personalbar=no,width=250,height=200,scrollbars=no,resizable=yes,left=' + scrX + ',top=' + scrY);
  });
  $("#divsmilies").click(function(event) {
    var scrX = (event.screenX - 270);
    var scrY = (event.screenY - 380);
    window.open('/post.forum?mode=smilies', 'chatbox_smilies', 'toolbar=no,menubar=no,personalbar=no,width=350,height=300,scrollbars=yes,resizable=yes,left=' + scrX + ',top=' + scrY);
  });
  $("form[name='post']").submit(function() {
    var message = $("#message").val().trim();
    if (message != "") {
      switch (message) {
        case '/banlist':
          window.chatbox.openBanlistPage();
          break;
        case '/help':
          window.chatbox.openHelpPage();
          break;
        default:
          window.chatbox.send();
          break;
      }
      $("#message").val("").focus();
    } else {
      $("#message").focus();
    }
    return false;
  });
  $("body").on('click', '.chatbox-username', function(event) {
    $("#message").val($("#message").val() + $(this).text()).focus();
  });
  $("#chatbox_members").on('contextmenu', '.chatbox-user-username', function(event) {
    var userId = $(this).data('user');
    var user = chatbox.users[userId];
    var me = chatbox.users[chatbox.userId];
    showMenu(event, user, me);
    return false;
  });
});
if (!String.prototype.trim) {
  String.prototype.trim = function() {
    return this.replace(/^\s+/g, '').replace(/\s+$/g, '');
  }
}

_cloneChatbox(window.chatbox);
<|endoftext|>


<commit_before>module.exports.name = 'notifyLINE';
module.exports.events = ['NEW'];
const ryryID = '202995638860906496';
const ririID = '282243864771821568';
const activeStaff = '240647591770062848';
const config = require('../config.json');
const request = require('request');

module.exports.isAllowed = (message, server, bot) => {
  return message.mentions.users.has(bot.owner_ID) || message.mentions.roles.has(activeStaff) // active staff
   || message.mentions.users.has(ryryID)|| message.mentions.users.has(ririID);
};

function notify(message, token) {
  let options = { method: 'POST',
    url: 'https://notify-api.line.me/api/notify',
    headers:
        {
          'authorization': `Bearer ${token}`,
          'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
        },
    formData: {message: `#${message.channel.name}\n@${message.author.username}:\n${message.cleanContent}`}
  };
  request(options, function (error) {
    if (error) console.log(error);
  });
}

module.exports.process = async (message, server, bot) => {
  if (message.content.startsWith('t!') || message.content.startsWith('.')) return; // ignore bot commands
  let me = await server.guild.member(bot.owner_ID);
  let ry = await server.guild.member(ryryID);
  let riri = await server.guild.member(ririID);
  if (me.presence.status == 'offline'
      &&  (message.mentions.users.has(bot.owner_ID)
           || message.mentions.roles.has(activeStaff))) { // If I'm offline
    notify(message, config.LINEnotifyToken);
  }
  if ((ry.presence.status == 'offline' && message.mentions.users.has(ryryID))
      || message.mentions.roles.has(activeStaff)) {
    notify(message, config.ryryLINEnotifyToken);
  }

  if (riri.presence.status == 'offline'
      &&  (message.mentions.users.has(ririID)
           || message.mentions.roles.has(activeStaff))) {
    notify(message, config.ririLINEnotifyToken);
  }
};

<commit_msg>new line<commit_after>module.exports.name = "notifyLINE";
module.exports.events = ["NEW"];
const ryryID = "202995638860906496";
const ririID = "282243864771821568";
const zappiID = "652705051684503557";
const skyzID = "107202830846148608";
const IDs = [ryryID, ririID, zappiID, skyzID];

const activeStaff = "240647591770062848";
const config = require("../config.json");
const request = require("request");

module.exports.isAllowed = (message, server, bot) => {
  const mentions = message.mentions.users;
  return (
    mentions.has(bot.owner_ID) ||
    message.mentions.roles.has(activeStaff) || // active staff
    IDs.some((id) => mentions.has(id))
  );
};

function notify(message, token) {
  const options = {
    method: "POST",
    url: "https://notify-api.line.me/api/notify",
    headers: {
      authorization: `Bearer ${token}`,
      "content-type":
        "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
    },
    formData: {
      message: `#${message.channel.name}\n${message.author.username}:\n${message.cleanContent}`,
    },
  };
  request(options, function (error) {
    if (error) console.log(error);
  });
}

module.exports.process = async (message, server, bot) => {
  if (message.content.startsWith("t!") || message.content.startsWith("."))
    return; // ignore bot commands
  const me = await server.guild.member(bot.owner_ID);
  const ry = await server.guild.member(ryryID);
  const riri = await server.guild.member(ririID);
  const zappi = await server.guild.member(zappiID);
  const skyz = await server.guild.member(skyzID);

  if (
    me.presence.status == "offline" &&
    (message.mentions.users.has(bot.owner_ID) ||
      message.mentions.roles.has(activeStaff))
  ) {
    // If I'm offline
    notify(message, config.LINEnotifyToken);
  }
  if (
    (ry.presence.status == "offline" && message.mentions.users.has(ryryID)) ||
    message.mentions.roles.has(activeStaff)
  ) {
    notify(message, config.ryryLINEnotifyToken);
  }

  if (
    riri.presence.status == "offline" &&
    (message.mentions.users.has(ririID) ||
      message.mentions.roles.has(activeStaff))
  ) {
    notify(message, config.ririLINEnotifyToken);
  }

  if (message.mentions.roles.has(activeStaff)) {
    notify(message, config.zappiLINEnotifyToken);
  }

  if (
    skyz.presence.status === "offline" &&
    message.mentions.roles.has(activeStaff)
  ) {
    notify(message, config.skyzLINEnotifyToken);
  }
};
<|endoftext|>


<commit_before>let xmlweb = require("xmlweb");
// get test: curl -X GET http://localhost:8080
// post test: curl -X POST http://localhost:8080
xmlweb("xp", function (xp, $_, t) {
    $_().imports({
        Index: {
            xml: "<i:HTTP xmlns:i='//xmlweb'>\
                    <i:Router url='/*' method='POST'/>\
                    <Response id='response'/>\
                  </i:HTTP>"
        },
        Response: {
            fun: function (sys, items, opts) {
                this.on("enter", (e, d) => {
                    d.res.setHeader("Content-Type", "application/json;");
                    d.res.end(JSON.stringify({data: "hello, world"}));
                });
            }
        }
    });
}).startup("//xp/Index");<commit_msg>commit<commit_after>// get test: curl -X GET http://localhost:8080
// post test: curl -X POST http://localhost:8080

let xmlweb = require("xmlweb");
xmlweb("xp", function (xp, $_, t) {
    $_().imports({
        Index: {
            xml: "<i:HTTP xmlns:i='//xmlweb'>\
                    <i:Router url='/*' method='POST'/>\
                    <Response id='response'/>\
                  </i:HTTP>"
        },
        Response: {
            fun: function (sys, items, opts) {
                this.on("enter", (e, d) => {
                    d.res.setHeader("Content-Type", "application/json;");
                    d.res.end(JSON.stringify({data: "hello, world"}));
                });
            }
        }
    });
}).startup("//xp/Index");<|endoftext|>