// // browser.js - client-side engine // var isFileProtocol = (location.protocol === 'file:' || location.protocol === 'chrome:' || location.protocol === 'chrome-extension:' || location.protocol === 'resource:'); less.env = 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; // Interval between watch polls less.poll = less.poll || (isFileProtocol ? 1000 : 1500); // // 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) { loadStyleSheets(function (root, sheet, env) { if (root) { createCSS(root.toCSS(), sheet, env.lastModified); } }); } }, less.poll); } else { less.optimization = 3; } var cache; try { cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage; } catch (_) { cache = null; } // // Get all tags with the 'rel' attribute set to "stylesheet/less" // var links = document.getElementsByTagName('link'); var typePattern = /^text\/(x-)?less$/; less.sheets = []; for (var i = 0; i < links.length; i++) { if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) && (links[i].type.match(typePattern)))) { less.sheets.push(links[i]); } } less.refresh = function (reload) { var startTime, endTime; startTime = endTime = new(Date); loadStyleSheets(function (root, sheet, env) { if (env.local) { log("loading " + sheet.href + " from cache."); } else { log("parsed " + sheet.href + " successfully."); createCSS(root.toCSS(), sheet, env.lastModified); } log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms'); (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms'); endTime = new(Date); }, reload); loadStyles(); }; less.refreshStyles = loadStyles; less.refresh(less.env === 'development'); function loadStyles() { var styles = document.getElementsByTagName('style'); for (var i = 0; i < styles.length; i++) { if (styles[i].type.match(typePattern)) { new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) { var css = tree.toCSS(); var style = styles[i]; try { style.innerHTML = css; } catch (_) { style.styleSheets.cssText = css; } style.type = 'text/css'; }); } } } function loadStyleSheets(callback, reload) { for (var i = 0; i < less.sheets.length; i++) { loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1)); } } function loadStyleSheet(sheet, callback, reload, remaining) { var url = window.location.href.replace(/[#?].*$/, ''); var href = sheet.href.replace(/\?.*$/, ''); var css = cache && cache.getItem(href); var timestamp = cache && cache.getItem(href + ':timestamp'); var styles = { css: css, timestamp: timestamp }; // Stylesheets in IE don't always return the full path if (! /^(https?|file):/.test(href)) { if (href.charAt(0) == "/") { href = window.location.protocol + "//" + window.location.host + href; } else { href = url.slice(0, url.lastIndexOf('/') + 1) + href; } } xhr(sheet.href, sheet.type, function (data, lastModified) { if (!reload && styles && lastModified && (new(Date)(lastModified).valueOf() === new(Date)(styles.timestamp).valueOf())) { // Use local copy createCSS(styles.css, sheet); callback(null, sheet, { local: true, remaining: remaining }); } else { // Use remote copy (re-parse) try { new(less.Parser)({ optimization: less.optimization, paths: [href.replace(/[\w\.-]+$/, '')], mime: sheet.type }).parse(data, function (e, root) { if (e) { return error(e, href) } try { callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining }); removeNode(document.getElementById('less-error-message:' + extractId(href))); } catch (e) { error(e, href); } }); } catch (e) { error(e, href); } } }, function (status, url) { throw new(Error)("Couldn't load " + url + " (" + status + ")"); }); } function extractId(href) { return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain .replace(/^\//, '' ) // Remove root / .replace(/\?.*$/, '' ) // Remove query .replace(/\.[^\.\/]+$/, '' ) // Remove file extension .replace(/[^\.\w-]+/g, '-') // Replace illegal characters .replace(/\./g, ':'); // Replace dots with colons(for valid id) } function createCSS(styles, sheet, lastModified) { var css; // Strip the query-string var href = sheet.href ? sheet.href.replace(/\?.*$/, '') : ''; // If there is no title set, use the filename, minus the extension var id = 'less:' + (sheet.title || extractId(href)); // 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 = sheet.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 { (function (node) { if (css.childNodes.length > 0) { if (css.firstChild.nodeValue !== node.nodeValue) { css.replaceChild(node, css.firstChild); } } else { css.appendChild(node); } })(document.createTextNode(styles)); } // Don't update the local store if the file wasn't modified if (lastModified && cache) { log('saving ' + href + ' to cache.'); cache.setItem(href, styles); cache.setItem(href + ':timestamp', lastModified); } } function xhr(url, type, callback, errback) { var xhr = getXMLHttpRequest(); var async = isFileProtocol ? false : less.async; if (typeof(xhr.overrideMimeType) === 'function') { xhr.overrideMimeType('text/css'); } xhr.open('GET', url, async); xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5'); xhr.send(null); if (isFileProtocol) { if (xhr.status === 0) { 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); } 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); } } } function getXMLHttpRequest() { if (window.XMLHttpRequest) { return new(XMLHttpRequest); } else { try { return new(ActiveXObject)("MSXML2.XMLHTTP.3.0"); } catch (e) { log("browser doesn't support AJAX."); return null; } } } function removeNode(node) { return node && node.parentNode.removeChild(node); } function log(str) { if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) } } function error(e, href) { var id = 'less-error-message:' + extractId(href); var template = [''].join('\n'); var elem = document.createElement('div'), timer, content; elem.id = id; elem.className = "less-error-message"; content = '

' + (e.message || 'There is an error in your .less file') + '

' + '

' + href + " "; if (e.extract) { content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':

' + 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) + '' + e.extract[1].slice(e.column) + ''); } elem.innerHTML = content; // CSS for error messages createCSS([ '.less-error-message ul, .less-error-message li {', 'list-style-type: none;', 'margin-right: 15px;', 'padding: 4px 0;', 'margin: 0;', '}', '.less-error-message label {', 'font-size: 12px;', 'margin-right: 15px;', 'padding: 4px 0;', 'color: #cc7777;', '}', '.less-error-message pre {', 'color: #ee4444;', 'padding: 4px 0;', 'margin: 0;', 'display: inline-block;', '}', '.less-error-message pre.ctx {', 'color: #dd4444;', '}', '.less-error-message h3 {', 'font-size: 20px;', 'font-weight: bold;', '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;', '}' ].join('\n'), { title: 'error-message' }); elem.style.cssText = [ "font-family: Arial, sans-serif", "border: 1px solid #e00", "background-color: #eee", "border-radius: 5px", "-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); } }