mirror of
https://github.com/less/less.js.git
synced 2026-01-24 22:58:04 -05:00
207 lines
7.1 KiB
JavaScript
207 lines
7.1 KiB
JavaScript
/*global window, XMLHttpRequest */
|
|
|
|
module.exports = function(less, isFileProtocol, log, logLevel) {
|
|
|
|
var fileCache = {};
|
|
|
|
//TODOS - move log somewhere. pathDiff and doing something similiar in node. use pathDiff in the other browser file for the initial load
|
|
// isFileProtocol is global
|
|
|
|
function getXMLHttpRequest() {
|
|
if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
|
|
return new XMLHttpRequest();
|
|
} else {
|
|
try {
|
|
/*global ActiveXObject */
|
|
return new ActiveXObject("Microsoft.XMLHTTP");
|
|
} catch (e) {
|
|
log("browser doesn't support AJAX.", logLevel.errors);
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
// make generic but overriddable
|
|
warn: function warn(env, msg) {
|
|
console.warn(msg);
|
|
},
|
|
// make generic but overriddable
|
|
getPath: function getPath(env, filename) {
|
|
var j = filename.lastIndexOf('/');
|
|
if (j < 0) {
|
|
j = filename.lastIndexOf('\\');
|
|
}
|
|
if (j < 0) {
|
|
return "";
|
|
}
|
|
return filename.slice(0, j + 1);
|
|
},
|
|
// make generic but overriddable
|
|
isPathAbsolute: function isPathAbsolute(env, filename) {
|
|
return /^(?:[a-z-]+:|\/|\\)/i.test(filename);
|
|
},
|
|
alwaysMakePathsAbsolute: function alwaysMakePathsAbsolute() {
|
|
return true;
|
|
},
|
|
getCleanCSS: function () {
|
|
},
|
|
supportsDataURI: function() {
|
|
return false;
|
|
},
|
|
pathDiff: function pathDiff(url, baseUrl) {
|
|
// diff between two paths to create a relative path
|
|
|
|
var urlParts = this.extractUrlParts(url),
|
|
baseUrlParts = this.extractUrlParts(baseUrl),
|
|
i, max, urlDirectories, baseUrlDirectories, diff = "";
|
|
if (urlParts.hostPart !== baseUrlParts.hostPart) {
|
|
return "";
|
|
}
|
|
max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
|
|
for(i = 0; i < max; i++) {
|
|
if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
|
|
}
|
|
baseUrlDirectories = baseUrlParts.directories.slice(i);
|
|
urlDirectories = urlParts.directories.slice(i);
|
|
for(i = 0; i < baseUrlDirectories.length-1; i++) {
|
|
diff += "../";
|
|
}
|
|
for(i = 0; i < urlDirectories.length-1; i++) {
|
|
diff += urlDirectories[i] + "/";
|
|
}
|
|
return diff;
|
|
},
|
|
join: function join(basePath, laterPath) {
|
|
if (!basePath) {
|
|
return laterPath;
|
|
}
|
|
return this.extractUrlParts(laterPath, basePath).path;
|
|
},
|
|
// helper function, not part of API
|
|
extractUrlParts: function extractUrlParts(url, baseUrl) {
|
|
// urlParts[1] = protocol&hostname || /
|
|
// urlParts[2] = / if path relative to host base
|
|
// urlParts[3] = directories
|
|
// urlParts[4] = filename
|
|
// urlParts[5] = parameters
|
|
|
|
var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
|
|
urlParts = url.match(urlPartsRegex),
|
|
returner = {}, directories = [], i, baseUrlParts;
|
|
|
|
if (!urlParts) {
|
|
throw new Error("Could not parse sheet href - '"+url+"'");
|
|
}
|
|
|
|
// Stylesheets in IE don't always return the full path
|
|
if (!urlParts[1] || urlParts[2]) {
|
|
baseUrlParts = baseUrl.match(urlPartsRegex);
|
|
if (!baseUrlParts) {
|
|
throw new Error("Could not parse page url - '"+baseUrl+"'");
|
|
}
|
|
urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
|
|
if (!urlParts[2]) {
|
|
urlParts[3] = baseUrlParts[3] + urlParts[3];
|
|
}
|
|
}
|
|
|
|
if (urlParts[3]) {
|
|
directories = urlParts[3].replace(/\\/g, "/").split("/");
|
|
|
|
// extract out . before .. so .. doesn't absorb a non-directory
|
|
for(i = 0; i < directories.length; i++) {
|
|
if (directories[i] === ".") {
|
|
directories.splice(i, 1);
|
|
i -= 1;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < directories.length; i++) {
|
|
if (directories[i] === ".." && i > 0) {
|
|
directories.splice(i-1, 2);
|
|
i -= 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
returner.hostPart = urlParts[1];
|
|
returner.directories = directories;
|
|
returner.path = urlParts[1] + directories.join("/");
|
|
returner.fileUrl = returner.path + (urlParts[4] || "");
|
|
returner.url = returner.fileUrl + (urlParts[5] || "");
|
|
return returner;
|
|
},
|
|
doXHR: function doXHR(url, type, callback, errback) {
|
|
|
|
var xhr = getXMLHttpRequest();
|
|
var async = isFileProtocol ? less.fileAsync : less.async;
|
|
|
|
if (typeof(xhr.overrideMimeType) === 'function') {
|
|
xhr.overrideMimeType('text/css');
|
|
}
|
|
log("XHR: Getting '" + url + "'", logLevel.debug);
|
|
xhr.open('GET', url, async);
|
|
xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
|
|
xhr.send(null);
|
|
|
|
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, url);
|
|
}
|
|
}
|
|
|
|
if (isFileProtocol && !less.fileAsync) {
|
|
if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
|
|
callback(xhr.responseText);
|
|
} else {
|
|
errback(xhr.status, url);
|
|
}
|
|
} else if (async) {
|
|
xhr.onreadystatechange = function () {
|
|
if (xhr.readyState == 4) {
|
|
handleResponse(xhr, callback, errback);
|
|
}
|
|
};
|
|
} else {
|
|
handleResponse(xhr, callback, errback);
|
|
}
|
|
},
|
|
loadFile: function loadFile(env, filename, currentDirectory, callback) {
|
|
if (currentDirectory && !this.isPathAbsolute(env, filename)) {
|
|
filename = currentDirectory + filename;
|
|
}
|
|
|
|
// sheet may be set to the stylesheet for the initial load or a collection of properties including
|
|
// some env variables for imports
|
|
var hrefParts = this.extractUrlParts(filename, window.location.href);
|
|
var href = hrefParts.url;
|
|
|
|
if (env.useFileCache && fileCache[href]) {
|
|
try {
|
|
var lessText = fileCache[href];
|
|
callback(null, lessText, href, { lastModified: new Date() });
|
|
} catch (e) {
|
|
callback(e, null, href);
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.doXHR(href, env.mime, function doXHRCallback(data, lastModified) {
|
|
// per file cache
|
|
fileCache[href] = data;
|
|
|
|
// Use remote copy (re-parse)
|
|
callback(null, data, href, { lastModified: lastModified });
|
|
}, function doXHRError(status, url) {
|
|
callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href);
|
|
});
|
|
|
|
}
|
|
};
|
|
|
|
};
|