function extractScrollbars(content) {
var scrollbar = [];
// Go through and extract out scrollbar customizations so that we can
// enable/disable via settings.
content = content
.replace(scrollbarsRegex, function (match) {
scrollbar.push(match);
return "";
});
return {
content: content,
scrollbar: scrollbar
};
}
function fixPath(path) {
return path.replace(/^([A-Z]+:)?\//, function (match) {
return match.toLocaleLowerCase();
});
}
Gets all available themes
function getAllThemes() {
return _.map(loadedThemes, function (theme) {
return theme;
});
}
Get current theme object that is loaded in the editor.
function getCurrentTheme() {
if (!currentTheme) {
currentTheme = loadedThemes[prefs.get("theme")] || loadedThemes[defaultTheme];
}
return currentTheme;
}
function getThemeByFile(file) {
var path = file._path;
return _.find(loadedThemes, function (item) {
return item.file._path === path;
});
}
function lessifyTheme(content, theme) {
var deferred = new $.Deferred();
less.render("#editor-holder {" + content + "\n}", {
rootpath: fixPath(stylesPath),
filename: fixPath(theme.file._path)
}, function (err, tree) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(tree.css);
}
});
return deferred.promise();
}
function loadCurrentTheme() {
var theme = getCurrentTheme();
var pending = theme && FileUtils.readAsText(theme.file)
.then(function (lessContent) {
return lessifyTheme(lessContent.replace(commentRegex, ""), theme);
})
.then(function (content) {
var result = extractScrollbars(content);
theme.scrollbar = result.scrollbar;
return result.content;
})
.then(function (cssContent) {
$("body").toggleClass("dark", theme.dark);
styleNode.text(cssContent);
return theme;
});
return $.when(pending);
}
Loads a theme from a file.
function loadFile(fileName, options) {
var deferred = new $.Deferred(),
file = FileSystem.getFileForPath(fileName),
currentThemeName = prefs.get("theme");
file.exists(function (err, exists) {
var theme;
if (exists) {
theme = new Theme(file, options);
loadedThemes[theme.name] = theme;
ThemeSettings._setThemes(loadedThemes);
// For themes that are loaded after ThemeManager has been loaded,
// we should check if it's the current theme. If it is, then we just
// load it.
if (currentThemeName === theme.name) {
refresh(true);
}
deferred.resolve(theme);
} else if (err || !exists) {
deferred.reject(err);
}
});
return deferred.promise();
}
Loads a theme from an extension package.
function loadPackage(themePackage) {
var fileName = themePackage.path + "/" + themePackage.metadata.theme.file;
return loadFile(fileName, themePackage.metadata);
}
prefs.on("change", "theme", function () {
// Make sure we don't reprocess a theme that's already loaded
if (currentTheme && currentTheme.name === prefs.get("theme")) {
return;
}
// Refresh editor with the new theme
refresh(true);
// Process the scrollbars for the editor
ThemeView.updateScrollbars(getCurrentTheme());
// Expose event for theme changes
exports.trigger("themeChange", getCurrentTheme());
});
prefs.on("change", "themeScrollbars", function () {
refresh();
ThemeView.updateScrollbars(getCurrentTheme());
});
// Monitor file changes. If the file that has changed is actually the currently loaded
// theme, then we just reload the theme. This allows to live edit the theme
FileSystem.on("change", function (evt, file) {
if (!file || file.isDirectory) {
return;
}
if (getThemeByFile(file)) {
refresh(true);
}
});
EditorManager.on("activeEditorChange", function () {
refresh();
});
EventDispatcher.makeEventDispatcher(exports);
exports.refresh = refresh;
exports.loadFile = loadFile;
exports.loadPackage = loadPackage;
exports.getCurrentTheme = getCurrentTheme;
exports.getAllThemes = getAllThemes;
// Exposed for testing purposes
exports._toDisplayName = toDisplayName;
exports._extractScrollbars = extractScrollbars;
});
Refresh current theme in the editor
function refresh(force) {
if (force) {
currentTheme = null;
}
$.when(force && loadCurrentTheme()).done(function () {
var editor = EditorManager.getActiveEditor();
if (!editor || !editor._codeMirror) {
return;
}
var cm = editor._codeMirror;
ThemeView.updateThemes(cm);
// currentTheme can be undefined, so watch out
cm.setOption("addModeClass", !!(currentTheme && currentTheme.addModeClass));
});
}
function toDisplayName(name) {
var extIndex = name.lastIndexOf('.');
name = name.substring(0, extIndex !== -1 ? extIndex : undefined).replace(/-/g, ' ');
return name.split(" ").map(function (part) {
return part[0].toUpperCase() + part.substring(1);
}).join(" ");
}
function Theme(file, options) {
options = options || {};
var fileName = file.name;
// If no options.name is provided, then we derive the name of the theme from whichever we find
// first, the options.title or the filename.
if (!options.name) {
if (options.title) {
options.name = options.title;
} else {
// Remove the file extension when the filename is used as the theme name. This is to
// follow CodeMirror conventions where themes are just a CSS file and the filename
// (without the extension) is used to build CSS rules. Also handle removing .min
// in case the ".min" is part of the file name.
options.name = FileUtils.getFilenameWithoutExtension(fileName).replace(/\.min$/, "");
}
// We do a bit of string treatment here to make sure we generate theme names that can be
// used as a CSS class name by CodeMirror.
options.name = options.name.toLocaleLowerCase().replace(/[\W]/g, '-');
}
this.file = file;
this.name = options.name;
this.displayName = options.title || toDisplayName(fileName);
this.dark = options.theme !== undefined && options.theme.dark === true;
this.addModeClass = options.theme !== undefined && options.theme.addModeClass === true;
}