using less for mifos custom styles. using require css and less plugins to dynamically load styles

This commit is contained in:
Silvio Montanari 2013-07-07 16:59:44 +10:00
parent 52346a28c3
commit e1582b10cc
16 changed files with 5541 additions and 65 deletions

View File

@ -4,10 +4,6 @@
<meta charset="utf-8">
<title>MifosX prototype</title>
<style>[data-ng-cloak] { display: none !important; }</style>
<link rel="stylesheet" href="stylesheets/skeleton/base.css">
<link rel="stylesheet" href="stylesheets/skeleton/skeleton.css">
<link rel="stylesheet" href="stylesheets/skeleton/layout.css">
<link rel="stylesheet" href="stylesheets/mifosX.css">
</head>
<body data-ng-cloak data-ng-controller="MainController">
<div class="container">

View File

@ -6,35 +6,37 @@
'angular-mocks': '../lib/angular/angular-mocks',
'underscore': '../lib/underscore/underscore',
'webstorage': '../lib/angular-webstorage',
'test': '../test/functional'
'require-css': '../lib/require-css',
'require-less': '../lib/require-less',
'styles': '../stylesheets',
'test': '../test/functional',
},
shim: {
'angular': {
exports: 'angular'
},
'angular-resource': {
deps: ['angular']
},
'webstorage': {
deps: ['angular']
},
'angular': { exports: 'angular' },
'angular-resource': { deps: ['angular'] },
'angular-mocks': { deps: ['angular'] },
'webstorage': { deps: ['angular'] },
'mifosX': {
deps: ['angular', 'angular-resource', 'webstorage'],
exports: 'mifosX'
}
},
packages: [
{
name: 'css',
location: '../lib/require-css',
main: 'css'
},
{
name: 'less',
location: '../lib/require-less',
main: 'less'
}
]
});
require(['mifosXComponents', 'mifosX', 'underscore'], function(components) {
var dependencies = _.reduce(_.keys(components), function(list, group) {
return list.concat(_.map(components[group], function(name) { return group + "/" + name; }));
}, [
'test/testInitializer',
'routes',
'webstorage-configuration'
]);
require(dependencies, function(testMode) {
require(['mifosXComponents', 'mifosXStyles'], function() {
require(['test/testInitializer'], function(testMode) {
if (!testMode) {
angular.bootstrap(document, ["MifosX_Application"]);
}

View File

@ -1,17 +1,26 @@
define({
models: [
'User',
'roleMap'
],
controllers: [
'MainController',
'LoginFormController'
],
services: [
'ResourceFactoryProvider',
'HttpServiceProvider',
'AuthenticationService',
'SessionManager'
],
directives: []
define(['underscore', 'mifosX'], function() {
var components = {
models: [
'User',
'roleMap'
],
controllers: [
'MainController',
'LoginFormController'
],
services: [
'ResourceFactoryProvider',
'HttpServiceProvider',
'AuthenticationService',
'SessionManager'
],
directives: []
};
require(_.reduce(_.keys(components), function(list, group) {
return list.concat(_.map(components[group], function(name) { return group + "/" + name; }));
}, [
'routes',
'webstorage-configuration'
]));
});

16
js/mifosXStyles.js Normal file
View File

@ -0,0 +1,16 @@
define(['underscore'], function() {
var styles = {
css: [
'skeleton/base',
'skeleton/skeleton',
'skeleton/layout'
],
less: [
'mifosX'
]
};
require(_.reduce(_.keys(styles), function(list, pluginName) {
return list.concat(_.map(styles[pluginName], function(stylename) { return pluginName + "!styles/" + stylename; }));
}, []));
});

1
lib/require-css/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
example/www-built

View File

@ -0,0 +1,251 @@
define(['require', './normalize'], function(req, normalize) {
var nodePrint = function() {};
if (requirejs.tools)
requirejs.tools.useLib(function(req) {
req(['node/print'], function(_nodePrint) {
nodePrint = _nodePrint;
}, function(){});
});
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);
nodePrint('Compressed CSS output to ' + Math.round(css.length / csslen * 100) + '%.');
return css;
}
catch(e) {
nodePrint('Compression module not installed. Use "npm install csso -g" to enable.');
return css;
}
}
nodePrint('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");
}
// NB add @media query support for media imports
var importRegEx = /@import\s*(url)?\s*(('([^']*)'|"([^"]*)")|\(('([^']*)'|"([^"]*)"|([^\)]*))\))\s*;?/g;
var loadCSSFile = function(fileUrl) {
var css = loadFile(fileUrl);
// normalize the css (except import statements)
css = normalize(css, fileUrl, baseUrl, cssBase);
// detect all import statements in the css and normalize
var importUrls = [];
var importIndex = [];
var importLength = [];
var match;
while (match = importRegEx.exec(css)) {
var importUrl = match[4] || match[5] || match[7] || match[8] || match[9];
// normalize import url
if (importUrl.substr(importUrl.length - 5, 5) != '.less' && importUrl.substr(importUrl.length - 4, 4) != '.css')
importUrl += '.css';
// contains a protocol
if (importUrl.match(/:\/\//))
continue;
// relative to css base
if (importUrl.substr(0, 1) == '/' && cssBase)
importUrl = cssBase + importUrl;
else
importUrl = baseUrl + importUrl;
importUrls.push(importUrl);
importIndex.push(importRegEx.lastIndex - match[0].length);
importLength.push(match[0].length);
}
// load the import stylesheets and substitute into the css
for (var i = 0; i < importUrls.length; i++)
(function(i) {
var importCSS = loadCSSFile(importUrls[i]);
css = css.substr(0, importIndex[i]) + importCSS + css.substr(importIndex[i] + importLength[i]);
var lenDiff = importCSS.length - importLength[i];
for (var j = i + 1; j < importUrls.length; j++)
importIndex[j] += lenDiff;
})(i);
return css;
}
var baseUrl;
var cssBase;
var curModule;
cssAPI.load = function(name, req, load, config, parse) {
if (!baseUrl)
baseUrl = config.baseUrl;
if (!cssBase)
cssBase = config.cssBase;
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;
name += !parse ? '.css' : '.less';
var fileUrl = req.toUrl(name);
//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
_cssBuffer[name] = loadCSSFile(fileUrl);
// parse if necessary
if (parse)
_cssBuffer[name] = parse(_cssBuffer[name]);
load();
}
cssAPI.normalize = function(name, normalize) {
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 = [];
var _cssBuffer = [];
cssAPI.write = function(pluginName, moduleName, write, parse) {
//external URLS don't get added (just like JS requires)
if (moduleName.substr(0, 7) == 'http://' || moduleName.substr(0, 8) == 'https://')
return;
var resourceName = moduleName + (!parse ? '.css' : '.less');
_layerBuffer.push(_cssBuffer[resourceName]);
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;
if (separateCSS)
write.asModule(pluginName + '!' + moduleName, 'define(function(){})');
else
write("requirejs.s.contexts._.nextTick = function(f){f()}; require(['css'], function(css) { css.addBuffer('" + resourceName + "'); }); requirejs.s.contexts._.nextTick = requirejs.nextTick;");
}
cssAPI.onLayerEnd = function(write, data, parser) {
firstWrite = true;
//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) {
nodePrint('Writing CSS! file: ' + data.name + '\n');
//calculate the css output path for this layer
var path = this.config.appDir ? this.config.baseUrl + data.name + '.css' : cssAPI.config.out.replace(/\.js$/, '.css');
//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 = escape(compress(css));
//the code below overrides async require functionality to ensure instant buffer injection
write("requirejs.s.contexts._.nextTick = function(f){f()}; require(['css'], function(css) { css.setBuffer('" + css + (parser ? "', true" : "'") + "); }); requirejs.s.contexts._.nextTick = requirejs.nextTick; ");
}
//clear layer buffer for next layer
_layerBuffer = [];
}
return cssAPI;
});

435
lib/require-css/css.js Normal file
View File

@ -0,0 +1,435 @@
/*
* Require-CSS RequireJS css! loader plugin
* Guy Bedford 2013
* MIT
*/
/*
*
* Usage:
* require(['css!./mycssFile']);
*
* NB leave out the '.css' extension.
*
* - Fully supports cross origin CSS loading
* - Works with builds
*
* Tested and working in (up to latest versions as of March 2013):
* Android
* iOS 6
* IE 6 - 10
* Chome 3 - 26
* Firefox 3.5 - 19
* Opera 10 - 12
*
* browserling.com used for virtual testing environment
*
* Credit to B Cavalier & J Hann for the elegant IE 6 - 9 hack.
*
* Sources that helped along the way:
* - https://developer.mozilla.org/en-US/docs/Browser_detection_using_the_user_agent
* - http://www.phpied.com/when-is-a-stylesheet-really-loaded/
* - https://github.com/cujojs/curl/blob/master/src/curl/plugin/css.js
*
*/
define(['./normalize'], function(normalize) {
function indexOf(a, e) { for (var i=0, l=a.length; i < l; i++) if (a[i] === e) return i; return -1 }
if (typeof window == 'undefined')
return { load: function(n, r, load){ load() } };
// set to true to enable test prompts for device testing
var testing = false;
var head = document.getElementsByTagName('head')[0];
var engine = window.navigator.userAgent.match(/Trident\/([^ ;]*)|AppleWebKit\/([^ ;]*)|Opera\/([^ ;]*)|rv\:([^ ;]*)(.*?)Gecko\/([^ ;]*)|MSIE\s([^ ;]*)/);
var hackLinks = false;
if (!engine) {}
else if (engine[1] || engine[7]) {
hackLinks = parseInt(engine[1]) < 6 || parseInt(engine[7]) <= 9;
engine = 'trident';
}
else if (engine[2]) {
// unfortunately style querying still doesnt work with onload callback in webkit
hackLinks = true;
engine = 'webkit';
}
else if (engine[3]) {
// engine = 'opera';
}
else if (engine[4]) {
hackLinks = parseInt(engine[4]) < 18;
engine = 'gecko';
}
else if (testing)
alert('Engine detection failed');
//main api object
var cssAPI = {};
var absUrlRegEx = /^\/|([^\:\/]*:)/;
cssAPI.pluginBuilder = './css-builder';
// used by layer builds to register their css buffers
// the current layer buffer items (from addBuffer)
var curBuffer = [];
// the callbacks for buffer loads
var onBufferLoad = {};
// the full list of resources in the buffer
var bufferResources = [];
cssAPI.addBuffer = function(resourceId) {
// just in case layer scripts are included twice, also check
// against the previous buffers
if (indexOf(curBuffer, resourceId) != -1)
return;
if (indexOf(bufferResources, resourceId) != -1)
return;
curBuffer.push(resourceId);
bufferResources.push(resourceId);
}
cssAPI.setBuffer = function(css, isLess) {
var pathname = window.location.pathname.split('/');
pathname.pop();
pathname = pathname.join('/') + '/';
var baseParts = require.toUrl('base_url').split('/');
baseParts.pop();
var baseUrl = baseParts.join('/') + '/';
baseUrl = normalize.convertURIBase(baseUrl, pathname, '/');
if (!baseUrl.match(absUrlRegEx))
baseUrl = '/' + baseUrl;
if (baseUrl.substr(baseUrl.length - 1, 1) != '/')
baseUrl = baseUrl + '/';
cssAPI.inject(normalize(css, baseUrl, pathname));
// set up attach callback if registered
// clear the current buffer for the next layer
// (just the less or css part as we have two buffers in one effectively)
for (var i = 0; i < curBuffer.length; i++) {
// find the resources in the less or css buffer dependening which one this is
if ((isLess && curBuffer[i].substr(curBuffer[i].length - 5, 5) == '.less') ||
(!isLess && curBuffer[i].substr(curBuffer[i].length - 4, 4) == '.css')) {
(function(resourceId) {
// mark that the onBufferLoad is about to be called (set to true if not already a callback function)
onBufferLoad[resourceId] = onBufferLoad[resourceId] || true;
// set a short timeout (as injection isn't instant in Chrome), then call the load
setTimeout(function() {
if (typeof onBufferLoad[resourceId] == 'function')
onBufferLoad[resourceId]();
// remove from onBufferLoad to indicate loaded
delete onBufferLoad[resourceId];
}, 7);
})(curBuffer[i]);
// remove the current resource from the buffer
curBuffer.splice(i--, 1);
}
}
}
cssAPI.attachBuffer = function(resourceId, load) {
// attach can happen during buffer collecting, or between injection and callback
// we assume it is not possible to attach multiple callbacks
// requirejs plugin load function ensures this by queueing duplicate calls
// check if the resourceId is in the current buffer
for (var i = 0; i < curBuffer.length; i++)
if (curBuffer[i] == resourceId) {
onBufferLoad[resourceId] = load;
return true;
}
// check if the resourceId is waiting for injection callback
// (onBufferLoad === true is a shortcut indicator for this)
if (onBufferLoad[resourceId] === true) {
onBufferLoad[resourceId] = load;
return true;
}
// if it's in the full buffer list and not either of the above, its loaded already
if (indexOf(bufferResources, resourceId) != -1) {
load();
return true;
}
}
var webkitLoadCheck = function(link, callback) {
setTimeout(function() {
for (var i = 0; i < document.styleSheets.length; i++) {
var sheet = document.styleSheets[i];
if (sheet.href == link.href)
return callback();
}
webkitLoadCheck(link, callback);
}, 10);
}
var mozillaLoadCheck = function(style, callback) {
setTimeout(function() {
try {
style.sheet.cssRules;
return callback();
} catch (e){}
mozillaLoadCheck(style, callback);
}, 10);
}
// ie link detection, as adapted from https://github.com/cujojs/curl/blob/master/src/curl/plugin/css.js
if (engine == 'trident' && hackLinks) {
var ieStyles = [],
ieQueue = [],
ieStyleCnt = 0;
var ieLoad = function(url, callback) {
var style;
ieQueue.push({
url: url,
cb: callback
});
style = ieStyles.shift();
if (!style && ieStyleCnt++ < 12) {
style = document.createElement('style');
head.appendChild(style);
}
ieLoadNextImport(style);
}
var ieLoadNextImport = function(style) {
var curImport = ieQueue.shift();
if (!curImport) {
style.onload = noop;
ieStyles.push(style);
return;
}
style.onload = function() {
curImport.cb(curImport.ss);
ieLoadNextImport(style);
};
var curSheet = style.styleSheet;
curImport.ss = curSheet.imports[curSheet.addImport(curImport.url)];
}
}
// uses the <link> load method
var createLink = function(url) {
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = url;
return link;
}
var noop = function(){}
cssAPI.linkLoad = function(url, callback) {
var timeout = setTimeout(function() {
if (testing) alert('timeout');
callback();
}, waitSeconds * 1000 - 100);
var _callback = function() {
clearTimeout(timeout);
if (link)
link.onload = noop;
// for style querying, a short delay still seems necessary
setTimeout(callback, 7);
}
if (!hackLinks) {
var link = createLink(url);
link.onload = _callback;
head.appendChild(link);
}
// hacks
else {
if (engine == 'webkit') {
var link = createLink(url);
webkitLoadCheck(link, _callback);
head.appendChild(link);
}
else if (engine == 'gecko') {
var style = document.createElement('style');
style.textContent = '@import "' + url + '"';
mozillaLoadCheck(style, _callback);
head.appendChild(style);
}
else if (engine == 'trident')
ieLoad(url, _callback);
}
}
/* injection api */
var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'];
var fileCache = {};
var get = function(url, callback, errback) {
if (fileCache[url]) {
callback(fileCache[url]);
return;
}
var xhr, i, progId;
if (typeof XMLHttpRequest !== 'undefined')
xhr = new XMLHttpRequest();
else if (typeof ActiveXObject !== 'undefined')
for (i = 0; i < 3; i += 1) {
progId = progIds[i];
try {
xhr = new ActiveXObject(progId);
}
catch (e) {}
if (xhr) {
progIds = [progId]; // so faster next time
break;
}
}
xhr.open('GET', url, requirejs.inlineRequire ? false : true);
xhr.onreadystatechange = function (evt) {
var status, err;
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
status = xhr.status;
if (status > 399 && status < 600) {
//An http 4xx or 5xx error. Signal an error.
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
errback(err);
}
else {
fileCache[url] = xhr.responseText;
callback(xhr.responseText);
}
}
};
xhr.send(null);
}
//uses the <style> load method
var styleCnt = 0;
var curStyle;
cssAPI.inject = function(css) {
if (styleCnt < 31) {
curStyle = document.createElement('style');
curStyle.type = 'text/css';
head.appendChild(curStyle);
styleCnt++;
}
if (curStyle.styleSheet)
curStyle.styleSheet.cssText += css;
else
curStyle.appendChild(document.createTextNode(css));
}
// NB add @media query support for media imports
var importRegEx = /@import\s*(url)?\s*(('([^']*)'|"([^"]*)")|\(('([^']*)'|"([^"]*)"|([^\)]*))\))\s*;?/g;
var pathname = window.location.pathname.split('/');
pathname.pop();
pathname = pathname.join('/') + '/';
var loadCSS = function(fileUrl, callback, errback) {
//make file url absolute
if (!fileUrl.match(absUrlRegEx))
fileUrl = '/' + normalize.convertURIBase(fileUrl, pathname, '/');
get(fileUrl, function(css) {
// normalize the css (except import statements)
css = normalize(css, fileUrl, pathname);
// detect all import statements in the css and normalize
var importUrls = [];
var importIndex = [];
var importLength = [];
var match;
while (match = importRegEx.exec(css)) {
var importUrl = match[4] || match[5] || match[7] || match[8] || match[9];
importUrls.push(importUrl);
importIndex.push(importRegEx.lastIndex - match[0].length);
importLength.push(match[0].length);
}
// load the import stylesheets and substitute into the css
var completeCnt = 0;
for (var i = 0; i < importUrls.length; i++)
(function(i) {
loadCSS(importUrls[i], function(importCSS) {
css = css.substr(0, importIndex[i]) + importCSS + css.substr(importIndex[i] + importLength[i]);
var lenDiff = importCSS.length - importLength[i];
for (var j = i + 1; j < importUrls.length; j++)
importIndex[j] += lenDiff;
completeCnt++;
if (completeCnt == importUrls.length) {
callback(css);
}
}, errback);
})(i);
if (importUrls.length == 0)
callback(css);
}, errback);
}
cssAPI.normalize = function(name, normalize) {
if (name.substr(name.length - 4, 4) == '.css')
name = name.substr(0, name.length - 4);
return normalize(name);
}
var waitSeconds;
var alerted = false;
cssAPI.load = function(cssId, req, load, config, parse) {
waitSeconds = waitSeconds || config.waitSeconds || 7;
var resourceId = cssId + (!parse ? '.css' : '.less');
// attach the load function to a buffer if there is one in registration
// if not, we do a full injection load
if (cssAPI.attachBuffer(resourceId, load))
return;
fileUrl = req.toUrl(resourceId);
if (!alerted && testing) {
alert(hackLinks ? 'hacking links' : 'not hacking');
alerted = true;
}
if (!parse) {
cssAPI.linkLoad(fileUrl, load);
}
else {
loadCSS(fileUrl, function(css) {
// run parsing after normalization - since less is a CSS subset this works fine
if (parse)
css = parse(css, function(css) {
cssAPI.inject(css);
setTimeout(load, 7);
});
});
}
}
if (testing)
cssAPI.inspect = function() {
if (stylesheet.styleSheet)
return stylesheet.styleSheet.cssText;
else if (stylesheet.innerHTML)
return stylesheet.innerHTML;
}
return cssAPI;
});

View File

@ -0,0 +1,138 @@
/*
* css.normalize.js
*
* CSS Normalization
*
* CSS paths are normalized based on an optional basePath and the RequireJS config
*
* Usage:
* normalize(css, fromBasePath, toBasePath);
*
* css: the stylesheet content to normalize
* fromBasePath: the absolute base path of the css relative to any root (but without ../ backtracking)
* toBasePath: the absolute new base path of the css relative to the same root
*
* Absolute dependencies are left untouched.
*
* Urls in the CSS are picked up by regular expressions.
* These will catch all statements of the form:
*
* url(*)
* url('*')
* url("*")
*
* @import '*'
* @import "*"
*
* (and so also @import url(*) variations)
*
* For urls needing normalization
*
*/
define(['require', 'module'], function(require, module) {
// regular expression for removing double slashes
// eg http://www.example.com//my///url/here -> http://www.example.com/my/url/here
var slashes = /([^:])\/+/g
var removeDoubleSlashes = function(uri) {
return uri.replace(slashes, '$1/');
}
// given a relative URI, and two absolute base URIs, convert it from one base to another
var protocolRegEx = /[^\:\/]*:\/\/([^\/])*/
function convertURIBase(uri, fromBase, toBase) {
if(uri.indexOf("data:") === 0)
return uri;
uri = removeDoubleSlashes(uri);
// absolute urls are left in tact
if (uri.match(/^\//) || uri.match(protocolRegEx))
return uri;
// if toBase specifies a protocol path, ensure this is the same protocol as fromBase, if not
// use absolute path at fromBase
var toBaseProtocol = toBase.match(protocolRegEx);
var fromBaseProtocol = fromBase.match(protocolRegEx);
if (fromBaseProtocol && (!toBaseProtocol || toBaseProtocol[1] != fromBaseProtocol[1] || toBaseProtocol[2] != fromBaseProtocol[2]))
return absoluteURI(uri, fromBase);
else {
return relativeURI(absoluteURI(uri, fromBase), toBase);
}
};
// given a relative URI, calculate the absolute URI
function absoluteURI(uri, base) {
if (uri.substr(0, 2) == './')
uri = uri.substr(2);
var baseParts = base.split('/');
var uriParts = uri.split('/');
baseParts.pop();
while (curPart = uriParts.shift())
if (curPart == '..')
baseParts.pop();
else
baseParts.push(curPart);
return baseParts.join('/');
};
// given an absolute URI, calculate the relative URI
function relativeURI(uri, base) {
// reduce base and uri strings to just their difference string
var baseParts = base.split('/');
baseParts.pop();
base = baseParts.join('/') + '/';
i = 0;
while (base.substr(i, 1) == uri.substr(i, 1))
i++;
while (base.substr(i, 1) != '/')
i--;
base = base.substr(i + 1);
uri = uri.substr(i + 1);
// each base folder difference is thus a backtrack
baseParts = base.split('/');
var uriParts = uri.split('/');
out = '';
while (baseParts.shift())
out += '../';
// finally add uri parts
while (curPart = uriParts.shift())
out += curPart + '/';
return out.substr(0, out.length - 1);
};
var normalizeCSS = function(source, fromBase, toBase, cssBase) {
fromBase = removeDoubleSlashes(fromBase);
toBase = removeDoubleSlashes(toBase);
var urlRegEx = /@import\s*("([^"]*)"|'([^']*)')|url\s*\(\s*(\s*"([^"]*)"|'([^']*)'|[^\)]*\s*)\s*\)/ig;
var result, url, source;
while (result = urlRegEx.exec(source)) {
url = result[3] || result[2] || result[5] || result[6] || result[4];
var newUrl;
if (cssBase && url.substr(0, 1) == '/')
newUrl = cssBase + url;
else
newUrl = convertURIBase(url, fromBase, toBase);
var quoteLen = result[5] || result[6] ? 1 : 0;
source = source.substr(0, urlRegEx.lastIndex - url.length - quoteLen - 1) + newUrl + source.substr(urlRegEx.lastIndex - quoteLen - 1);
urlRegEx.lastIndex = urlRegEx.lastIndex + (newUrl.length - url.length);
}
return source;
};
normalizeCSS.convertURIBase = convertURIBase;
return normalizeCSS;
});

1
lib/require-less/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
example/www-built

View File

@ -0,0 +1,42 @@
define(['css/css-builder', './lessc-server', 'require'], function(css, lessc, req) {
var less = {};
var baseParts = req.toUrl('base_url').split('/');
baseParts.pop();
var baseUrl = baseParts.join('/');
// include the base url as a path
var parser = new lessc.Parser({
paths: [baseUrl + '/']
});
var parseLess = function(less) {
var CSS;
parser.parse(less, function(err, tree) {
if (err)
throw err;
CSS = tree.toCSS();
});
return CSS;
}
less.normalize = function(name, normalize) {
if (name.substr(name.length - 5, 5) == '.less')
name = name.substr(0, name.length - 5);
return normalize(name);
}
less.load = function(name, req, load, config) {
css.load(name, req, load, config, parseLess);
}
less.write = function(pluginName, moduleName, write) {
css.write(pluginName, moduleName, write, parseLess);
}
less.onLayerEnd = function(write, data) {
css.onLayerEnd(write, data, true);
}
return less;
});

46
lib/require-less/less.js Normal file
View File

@ -0,0 +1,46 @@
define(['css', 'require'], function(css, require) {
var less = {};
less.pluginBuilder = './less-builder';
if (typeof window == 'undefined') {
less.load = function(n, r, load) { load(); }
return less;
}
//copy api methods from the css plugin
less.normalize = function(name, normalize) {
if (name.substr(name.length - 5, 5) == '.less')
name = name.substr(0, name.length - 5);
name = normalize(name);
return name;
}
less.parse = function(less, callback) {
require(['./lessc'], function(lessc) {
var css;
var parser = new lessc.Parser();
parser.parse(less, function(err, tree) {
if (err)
throw err;
try {
css = tree.toCSS();
}
catch(e) {
throw new Error("LESS parse error: " + e.type + ", " + e.message);
}
//instant callback luckily for builds
callback(css);
});
});
}
less.load = function(lessId, req, load, config) {
css.load(lessId, req, load, config, less.parse);
}
return less;
});

View File

@ -0,0 +1,96 @@
define(['./lessc'], function(less) {
if (['node', 'rhino'].indexOf(less.mode) == -1) {
throw new Error('Environment not supported by require-less builder: ' + less.mode);
}
var readFile = (function() {
if (less.mode === 'node') {
var fs = require.nodeRequire('fs');
var path = require.nodeRequire('path');
return function(pathname) {
return fs.readFileSync(pathname, 'utf-8');
};
} else if (less.mode === 'rhino') {
return function(pathname) {
return readFile(pathname, 'UTF-8');
};
}
}());
var checkPath = (function() {
if (less.mode === 'node') {
var fs = require.nodeRequire('fs');
var path = require.nodeRequire('path');
return function(pathname, file) {
try {
pathname = path.join(pathname, file);
fs.statSync(pathname);
return pathname;
} catch (e) {
return null;
}
};
} else if (less.mode === 'rhino') {
return function(pathname, file) {
var f = new java.io.File(pathname, file);
if (f.isFile()) {
return f.getPath();
} else {
return null;
}
};
}
}());
less.Parser.importer = function (file, paths, callback, env) {
var pathname, data;
// TODO: Undo this at some point,
// or use different approach.
var paths = [].concat(paths);
paths.push('.');
for (var i = 0; i < paths.length; i++) {
pathname = checkPath(paths[i], file);
if (pathname != null) {
break;
}
}
paths = paths.slice(0, paths.length - 1);
if (!pathname) {
if (typeof(env.errback) === "function") {
env.errback(file, paths, callback);
} else {
callback({ type: 'File', message: "'" + file + "' wasn't found.\n" });
}
return;
}
function parseFile(e, data) {
if (e) return callback(e);
env.contents = env.contents || {};
env.contents[pathname] = data; // Updating top importing parser content cache.
new(less.Parser)({
paths: [path.dirname(pathname)].concat(paths),
filename: pathname,
contents: env.contents,
files: env.files,
syncImport: env.syncImport,
dumpLineNumbers: env.dumpLineNumbers
}).parse(data, function (e, root) {
callback(e, root, pathname);
});
};
try {
readFile(pathname);
parseFile(null, data);
} catch (e) {
parseFile(e);
}
}
return less;
});

4441
lib/require-less/lessc.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +0,0 @@
nav a:link {
color: #58b4df;
}
nav a:visited {
color: #58b4df;
}
nav a:hover {
color: #000000;
}
nav a:active {
color: #000000;
}
.menu-links {
float: left;
}
.menu-user {
margin-right: 20px;
float: right;
}
.menu-user a {
cursor: pointer;
}

26
stylesheets/mifosX.less Normal file
View File

@ -0,0 +1,26 @@
nav a {
&:link {
color: #58b4df;
}
&:visited {
color: #58b4df;
}
&:hover {
color: #000000;
}
&:active {
color: #000000;
}
}
.menu-links {
float: left;
}
.menu-user {
margin-right: 20px;
float: right;
}
.menu-user a {
cursor: pointer;
}

View File

@ -4,7 +4,7 @@ define(['mifosX', 'angular-mocks'], {
mifosX.ng.application.config(function($provide) {
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
}).run(function($httpBackend, $log) {
$log.warn("Loading test scenario: " + scenarioName);
$log.warn("Running test scenario: " + scenarioName);
$httpBackend.when("GET", /\.html$/).passThrough();
scenario.stubServer($httpBackend);
});