Skip to content

Instantly share code, notes, and snippets.

@DimitarChristoff
Created May 8, 2013 12:51
Show Gist options
  • Save DimitarChristoff/5540233 to your computer and use it in GitHub Desktop.
Save DimitarChristoff/5540233 to your computer and use it in GitHub Desktop.
define(['require', 'lib/requirejs/require-css/normalize'], function(req, normalize){
// todo: refactor via require('colors') for nodejs;
var baseUrl = require.toUrl('.'),
greenOn = '\033[32m',
greenOff = '\033[39m',
redOn = '\033[31m',
redOff = greenOff,
ok = greenOn + '✔' + greenOff,
fail = redOn + '×' + redOff;
var imgbase = (function(){
// custom hack for imgbase npm by DC
var fs = require.nodeRequire('fs'),
http = require.nodeRequire('http'),
mimes = {
png: 'image/png', gif: 'image/gif', jpeg: 'image/jpeg', jpg: 'image/jpeg', ttf: 'font/ttf', svg: 'image/svg+xml', otf: 'font/otf'
},
types = [],
reString;
for (var i in mimes) {
types.push(i);
}
reString = 'url\\(["\']?\\s*([^\\)]*\\.(' + types.join('|') + '))\\??#?\\w*\\s*["\']?\\)';
function getBase64(url, opt){
opt = opt || {};
var ret = false,
data;
if (url[0] === '/') {
url = (opt.base ? opt.base + '/' : '') + url.slice(1);
} else {
url = (opt.rel ? opt.rel + '/' : '') + url;
}
data = fs.readFileSync(url);
if (!data || !data.length) {
console.log(fail + ' imgbase64: Could not process/find ' + url);
}
else {
console.log(ok + ' imgbase64: Converted ' + url);
ret = data.toString('base64');
}
return ret;
}
// what to export...
return function(str, opt){
opt = opt || {};
var result = '',
re = new RegExp(reString, 'g'),
done = false,
noop = function(){
},
parse = function(start){
var m = re.exec(str);
if (m !== null) {
var index = m.index,
len = m[0].length,
url = m[1],
type = m[2],
img = getBase64(url, opt);
if (img !== false) {
result += (str.slice(start, index) + 'url(data:' + mimes[type] + ';base64,' + img + ')');
}
parse(index + len);
} else {
done = result + str.slice(start);
}
};
parse(0);
while (done == false) {
noop();
}
return done;
};
}());
var cssAPI = {};
function compress(css){
if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) {
try {
var csso = require.nodeRequire('csso');
var csslen = css.length;
css = csso.justDoIt(css);
console.log(ok + ' Compressed CSS output to ' + Math.round(css.length / csslen * 100) + '%.');
return css;
}
catch (e) {
console.log(fail + ' Compression module not installed. Use "npm install csso -g" to enable.');
return css;
}
}
console.log(fail + ' Compression not supported outside of nodejs environments.');
return css;
}
//load file code - stolen from text plugin
function loadFile(path){
if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) {
var fs = require.nodeRequire('fs');
var file = fs.readFileSync(path, 'utf8');
if (file.indexOf('\uFEFF') === 0)
return file.substring(1);
return file;
}
else {
var file = new java.io.File(path),
lineSeparator = java.lang.System.getProperty("line.separator"),
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), 'utf-8')),
stringBuffer, line;
try {
stringBuffer = new java.lang.StringBuffer();
line = input.readLine();
if (line && line.length() && line.charAt(0) === 0xfeff)
line = line.substring(1);
stringBuffer.append(line);
while ((line = input.readLine()) !== null) {
stringBuffer.append(lineSeparator).append(line);
}
return String(stringBuffer.toString());
}
finally {
input.close();
}
}
}
function saveFile(path, data){
if (typeof process !== "undefined" && process.versions && !!process.versions.node && require.nodeRequire) {
var fs = require.nodeRequire('fs');
fs.writeFileSync(path, data, 'utf8');
}
else {
var content = new java.lang.String(data);
var output = new java.io.BufferedWriter(new java.io.OutputStreamWriter(new java.io.FileOutputStream(path), 'utf-8'));
try {
output.write(content, 0, content.length());
output.flush();
}
finally {
output.close();
}
}
}
//when adding to the link buffer, paths are normalised to the baseUrl
//when removing from the link buffer, paths are normalised to the output file path
function escape(content){
return content.replace(/(["'\\])/g, '\\$1')
.replace(/[\f]/g, "\\f")
.replace(/[\b]/g, "\\b")
.replace(/[\n]/g, "\\n")
.replace(/[\t]/g, "\\t")
.replace(/[\r]/g, "\\r");
}
var loadCSS = function(cssId, parse){
var fileUrl = cssId;
if (fileUrl.substr(fileUrl.length - 4, 4) != '.css' && !parse)
fileUrl += '.css';
fileUrl = req.toUrl(fileUrl);
//external URLS don't get added (just like JS requires)
if (fileUrl.substr(0, 7) == 'http://' || fileUrl.substr(0, 8) == 'https://')
return;
//add to the buffer
var css = loadFile(fileUrl);
//make file url absolute
//if (fileUrl.substr(0, 1) != '/')
// fileUrl = '/' + fileUrl;
//normalize all css to the base url - as the common path reference
//for injection we then only need one normalization from the base url
css = normalize(css, fileUrl, baseUrl);
// parse if necessary
if (parse)
css = parse(css);
return css;
};
var curModule;
cssAPI.load = function(name, req, load, config){
if (config.modules) {
//run through the module list - the first one without a layer set is the current layer we are in
//allows to track the current layer number for layer-specific config
for (var i = 0; i < config.modules.length; i++)
if (config.modules[i].layer === undefined) {
curModule = i;
break;
}
}
//store config
cssAPI.config = cssAPI.config || config;
//just return - 'write' calls are made after exclusions so we run loading there
load();
};
cssAPI.normalize = function(name, normalize){
if (name.substr(name.length - 1, 1) == '!')
name = name.substr(0, name.length - 1);
if (name.substr(name.length - 4, 4) == '.css')
name = name.substr(0, name.length - 4);
return normalize(name);
};
//list of cssIds included in this layer
var _layerBuffer = [];
cssAPI.write = function(pluginName, moduleName, write, extension, parse){
//external URLS don't get added (just like JS requires)
if (moduleName.substr(0, 7) == 'http://' || moduleName.substr(0, 8) == 'https://')
return;
_layerBuffer.push(loadCSS(moduleName + (extension ? '.' + extension : ''), parse));
write.asModule(pluginName + '!' + moduleName, 'define(function(){})');
};
cssAPI.onLayerEnd = function(write, data){
//separateCSS parameter set either globally or as a layer setting
var separateCSS = false;
if (cssAPI.config.separateCSS)
separateCSS = true;
if (typeof curModule == 'number' && cssAPI.config.modules[curModule].separateCSS !== undefined)
separateCSS = cssAPI.config.modules[curModule].separateCSS;
curModule = null;
//calculate layer css
var css = _layerBuffer.join('');
if (separateCSS) {
if (typeof console != 'undefined' && console.log)
console.log('Writing CSS! file: ' + data.name + '\n');
//calculate the css output path for this layer
var path = this.config.dir ? this.config.dir + data.name + '.css' : cssAPI.config.out.replace(/\.js$/, '.css');
css = imgbase(css, {
rel: '../../src/'
});
//renormalize the css to the output path
var output = compress(normalize(css, baseUrl, path));
saveFile(path, output);
}
else {
if (css == '')
return;
//write the injection and layer index into the layer
//prepare the css
css = imgbase(css, {
rel: '../../src'
});
css = escape(css);
//derive the absolute path for the normalize helper
var normalizeName = './normalize'; // normalize.convertURIBase('normalize.js', req.toUrl('./normalize.js'), require.toUrl('.'));
//the code below overrides async require functionality to ensure instant layer css injection
//it then runs normalization and injection
//normalization is based on determining the absolute pathname of the html page
//then determining the absolute baseurl url
//normalization is then performed from the absolute baseurl to the absolute pathname
write(''
+ 'for (var c in requirejs.s.contexts) { requirejs.s.contexts[c].nextTick = function(f){f()} } \n'
+ 'require([\'css\', \'' + normalizeName + '\', \'require\'], function(css, normalize, require) { \n'
+ 'var pathname = window.location.pathname.split(\'/\'); \n'
+ 'pathname.pop(); \n'
+ 'pathname = pathname.join(\'/\') + \'/\'; \n'
+ 'var baseUrl = require.toUrl(\'.\'); \n'
+ 'baseUrl = normalize.convertURIBase(baseUrl, pathname, \'/\'); \n'
+ 'if (baseUrl.substr(0, 1) != \'/\') \n'
+ ' baseUrl = \'/\' + baseUrl; \n'
// + 'css.inject(normalize(\'' + css + '\', baseUrl, pathname)); \n'
+ 'css.inject(\'' + css + '\'); \n'
+ '}); \n'
+ 'for (var c in requirejs.s.contexts) { requirejs.s.contexts[c].nextTick = requirejs.nextTick; } \n'
);
// debug to see what we generate.
// saveFile(path + '.build.css', css);
}
//clear layer buffer for next layer
_layerBuffer = [];
};
return cssAPI;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment