devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
407 lines (326 loc) • 10.6 kB
JavaScript
var $ = require("../core/renderer"),
domAdapter = require("../core/dom_adapter"),
windowUtils = require("../core/utils/window"),
window = windowUtils.getWindow(),
Deferred = require("../core/utils/deferred").Deferred,
errors = require("./widget/ui.errors"),
domUtils = require("../core/utils/dom"),
readyCallbacks = require("../core/utils/ready_callbacks"),
ready = readyCallbacks.add,
each = require("../core/utils/iterator").each,
devices = require("../core/devices"),
viewPortUtils = require("../core/utils/view_port"),
themeReadyCallback = require("./themes_callback"),
viewPort = viewPortUtils.value,
viewPortChanged = viewPortUtils.changeCallback;
var DX_LINK_SELECTOR = "link[rel=dx-theme]",
THEME_ATTR = "data-theme",
ACTIVE_ATTR = "data-active",
DX_HAIRLINES_CLASS = "dx-hairlines";
var context, $activeThemeLink, knownThemes, currentThemeName, pendingThemeName, isMaterialTheme;
var timerId;
var THEME_MARKER_PREFIX = "dx.";
function readThemeMarker() {
if (!windowUtils.hasWindow()) {
return null;
}
var element = $("<div>", context).addClass("dx-theme-marker").appendTo(context.documentElement),
result;
try {
result = element.css("fontFamily");
if (!result) {
return null;
}
result = result.replace(/["']/g, "");
if (result.substr(0, THEME_MARKER_PREFIX.length) !== THEME_MARKER_PREFIX) {
return null;
}
return result.substr(THEME_MARKER_PREFIX.length);
} finally {
element.remove();
}
}
// FYI
// http://stackoverflow.com/q/2635814
// http://stackoverflow.com/a/3078636
function waitForThemeLoad(themeName) {
var waitStartTime;
pendingThemeName = themeName;
function handleLoaded() {
pendingThemeName = null;
themeReadyCallback.fire();
themeReadyCallback.empty();
}
if (isPendingThemeLoaded()) {
handleLoaded();
} else {
waitStartTime = Date.now();
timerId = setInterval(function () {
var isLoaded = isPendingThemeLoaded(),
isTimeout = !isLoaded && Date.now() - waitStartTime > 15 * 1000;
if (isTimeout) {
errors.log("W0004", pendingThemeName);
}
if (isLoaded || isTimeout) {
clearInterval(timerId);
timerId = undefined;
handleLoaded();
}
}, 10);
}
}
function isPendingThemeLoaded() {
return !pendingThemeName || readThemeMarker() === pendingThemeName;
}
function processMarkup() {
var $allThemeLinks = $(DX_LINK_SELECTOR, context);
if (!$allThemeLinks.length) {
return;
}
knownThemes = {};
$activeThemeLink = $(domUtils.createMarkupFromString("<link rel=stylesheet>"), context);
$allThemeLinks.each(function () {
var link = $(this, context),
fullThemeName = link.attr(THEME_ATTR),
url = link.attr("href"),
isActive = link.attr(ACTIVE_ATTR) === "true";
knownThemes[fullThemeName] = {
url: url,
isActive: isActive
};
});
$allThemeLinks.last().after($activeThemeLink);
$allThemeLinks.remove();
}
function resolveFullThemeName(desiredThemeName) {
var desiredThemeParts = desiredThemeName.split("."),
result = null;
if (knownThemes) {
if (desiredThemeName in knownThemes) {
return desiredThemeName;
}
each(knownThemes, function (knownThemeName, themeData) {
var knownThemeParts = knownThemeName.split(".");
if (knownThemeParts[0] !== desiredThemeParts[0]) {
return;
}
if (desiredThemeParts[1] && desiredThemeParts[1] !== knownThemeParts[1]) {
return;
}
if (desiredThemeParts[2] && desiredThemeParts[2] !== knownThemeParts[2]) {
return;
}
if (!result || themeData.isActive) {
result = knownThemeName;
}
if (themeData.isActive) {
return false;
}
});
}
return result;
}
function initContext(newContext) {
try {
if (newContext !== context) {
knownThemes = null;
}
} catch (x) {
// Cross-origin permission error
knownThemes = null;
}
context = newContext;
}
function init(options) {
options = options || {};
initContext(options.context || domAdapter.getDocument());
if (!context) return;
processMarkup();
currentThemeName = undefined;
isMaterialTheme = undefined;
current(options);
}
function current(options) {
if (!arguments.length) {
currentThemeName = currentThemeName || readThemeMarker();
return currentThemeName;
}
detachCssClasses(viewPort());
options = options || {};
if (typeof options === "string") {
options = { theme: options };
}
var isAutoInit = options._autoInit,
loadCallback = options.loadCallback,
currentThemeData;
currentThemeName = options.theme || currentThemeName;
if (isAutoInit && !currentThemeName) {
currentThemeName = themeNameFromDevice(devices.current());
}
currentThemeName = resolveFullThemeName(currentThemeName);
if (currentThemeName) {
currentThemeData = knownThemes[currentThemeName];
}
if (loadCallback) {
themeReadyCallback.add(loadCallback);
}
if (currentThemeData) {
// NOTE:
// 1. <link> element re-creation leads to incorrect CSS rules priority in Internet Explorer (T246821).
// 2. We have no reliable info, why this hack has been applied and whether it is still relevant.
// 3. This hack leads Internet Explorer crashing after icon font has been implemented.
// $activeThemeLink.removeAttr("href"); // this is for IE, to stop loading prev CSS
$activeThemeLink.attr("href", knownThemes[currentThemeName].url);
if ((themeReadyCallback.has() || options._forceTimeout) && !timerId) {
waitForThemeLoad(currentThemeName);
} else {
if (pendingThemeName) {
pendingThemeName = currentThemeName;
}
}
} else {
if (isAutoInit) {
themeReadyCallback.fire();
themeReadyCallback.empty();
} else {
throw errors.Error("E0021", currentThemeName);
}
}
isMaterialTheme = /material/.test(currentThemeName || readThemeMarker());
checkThemeDeprecation();
attachCssClasses(viewPortUtils.originalViewPort(), currentThemeName);
}
function themeNameFromDevice(device) {
var themeName = device.platform;
var majorVersion = device.version && device.version[0];
switch (themeName) {
case "ios":
themeName += "7";
break;
case "android":
themeName += "5";
break;
case "win":
themeName += majorVersion && majorVersion === 8 ? "8" : "10";
break;
}
return themeName;
}
function getCssClasses(themeName) {
themeName = themeName || current();
var result = [],
themeNameParts = themeName && themeName.split(".");
if (themeNameParts) {
result.push("dx-theme-" + themeNameParts[0], "dx-theme-" + themeNameParts[0] + "-typography");
if (themeNameParts.length > 1) {
result.push("dx-color-scheme-" + themeNameParts[1] + (isMaterial() ? "-" + themeNameParts[2] : ""));
}
}
return result;
}
var themeClasses;
function attachCssClasses(element, themeName) {
themeClasses = getCssClasses(themeName).join(" ");
$(element).addClass(themeClasses);
var activateHairlines = function activateHairlines() {
var pixelRatio = windowUtils.hasWindow() && window.devicePixelRatio;
if (!pixelRatio || pixelRatio < 2) {
return;
}
var $tester = $("<div>");
$tester.css("border", ".5px solid transparent");
$("body").append($tester);
if ($tester.outerHeight() === 1) {
$(element).addClass(DX_HAIRLINES_CLASS);
themeClasses += " " + DX_HAIRLINES_CLASS;
}
$tester.remove();
};
activateHairlines();
}
function detachCssClasses(element) {
$(element).removeClass(themeClasses);
}
function themeReady(callback) {
themeReadyCallback.add(callback);
}
function isMaterial() {
return isMaterialTheme;
}
function checkThemeDeprecation() {
var name = currentThemeName || readThemeMarker();
if (/win8/.test(name)) {
errors.log("W0010", "The 'win8' theme", "16.1", "Use the 'generic' theme instead.");
}
if (/win10/.test(name)) {
errors.log("W0010", "The 'win10' theme", "17.2", "Use the 'generic' theme instead.");
}
if (/android/.test(name)) {
errors.log("W0010", "The 'android5' theme", "18.1", "Use the 'material' theme instead.");
}
}
var initDeferred = new Deferred();
function autoInit() {
init({
_autoInit: true,
_forceTimeout: true
});
if ($(DX_LINK_SELECTOR, context).length) {
throw errors.Error("E0022");
}
initDeferred.resolve();
}
if (windowUtils.hasWindow()) {
autoInit();
} else {
ready(autoInit);
}
viewPortChanged.add(function (viewPort, prevViewPort) {
initDeferred.done(function () {
detachCssClasses(prevViewPort);
attachCssClasses(viewPort);
});
});
devices.changed.add(function () {
init({ _autoInit: true });
});
/**
* @name ui.themes
* @publicName themes
* @namespace DevExpress.ui
* @module ui/themes
* @export default
*/
/**
* @name ui.themesmethods.current
* @publicName current()
* @static
* @return string
*/
/**
* @name ui.themesmethods.current
* @publicName current(themeName)
* @param1 themeName:string
* @static
*/
exports.current = current;
/**
* @name ui.themesmethods.ready
* @publicName ready(callback)
* @param1 callback:function
* @static
*/
exports.ready = themeReady;
exports.init = init;
exports.attachCssClasses = attachCssClasses;
exports.detachCssClasses = detachCssClasses;
exports.themeNameFromDevice = themeNameFromDevice;
exports.waitForThemeLoad = waitForThemeLoad;
exports.isMaterial = isMaterial;
exports.resetTheme = function () {
$activeThemeLink && $activeThemeLink.attr("href", "about:blank");
currentThemeName = null;
pendingThemeName = null;
isMaterialTheme = false;
};
;