carto/lib/less/browser.js

298 lines
9.3 KiB
JavaScript
Raw Normal View History

//
// browser.js - client-side engine
//
2010-05-19 09:16:44 +08:00
var isFileProtocol = location.protocol === 'file:';
less.env = location.hostname == '127.0.0.1' ||
location.hostname == '0.0.0.0' ||
location.hostname == 'localhost' ||
location.port.length > 0 ||
isFileProtocol ? 'development'
: 'production';
// Load styles asynchronously (default: false)
//
// This is set to `false` by default, so that the body
// doesn't start loading before the stylesheets are parsed.
// Setting this to `true` can result in flickering.
//
less.async = false;
2010-06-15 12:56:37 +08:00
// Interval between watch polls
less.poll = isFileProtocol ? 1000 : 1500;
2010-06-15 12:56:37 +08:00
//
// Watch mode
//
less.watch = function () { return this.watchMode = true };
less.unwatch = function () { return this.watchMode = false };
if (less.env === 'development') {
less.optimization = 0;
if (/!watch/.test(location.hash)) {
less.watch();
}
less.watchTimer = setInterval(function () {
if (less.watchMode) {
2010-06-15 12:56:37 +08:00
loadStyleSheets(function (root, sheet, env) {
if (root) {
2010-06-15 12:56:37 +08:00
createCSS(root.toCSS(), sheet, env.lastModified);
}
});
}
2010-06-15 12:56:37 +08:00
}, less.poll);
} else {
less.optimization = 3;
}
2010-06-15 15:52:25 +08:00
var cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
2010-06-15 09:20:04 +08:00
//
// Select all links with the 'rel' attribute set to "less"
//
var sheets = select('link[rel="stylesheet/less"]');
less.refresh = function (reload) {
2010-06-15 14:11:35 +08:00
loadStyleSheets(function (root, sheet, env) {
if (env.local) {
log("less: loading " + sheet.href + " from local storage.");
} else {
createCSS(root.toCSS(), sheet, env.lastModified);
log("less: parsed " + sheet.href + " successfully.");
}
}, reload);
2010-06-15 14:11:35 +08:00
};
less.refresh();
function select(str) {
if (!document.querySelectorAll && typeof(jQuery) === "undefined") {
log("No selector method found");
} else {
return (document.querySelectorAll || jQuery).call(document, str);
}
}
function loadStyleSheets(callback, reload) {
for (var i = 0; i < sheets.length; i++) {
loadStyleSheet(sheets[i], callback, reload);
2010-06-08 04:12:25 +08:00
}
}
function loadStyleSheet(sheet, callback, reload) {
2010-06-15 15:52:25 +08:00
var css = cache && cache.getItem(sheet.href);
var timestamp = cache && cache.getItem(sheet.href + ':timestamp');
var styles = { css: css, timestamp: timestamp };
xhr(sheet.href, function (data, lastModified) {
if (!reload && styles &&
(new(Date)(lastModified).valueOf() ===
new(Date)(styles.timestamp).valueOf())) {
2010-06-08 04:12:25 +08:00
// Use local copy
createCSS(styles.css, sheet);
callback(null, sheet, { local: true });
2010-06-08 04:12:25 +08:00
} else {
// Use remote copy (re-parse)
new(less.Parser)({
optimization: less.optimization
}).parse(data, function (e, root) {
2010-06-08 04:12:25 +08:00
if (e) { return error(e, sheet.href) }
try {
callback(root, sheet, { local: false, lastModified: lastModified });
removeNode(document.getElementById('less-error-message:' + sheet.href.replace(/[^a-z]+/gi, '-')));
2010-06-08 04:12:25 +08:00
} catch (e) {
error(e, sheet.href);
}
});
2010-06-08 04:12:25 +08:00
}
}, function (status) {
throw new(Error)("Couldn't load " + sheet.href + " (" + status + ")");
2010-06-08 04:12:25 +08:00
});
}
function createCSS(styles, sheet, lastModified) {
var css;
// If there is no title set, use the filename, minus the extension
var id = 'less:' + (sheet.title || sheet.href.match(/(?:^|\/)([-\w]+)\.[a-z]+$/i)[1]);
// If the stylesheet doesn't exist, create a new node
if ((css = document.getElementById(id)) === null) {
css = document.createElement('style');
css.type = 'text/css';
css.media = 'screen';
css.id = id;
document.getElementsByTagName('head')[0].appendChild(css);
}
if (css.styleSheet) { // IE
try {
css.styleSheet.cssText = styles;
} catch (e) {
throw new(Error)("Couldn't reassign styleSheet.cssText.");
}
} else {
2010-06-15 12:56:37 +08:00
(function (node) {
if (css.childNodes.length > 0) {
if (css.firstChild.nodeValue !== node.nodeValue) {
css.replaceChild(node, css.firstChild);
}
2010-06-15 12:56:37 +08:00
} else {
css.appendChild(node);
}
})(document.createTextNode(styles));
}
// Don't update the local store if the file wasn't modified
2010-06-15 15:52:25 +08:00
if (lastModified && cache) {
log('saving ' + sheet.href + ' to cache.');
2010-06-15 15:52:25 +08:00
cache.setItem(sheet.href, styles);
cache.setItem(sheet.href + ':timestamp', lastModified);
}
}
function xhr(url, callback, errback) {
2010-05-23 12:11:14 +08:00
var xhr = getXMLHttpRequest();
var async = isFileProtocol ? false : less.async;
2010-06-16 02:52:17 +08:00
xhr.open('GET', url, async);
xhr.send(null);
if (isFileProtocol) {
if (xhr.status === 0) {
callback(xhr.responseText);
} else {
errback(xhr.status);
}
2010-06-16 02:52:17 +08:00
} else if (async) {
xhr.onreadystatechange = function () {
2010-05-23 12:11:14 +08:00
if (xhr.readyState == 4) {
2010-06-16 02:52:17 +08:00
handleResponse(xhr, callback, errback);
}
};
2010-06-16 02:52:17 +08:00
} else {
handleResponse(xhr, callback, errback);
}
function handleResponse(xhr, callback, errback) {
if (xhr.status >= 200 && xhr.status < 300) {
callback(xhr.responseText,
xhr.getResponseHeader("Last-Modified"));
} else if (typeof(errback) === 'function') {
errback(xhr.status);
}
}
}
2010-05-23 12:11:14 +08:00
function getXMLHttpRequest() {
if (window.XMLHttpRequest) {
return new(XMLHttpRequest);
} else {
try {
return new(ActiveXObject)("MSXML2.XMLHTTP.3.0");
2010-05-24 00:52:23 +08:00
} catch (e) {
log("less: browser doesn't support AJAX.");
2010-05-23 12:11:14 +08:00
return null;
}
}
}
2010-06-15 12:56:37 +08:00
function removeNode(node) {
return node && node.parentNode.removeChild(node);
}
function log(str) {
2010-05-19 08:28:24 +08:00
if (less.env == 'development' && typeof(console) !== "undefined") { console.log(str) }
}
function error(e, href) {
var id = 'less-error-message:' + href.replace(/[^a-z]+/ig, '-');
if (! e.extract) { throw e }
var template = ['<div>',
'<pre class="ctx"><span>[-1]</span>{0}</pre>',
'<pre><span>[0]</span>{current}</pre>',
'<pre class="ctx"><span>[1]</span>{2}</pre>',
'</div>'].join('\n');
var elem = document.createElement('div'), timer;
elem.id = id;
elem.className = "less-error-message";
elem.innerHTML = '<h3>' + (e.message || 'There is an error in your .less file') + '</h3>' +
'<p><a href="' + href + '">' + href + "</a> " +
'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
template.replace(/\[(-?\d)\]/g, function (_, i) {
return (parseInt(e.line) + parseInt(i)) || '';
}).replace(/\{(\d)\}/g, function (_, i) {
return e.extract[parseInt(i)] || '';
}).replace(/\{current\}/, e.extract[1].slice(0, e.column) +
'<span class="error">' +
e.extract[1].slice(e.column) +
'</span>');
// CSS for error messages
createCSS([
'.less-error-message span {',
'margin-right: 15px;',
'}',
'.less-error-message pre {',
'color: #ee4444;',
'padding: 4px 0;',
'margin: 0;',
'}',
'.less-error-message pre.ctx {',
'color: #dd7777;',
'}',
'.less-error-message h3 {',
'padding: 15px 0 5px 0;',
'margin: 0;',
'}',
'.less-error-message a {',
'color: #10a',
'}',
'.less-error-message .error {',
'color: red;',
'font-weight: bold;',
'padding-bottom: 2px;',
'border-bottom: 1px dashed red;',
'}'
2010-06-15 12:56:37 +08:00
].join('\n'), { title: 'error-message' });
elem.style.cssText = [
"font-family: Arial, sans-serif",
"border: 1px solid #e00",
"background-color: #eee",
"border-radius: 5px",
2010-06-15 12:56:37 +08:00
"-webkit-border-radius: 5px",
"-moz-border-radius: 5px",
"color: #e00",
"padding: 15px",
"margin-bottom: 15px"
].join(';');
if (less.env == 'development') {
timer = setInterval(function () {
if (document.body) {
if (document.getElementById(id)) {
document.body.replaceChild(elem, document.getElementById(id));
} else {
document.body.insertBefore(elem, document.body.firstChild);
}
clearInterval(timer);
}
}, 10);
}
}
//
// Used by `@import` directives
//
less.Parser.importer = function (path, paths, callback) {
loadStyleSheet({ href: path, title: path }, function (root) {
callback(root);
});
};