gadget
Version:
Gadget - Snap together HTML5 Applications
756 lines (647 loc) • 17.3 kB
JavaScript
var https = require('https');
var http = require('http');
var querystring = require('querystring');
var mkdirp = require('mkdirp');
var fs = require('fs');
var _path = require('path');
var request = require('request');
var AdmZip = require('adm-zip');
var url = require('url');
var CONNECTORS = {
"gitana": {
"type": "js",
"url": "http://code.cloudcms.com/gitana-javascript-driver/{version}/gitana.js",
"dependencies": {},
"shim": true
},
"jquery": {
"type": "js",
"url": "http://code.jquery.com/jquery-{version}.js",
"dependencies": {},
"shim": true
},
"alpaca": {
"type": "zip",
"url2": "http://www.alpacajs.org/downloads/alpaca-{version}-all.zip",
"url": "local:/projects/alpaca/build/package/downloads/alpaca.zip",
"locations": {
"js": "js/alpaca.js",
"css": "css/alpaca.css"
},
"dependencies": {
"jquery": "*"
},
"shim2": true,
"shim": false
},
"ratchet": {
"type": "js",
"url": "local:/projects/ratchet/build/package/downloads/ratchet-{version}.js",
"dependencies": {
"jquery": "*"
},
"shim": true
},
"ratchet-jquery-template-engine": {
"type": "js",
"url": "local:/projects/ratchet/build/package/downloads/ratchet-{version}-jquery-template-engine.js",
"dependencies": {
"ratchet": "*"
},
"shim": true
},
"ratchet-gadgets": {
"type": "zip",
"url": "local:/projects/ratchet/build/package/downloads/ratchet-{version}-gadget-pack.zip",
"locations": {
"js": "js/ratchet-{version}-gadget-pack.js",
"css": "css/ratchet-{version}-gadget-pack.css"
},
"dependencies": {
"ratchet": "*"
},
"shim": true
},
"bootstrap": {
"type": "zip",
"url": "http://twitter.github.com/bootstrap/assets/bootstrap.zip",
"locations": {
"js": "js/bootstrap.js",
"css": "css/bootstrap-responsive.css"
},
"skipFirstLevel": true,
"dependencies": {
"ratchet": "*"
},
"shim": true
}
};
var rmdirRecursiveSync = function(dir)
{
if (!fs.existsSync(dir))
{
return;
}
var list = fs.readdirSync(dir);
for (var i = 0; i < list.length; i++)
{
var filename = _path.join(dir, list[i]);
var stat = fs.statSync(filename);
if (filename == "." || filename == "..")
{
// pass these files
}
else if (stat.isDirectory())
{
// rmdir recursively
rmdirRecursiveSync(filename);
}
else
{
// rm fiilename
fs.unlinkSync(filename);
}
}
fs.rmdirSync(dir);
};
var mkdirRecursive = function(path, fn)
{
mkdirp(path, 0755, function(err) {
if (err) throw err;
// console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
};
/**
* Sends a free trial request to Cloud CMS for a given email address.
*/
exports.register = function(program, email, callback)
{
var doRegister = function(program, email, callback)
{
console.log("Please wait, requesting a trial for: " + email + " ...");
console.log("");
// send email to cloud cms
var data = querystring.stringify({
"email": email,
"projectdesc": "(origin: cloudcms-node-js client)"
});
var req = https.request({
host: "www.cloudcms.com",
port: 443,
path: "/form/trial/submit",
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': data.length
}
}, function(res) {
/*
res.on('data', function(d) {
console.log(d.toString());
});
*/
res.on('end', function() {
console.log("");
console.log("Your free trial request was submitted.");
console.log("Please check your email for notification of your free trial account.");
console.log("");
callback();
});
});
req.write(data);
req.on('error', function(err) {
console.log(err);
});
};
console.log("");
console.log("-------------------------------------------------------------------------");
console.log("Register for a Cloud CMS account");
console.log("");
if (!email)
{
console.log("All we need is an email address to get you started.");
console.log("There is no cost and you can cancel your account at any time.")
console.log("");
program.prompt("Email > ", function(email) {
doRegister(program, email, callback);
});
}
else
{
doRegister(program, email, callback);
}
};
var installDependency = function(name, version, path, callback)
{
var connector = CONNECTORS[name];
if (!connector)
{
console.log("Cannot find connector for dependency type: " + name);
return;
}
var type = connector.type;
if (!type)
{
type = "js";
}
var dependencyUrl = connector.url;
dependencyUrl = dependencyUrl.replace("{version}", version);
var libPath = path + "/public/lib";
var afterInstallCallback = function()
{
// for modules, we do some renaming of the JS and CSS files contained within to our convention
if (type == "zip")
{
var jsLocation = connector.locations["js"];
var jsMain = null;
var cssLocation = connector.locations["css"];
var cssMain = null;
// if jsLocation, rename to module convention name
if (jsLocation)
{
jsLocation = jsLocation.replace("{version}", version);
jsMain = jsLocation.substring(0, jsLocation.lastIndexOf("."));
//fs.renameSync(libPath + "/" + name + "/" + jsLocation, libPath + "/" + name + "/" + jsMain + ".js");
}
// if cssLocation, rename to module convention name
if (cssLocation)
{
cssLocation = cssLocation.replace("{version}", version);
cssMain = cssLocation.substring(0, cssLocation.lastIndexOf("."));
//fs.renameSync(libPath + "/" + name + "/" + cssLocation, libPath + "/" + cssModulePath + ".css");
}
updateRequireJS(name, libPath, {
"shim": connector.shim,
"dependencies": connector.dependencies,
"jsMain": jsMain,
"cssMain": cssMain
});
}
else if (type == "js")
{
var x = dependencyUrl.lastIndexOf("/");
var filename = dependencyUrl.substring(x+1);
jsMain = filename.substring(0, filename.lastIndexOf("."));;
//fs.renameSync(libPath + "/" + name + "/" + filename, libPath + "/" + jsModulePath + ".js");
updateRequireJS(name, libPath, {
"shim": connector.shim,
"dependencies": connector.dependencies,
"jsMain": jsMain,
"cssMain": null
});
}
callback();
};
// remove module from library
rmdirRecursiveSync(libPath + "/" + name);
// REQUIREJS: auto-update
updateRequireJS(name, libPath);
if (type == "js")
{
_installFile(dependencyUrl, libPath + "/" + name, afterInstallCallback);
}
else if (type == "zip")
{
var filterFunction = buildLibraryFilterFunction(connector, name, version);
_installZip(dependencyUrl, libPath + "/" + name, connector.skipFirstLevel, filterFunction, afterInstallCallback);
}
};
var updateRequireJS = function(name, libPath, config)
{
if (!config)
{
config = {};
}
var dependencies = config.dependencies;
var shim = config.shim;
var jsMain = config.jsMain;
var cssMain = config.cssMain;
// MAIN.JS
(function() {
var text = fs.readFileSync(libPath + "/../main.js").toString();
var KEY1 = "require.config(";
var KEY2 = ");";
var x1 = text.indexOf(KEY1);
var x2 = text.indexOf(KEY2, x1);
var piece = text.substring(x1 + KEY1.length, x2);
var json = JSON.parse(text.substring(x1 + KEY1.length, x2));
if (!json.shim)
{
json.shim = {};
}
// remove "name" from shim
delete json.shim[name];
if (!json.packages)
{
json.packages = [];
}
// from "name" from packages
var i = 0;
while (i < json.packages.length)
{
if (json.packages[i] == name)
{
json.packages.splice(i,i);
break;
}
else if (typeof json.packages[i] == "object")
{
if (json.packages[i].name == name)
{
json.packages.splice(i,i);
break;
}
}
i++;
}
if (jsMain)
{
if (shim && dependencies)
{
var dependencyArray = [];
for (var dependencyName in dependencies)
{
dependencyArray.push(dependencyName);
}
json.shim[name] = dependencyArray;
}
// set package
json.packages.push({
"name": name,
"main": jsMain
});
}
text = text.substring(0, x1 + KEY1.length) + JSON.stringify(json, null, 4) + text.substring(x2);
fs.writeFileSync(libPath + "/../main.js", text);
})();
// MAIN.CSS
(function() {
var text = fs.readFileSync(libPath + "/../main.css").toString();
var KEY1 = "/** START: " + name + " **/";
var KEY2 = "/** END: " + name + " **/";
var x1 = text.indexOf(KEY1);
if (x1 > -1)
{
// remove
var x2 = text.indexOf(KEY2, x1);
text = text.substring(0, x1) + text.substring(x2 + KEY2.length);
}
if (cssMain)
{
// add
text = text + "\r\n" + KEY1 + '\r\n' + '@import url("lib/' + name + '/' + cssMain + '.css" );' + '\r\n' + KEY2 + '\r\n';
}
fs.writeFileSync(libPath + "/../main.css", text);
})();
};
var installDependencies = function(dependencies, path, callback)
{
var names = [];
for (var name in dependencies)
{
names.push(name);
}
var f = function(count, path, callback)
{
if (count < names.length)
{
var name = names[count];
var version = dependencies[name];
installDependency(name, version, path, function(err) {
if (err)
{
callback(err);
return;
}
f(count + 1, path, callback);
});
}
else
{
callback();
}
};
f(0, path, callback);
}
var installApplicationTemplate = function(path, callback)
{
_installZip("https://github.com/gitana/cloudcms-application-template/zipball/master", path, true, null, function() {
callback();
});
}
var buildLibraryFilterFunction = function(connector, name, version)
{
var jsLocation = connector.locations["js"];
if (jsLocation)
{
jsLocation = jsLocation.replace("{version}", version);
}
var cssLocation = connector.locations["css"];
if (cssLocation)
{
cssLocation = cssLocation.replace("{version}", version);
}
return function(path)
{
if (path)
{
path = path.toLowerCase();
// directories
if (path.indexOf("examples/") > -1)
{
return true;
}
if (path.indexOf("favicon.ico") > -1)
{
return true;
}
if (path.indexOf("lib/") > -1)
{
return true;
}
if (path.indexOf("tests/") > -1)
{
return true;
}
// html files
var x = path.indexOf("/");
if (x == -1 || x == 0)
{
if (path.indexOf(".html"))
{
return true;
}
if (path.indexOf(".htm"))
{
return true;
}
}
// if we have a jsLocation, remove all javascript except for it
if (jsLocation)
{
if (path.indexOf(".js") > -1)
{
if (path.indexOf(jsLocation.toLowerCase()) > -1)
{
return false;
}
return true;
}
}
// if we have a cssLocation, remove all css except for it
if (cssLocation)
{
if (path.indexOf(".css") > -1)
{
if (path.indexOf(cssLocation.toLowerCase()) > -1)
{
return false;
}
return true;
}
}
}
return false;
};
};
var _installZip = function(file_url, targetPath, skipFirstLevel, filterFunction, callback)
{
var protocol = url.parse(file_url).protocol;
// once we have a buffer, this extracts the zip into the right place
var doExtractZip = function(buf, callback)
{
var zip = new AdmZip(buf);
var zipEntries = zip.getEntries();
zipEntries.forEach(function(zipEntry)
{
var entryName = zipEntry.entryName;
var subPath = entryName;
var x = subPath.lastIndexOf("/");
var filename = subPath.substring(x+1);
subPath = subPath.substring(0,x); // keep only folder path
// if skip first level, the cut off first directory
if (skipFirstLevel)
{
var x = subPath.indexOf("/");
if (x == -1)
{
subPath = "";
}
else
{
subPath = subPath.substring(x + 1);
}
}
if (filename.length > 0)
{
var filter = false;
if (filterFunction)
{
filter = filterFunction(subPath + "/" + filename)
}
if (!filter)
{
zip.extractEntryTo(entryName, targetPath + "/" + subPath, false, true);
}
}
});
callback();
};
// load via HTTP or HTTPS
if (protocol == "http:" || protocol == "https:")
{
// use either HTTP or HTTPS
var h = (protocol == "http:" ? http : https);
var options = {
host: url.parse(file_url).host,
port: (protocol == "http:" ? 80 : 443),
path: url.parse(file_url).pathname
};
h.get(options, function(res) {
var data = [], dataLen = 0;
// automatically follow any redirects
if (res.statusCode >= 300 && res.statusCode <= 399) {
_installZip(res.headers.location, targetPath, skipFirstLevel, filterFunction, callback);
return;
}
res.on('data', function(chunk) {
data.push(chunk);
dataLen += chunk.length;
}).on('end', function() {
var buf = new Buffer(dataLen);
for (var i=0, len = data.length, pos = 0; i < len; i++) {
data[i].copy(buf, pos);
pos += data[i].length;
}
doExtractZip(buf, callback);
});
});
}
else if (protocol == "local:")
{
var filepath = file_url.substring(6);
fs.readFile(filepath, function(err, data) {
doExtractZip(data, callback);
});
}
};
var _installFile = function(file_url, targetPath, callback)
{
var protocol = url.parse(file_url).protocol;
var doInstallFile = function(data, callback)
{
var x = file_url.lastIndexOf("/");
var filename = file_url.substring(x+1);
mkdirRecursive(targetPath, function() {
fs.writeFileSync(targetPath + "/" + filename, data, "utf-8");
callback();
});
};
// load via HTTP or HTTPS
if (protocol == "http:" || protocol == "https:")
{
// use either HTTP or HTTPS
var h = (protocol == "http:" ? http : https);
var options = {
host: url.parse(file_url).host,
port: (protocol == "http:" ? 80 : 443),
path: url.parse(file_url).pathname
};
h.get(options, function(res) {
var data = [], dataLen = 0;
// automatically follow any redirects
if (res.statusCode >= 300 && res.statusCode <= 399) {
_installFile(res.headers.location, targetPath, callback);
return;
}
res.on('data', function(chunk) {
data.push(chunk);
dataLen += chunk.length;
}).on('end', function() {
var buf = new Buffer(dataLen);
for (var i=0, len = data.length, pos = 0; i < len; i++) {
data[i].copy(buf, pos);
pos += data[i].length;
}
doInstallFile(buf, callback);
});
});
}
else if (protocol == "local:")
{
var filepath = file_url.substring(6);
fs.readFile(filepath, function(err, data) {
doInstallFile(data, callback);
});
}
};
exports.createApp = function(path, callback)
{
// install the application template
installApplicationTemplate(path, function() {
// ensure base directories
mkdirRecursive(path + "/public/scripts");
mkdirRecursive(path + "/public/styles");
mkdirRecursive(path + "/public/images");
mkdirRecursive(path + "/public/lib");
// ensure there is a "/config" folder
mkdirRecursive(path + "/config");
// ensure there is a public "/templates" folder
mkdirRecursive(path + "/public/templates");
// update dependencies
updateDependencies(path, function() {
callback();
});
});
};
var addPackageClientDependency = function(path, name, version)
{
var text = fs.readFileSync(path + "/package.json", text, "utf-8");
var packageJson = JSON.parse(text);
if (!packageJson["client"]) {
packageJson["client"] = {};
}
if (!packageJson["client"]["dependencies"]) {
packageJson["client"]["dependencies"] = {};
}
packageJson["client"]["dependencies"][name] = version;
fs.writeFileSync(path + "/package.json", JSON.stringify(packageJson, null, 4), "utf-8");
};
var listPackageClientDependencies = function(path)
{
var text = fs.readFileSync(path + "/package.json", text, "utf-8");
var packageJson = JSON.parse(text);
var dependencies = {};
if (packageJson["client"] && packageJson["client"]["dependencies"])
{
dependencies = packageJson["client"]["dependencies"];
}
return dependencies;
}
var updateDependencies = exports.updateDependencies = function(path, callback)
{
// make sure the application template is already installed
fs.exists(path + "/public", function(exists) {
if (!exists)
{
console.log("The Cloud CMS application template has not been installed.");
console.log("Please run 'createapp' first.");
callback();
return;
}
// add core libraries to package json
addPackageClientDependency(path, "jquery", "1.8.2");
addPackageClientDependency(path, "alpaca", "1.0.2-SNAPSHOT");
addPackageClientDependency(path, "ratchet", "1.0.2-SNAPSHOT");
addPackageClientDependency(path, "ratchet-jquery-template-engine", "1.0.2-SNAPSHOT");
addPackageClientDependency(path, "ratchet-gadgets", "1.0.2-SNAPSHOT");
addPackageClientDependency(path, "bootstrap", "2.1.1");
// package dependencies
var dependencies = listPackageClientDependencies(path);
// install dependencies
installDependencies(dependencies, path, function() {
callback();
});
});
};