/*
|
* This module can be executed standalone in node, to build CSS files, inlining imports, and
|
* pre-processing extensions for faster run-time execution. This module is also
|
* used by the AMD build process.
|
*/
|
var operatorMatch = {
|
'{': '}',
|
'[': ']',
|
'(': ')'
|
};
|
var nextId = 1;
|
|
document = {
|
createElement: function(){
|
return {style:
|
{// TODO: may want to import static has features to determine if some of these exist
|
}
|
};
|
},
|
getElementsByTagName: function(){
|
return [];
|
},
|
addEventListener: function(){}
|
};
|
navigator = {
|
userAgent: "build"
|
};
|
|
var pseudoDefine = function(id, deps, factory){
|
parse = factory({
|
isBuild: true,
|
isTagSupported: function(){
|
return true;
|
}
|
});
|
};
|
var requiredModules = [], base64Module;
|
|
if(typeof define == 'undefined'){
|
var fs = require('fs'),
|
pathModule = require('path'),
|
parse;
|
define = pseudoDefine;
|
require('./core/parser');
|
}else{
|
define(['build/fs', 'build/fileUtils', './build/base64'], function(fsModule, fileUtils, base64){
|
fs = fsModule;
|
base64Module = base64;
|
// must create our own path module for Rhino :/
|
pathModule = {
|
resolve: function(base, target){
|
return fileUtils.compactPath(base.replace(/[^\/]+$/, '') + target);
|
},
|
dirname: function(path){
|
return path.replace(/[\/\\][^\/\\]*$/, '');
|
},
|
relative: function(basePath, path){
|
basePath = this.dirname(basePath);
|
var baseParts = basePath.split(/[\/\\]/);
|
var parts = path.split(/[\/\\]/);
|
var l = parts.length;
|
for (var i = 0; i < l; i++){
|
if (parts[i] !== baseParts[i]){
|
break;
|
}
|
}
|
parts.splice(0, i);
|
for (; i < baseParts.length; i++){
|
parts.unshift('..');
|
}
|
return parts.join('/');
|
},
|
join: function(base, target){
|
return fileUtils.compactPath((base[base.length - 1] == '/' ? base : (base + '/')) + target);
|
}
|
};
|
return function(xstyleText){
|
var define = pseudoDefine;
|
var vm;
|
try {
|
require(['dojo/node!vm']);
|
vm = require('dojo/node!vm');
|
} catch (e) {
|
}
|
vm ?
|
vm.runInNewContext(xstyleText, {
|
define: define,
|
console: console
|
}, 'xstyle/core/parser.js') :
|
eval(xstyleText);
|
return processCss;
|
};
|
});
|
}
|
function main(source, target){
|
var imported = {};
|
var basePath = source.replace(/[^\/]*$/, '');
|
var cssText = fs.readFileSync(source).toString("utf-8");
|
var processed = processCss(cssText, basePath, basePath);
|
var output = processed.standardCss;
|
if(processed.xstyleCss){
|
output += 'x-xstyle{content:"' +
|
processed.xstyleCss.replace(/["\\\n\r]/g, '\\$&') +
|
'";}';
|
}
|
if(target){
|
fs.writeFileSync(target, output);
|
}else{
|
console.log(output);
|
}
|
}
|
function minify(cssText){
|
return cssText.
|
replace(/\/\*([^\*]|\*[^\/])*\*\//g, ' ').
|
replace(/\s*("(\\\\|[^\"])*"|'(\\\\|[^\'])*'|[;}{:])\s*/g,"$1").
|
replace(/[^\x00-\x7F]([0-9a-fA-F])?/g, function(character, hexComesNext){
|
// escape non-ascii characters to be safe
|
return '\\' + character.charCodeAt(0).toString(16) + (hexComesNext ? ' ' + hexComesNext : '');
|
});
|
}
|
var mimeTypes = {
|
eot: "application/vnd.ms-fontobject",
|
woff: "application/font-woff",
|
gif: "image/gif",
|
jpg: "image/jpeg",
|
jpeg: "image/jpeg",
|
png: "image/png"
|
}
|
function processCss(cssText, basePath, cssPath, inlineAllResources){
|
function XRule(){
|
}
|
var ruleCount = 0;
|
XRule.prototype = {
|
newCall: function(name){
|
var call = new Call(name);
|
call.parent = this;
|
return call;
|
},
|
childRules: 0,
|
newRule: function(name){
|
var rule = (this.rules || (this.rules = {}))[name] = new XRule();
|
rule.ruleIndex = this.childRules++;
|
rule.parent = this;
|
return rule;
|
},
|
getDefinition: function(name, includeRules){
|
var parentRule = this;
|
do{
|
var target = parentRule.definitions && parentRule.definitions[name]
|
|| (includeRules && parentRule.rules && parentRule.rules[name]);
|
parentRule = parentRule.parent;
|
}while(!target && parentRule);
|
if(target){
|
target.name = name;
|
}
|
return target;
|
},
|
declareDefinition: function(name, value, conditional){
|
// TODO: access staticHasFeatures to check conditional
|
var valueString = {
|
toString: function(){
|
return value.toString(2);
|
}
|
};
|
(this.xstyleCss = this.xstyleCss || []).push(name + '=', valueString, ';');
|
var definitions = (this.definitions || (this.definitions = {}));
|
definitions[name] = value || new XRule();
|
},
|
setValue: function(name, value){
|
var target = this.getDefinition(name);
|
var browserCss = this.browserCss = this.browserCss || [];
|
if(!this.ruleStarted && !this.root){
|
this.ruleStarted = true;
|
browserCss.push(this.selector, '{');
|
}
|
if(!target){
|
browserCss.push(name, ':', value, ';');
|
}
|
if(target || typeof value == 'object'){
|
if(!this.xstyleStarted){
|
this.xstyleStarted = true;
|
this.xstyleCss = this.xstyleCss || [];
|
}
|
this.xstyleCss.push(name + ':' + value + ';');
|
}
|
},
|
onArguments: function(){},
|
setMediaSelector: function(selector){
|
this.selector = '';
|
browserCss.push(selector, '{');
|
this.isMediaBlock = true;
|
},
|
toString: function(mode){
|
var str = '';
|
str += this.xstyleCss ? this.xstyleCss.join('') : '';
|
for(var i in this.rules){
|
var rule = this.rules[i];
|
if(rule.ref && !rule.creating){
|
str += rule.toString(1);
|
}
|
}
|
if(!this.root && !this.isMediaBlock && this.xstyleStarted && (str || mode != 1)){
|
str = ((mode == 2 && this.bases) || this.tagName || '') + '{' + this.ref + str.replace(/\s+/g, ' ') + '}';
|
}
|
return str;
|
},
|
onRule: function(){
|
if(this.browserCss){
|
this.browserCss.push('}');
|
this.ref = '/' + ruleCount;
|
ruleCount++;
|
browserCss.push(this.browserCss.join(''));
|
}
|
if(this.isMediaBlock){
|
browserCss.push('}');
|
}
|
},
|
extend: function(derivative, fullExtension){
|
var definitions = this.definitions;
|
if(definitions && fullExtension){
|
// TODO: need to mixin this in, if it already exists
|
derivative.definitions = Object.create(definitions);
|
}
|
(derivative.bases = derivative.bases || []).push(this.name);
|
}
|
};
|
// a class representing function calls
|
function Call(value){
|
// we store the caller and the arguments
|
this.caller = value;
|
this.args = [];
|
}
|
var CallPrototype = Call.prototype = new XRule;
|
CallPrototype.declareProperty = CallPrototype.setValue = function(name, value){
|
// handle these both as addition of arguments
|
this.args.push(value);
|
};
|
CallPrototype.toString = function(){
|
var operator = this.operator;
|
return operator + this.args + operatorMatch[operator];
|
};
|
|
function insertRule(cssText){
|
//browserCss.push(cssText);
|
}
|
function correctUrls(cssText, path){
|
// correct all the URLs in the stylesheets
|
// determine the directory path
|
path = pathModule.dirname(path) + '/';
|
//console.log("starting path", basePath , path);
|
// compute the relative path from where we are to the base path where the stylesheet will go
|
var relativePath = pathModule.relative(basePath, path);
|
return cssText.replace(/url\s*\(\s*['"]?([^'"\)]*)['"]?\s*\)/g, function(t, url){
|
if(url.match(/^data:/)){
|
return url;
|
}
|
if(inlineAllResources || /#inline$/.test(url)){
|
// we can inline the resource
|
suffix = url.match(/\.(\w+)(#|\?|$)/);
|
suffix = suffix && suffix[1];
|
url = url.replace(/[\?#].*/,'');
|
|
if(typeof java !== 'undefined'){
|
// reading binary is hard in rhino
|
var file = new java.io.File(pathModule.join(path, url));
|
var length = file.length();
|
var fis = new java.io.FileInputStream(file);
|
var bytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, length);
|
fis.read(bytes, 0, length);
|
var jsBytes = new Array(length);
|
for(var i = 0; i < bytes.length;i++){
|
var singleByte = bytes[i];
|
if(singleByte < 0){
|
singleByte = 256 + singleByte;
|
}
|
jsBytes[i] = singleByte;
|
}
|
var moduleText =base64Module.encode(jsBytes);
|
}else{
|
// in node base64 encoding is easy
|
var moduleText = fs.readFileSync(pathModule.join(path, url)).toString("base64");
|
}
|
return 'url("data:' + (mimeTypes[suffix] || 'application/octet-stream') +
|
';base64,' + moduleText + '")';
|
}
|
// or we adjust the URL
|
var adjustedPath = pathModule.resolve(relativePath, url);
|
return 'url("' + adjustedPath + '")';
|
});
|
};
|
parse.getStyleSheet = function(importRule, sequence, styleSheet){
|
var importValue = sequence[1];
|
var path;
|
try{
|
if(importValue.operator === '('){
|
// a url() call
|
var firstArg = importValue.args[0];
|
// extract from a literal string, or just grab the raw text
|
// note that the correctUrls should have already resolved this path
|
path = firstArg.length? firstArg[0].value : firstArg.toString();
|
}else{
|
// assume it is a string
|
path = pathModule.resolve(styleSheet.href, importValue.value);
|
}
|
}catch(error){
|
console.error('Can not parse import value of ', error, importValue, firstArg);
|
}
|
var localSource = '';
|
try{
|
localSource = fs.readFileSync(path).toString('utf-8');
|
}catch(e){
|
console.error(e.stack || e);
|
}
|
localSource = correctUrls(localSource, path);
|
return {
|
localSource: localSource,
|
href: path || '.',
|
insertRule: insertRule,
|
cssRules: []
|
};
|
};
|
var browserCss = [];//[correctUrls(cssText, basePath + "placeholder.css")];
|
var rootRule = new XRule;
|
rootRule.root = true;
|
parse(rootRule, correctUrls(cssText, cssPath),
|
{href: cssPath || '.', cssRules:[], insertRule: insertRule});
|
rootRule.definitions = {
|
Math:1,
|
require:1,
|
item: 1,
|
'native': 1,
|
prefixed: 1
|
}
|
function visit(parent){
|
//browserCss.push(parent.selector + '{' + parent.cssText + '}');
|
/*for(var i in parent.variables){
|
if(!intrinsicVariables.hasOwnProperty(i)){
|
xstyleCss.push(i,'=',parent.variables[i]);
|
}
|
}*/
|
}
|
visit(rootRule);
|
return {
|
standardCss: minify(browserCss.join('')),
|
xstyleCss: rootRule.toString(),
|
requiredModules: requiredModules
|
};
|
}
|
if(typeof module != 'undefined' && require.main == module){
|
main.apply(this, process.argv.slice(2));
|
}
|