devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
383 lines (382 loc) • 16.5 kB
JavaScript
/**
* DevExtreme (framework/html/html_application.js)
* Version: 18.1.3
* Build date: Tue May 15 2018
*
* Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
;
require("../../integration/jquery");
var $ = require("jquery"),
commonUtils = require("../../core/utils/common"),
window = require("../../core/utils/window").getWindow(),
domAdapter = require("../../core/dom_adapter"),
Component = require("../../core/component"),
extendUtils = require("../../core/utils/extend"),
each = require("../../core/utils/iterator").each,
errors = require("../errors"),
Application = require("../application").Application,
ConditionalViewCacheDecorator = require("../view_cache").ConditionalViewCacheDecorator,
html = require("./presets"),
CommandManager = require("./command_manager"),
ViewEngine = require("./view_engine").ViewEngine,
messageLocalization = require("../../localization/message"),
viewPort = require("../../core/utils/view_port").value,
initMobileViewportModule = require("../../mobile/init_mobile_viewport/init_mobile_viewport"),
devices = require("../../core/devices"),
feedbackEvents = require("../../events/core/emitter.feedback"),
TransitionExecutorModule = require("../../animation/transition_executor/transition_executor"),
animationPresetsModule = require("../../animation/presets/presets"),
when = require("../../core/utils/deferred").when;
require("./layout_controller");
require("../../ui/themes");
var VIEW_PORT_CLASSNAME = "dx-viewport",
LAYOUT_CHANGE_ANIMATION_NAME = "layout-change";
var HtmlApplication = Application.inherit({
ctor: function(options) {
options = options || {};
this.callBase(options);
this._$root = $(options.rootNode || domAdapter.getBody());
this._initViewport(options.viewPort);
if ("mobileApp" === this._applicationMode) {
initMobileViewportModule.initMobileViewport(options.viewPort)
}
this.device = options.device || devices.current();
this.commandManager = options.commandManager || new CommandManager({
commandMapping: this.commandMapping
});
this._initTemplateContext();
this.viewEngine = options.viewEngine || new ViewEngine({
$root: this._$root,
device: this.device,
templateCacheStorage: options.templateCacheStorage || window.localStorage,
templatesVersion: options.templatesVersion,
templateContext: this._templateContext
});
this.components.push(this.viewEngine);
this._initMarkupFilters(this.viewEngine);
this._layoutSet = options.layoutSet || html.layoutSets.default;
this._animationSet = options.animationSet || html.animationSets.default;
this._availableLayoutControllers = [];
this._activeLayoutControllersStack = [];
this.transitionExecutor = new TransitionExecutorModule.TransitionExecutor;
this._initAnimations(this._animationSet)
},
_initAnimations: function(animationSet) {
if (!animationSet) {
return
}
each(animationSet, function(name, configs) {
each(configs, function(index, config) {
animationPresetsModule.presets.registerPreset(name, config)
})
});
animationPresetsModule.presets.applyChanges()
},
_localizeMarkup: function($markup) {
messageLocalization.localizeNode($markup)
},
_notifyIfBadMarkup: function($markup) {
$markup.each(function() {
var html = $(this).html();
if (/href="#/.test(html)) {
errors.log("W3005", html)
}
})
},
_initMarkupFilters: function(viewEngine) {
var filters = [];
filters.push(this._localizeMarkup);
if (viewEngine.markupLoaded) {
viewEngine.markupLoaded.add(function(args) {
each(filters, function(_, filter) {
filter(args.markup)
})
})
}
},
_createViewCache: function(options) {
var result = this.callBase(options);
if (!options.viewCache) {
result = new ConditionalViewCacheDecorator({
filter: function(key, viewInfo) {
return !viewInfo.viewTemplateInfo.disableCache
},
viewCache: result
})
}
return result
},
_initViewport: function() {
this._$viewPort = this._getViewPort();
viewPort(this._$viewPort)
},
_getViewPort: function() {
var $viewPort = $("." + VIEW_PORT_CLASSNAME);
if (!$viewPort.length) {
$viewPort = $("<div>").addClass(VIEW_PORT_CLASSNAME).appendTo(this._$root)
}
return $viewPort
},
_initTemplateContext: function() {
this._templateContext = new Component({
orientation: devices.orientation()
});
devices.on("orientationChanged", function(args) {
this._templateContext.option("orientation", args.orientation)
}.bind(this))
},
_showViewImpl: function(viewInfo, direction) {
var that = this,
deferred = $.Deferred(),
result = deferred.promise(),
layoutController = viewInfo.layoutController;
that._obtainViewLink(viewInfo);
layoutController.showView(viewInfo, direction).done(function() {
that._activateLayoutController(layoutController, that._getTargetNode(viewInfo), direction).done(function() {
deferred.resolve()
})
});
feedbackEvents.lock(result);
return result
},
_resolveLayoutController: function(viewInfo) {
var args = {
viewInfo: viewInfo,
layoutController: null,
availableLayoutControllers: this._availableLayoutControllers
};
this._processEvent("resolveLayoutController", args, viewInfo.model);
return args.layoutController || this._resolveLayoutControllerImpl(viewInfo)
},
_checkLayoutControllerIsInitialized: function(layoutController) {
if (layoutController) {
var isControllerInited = false;
each(this._layoutSet, function(_, controllerInfo) {
if (controllerInfo.controller === layoutController) {
isControllerInited = true;
return false
}
});
if (!isControllerInited) {
throw errors.Error("E3024")
}
}
},
_ensureOneLayoutControllerFound: function(target, matches) {
var toJSONInterceptor = function(key, value) {
if ("controller" === key) {
return "[controller]: { name:" + value.name + " }"
}
return value
};
if (!matches.length) {
errors.log("W3003", JSON.stringify(target, null, 4), JSON.stringify(this._availableLayoutControllers, toJSONInterceptor, 4));
throw errors.Error("E3011")
}
if (matches.length > 1) {
errors.log("W3004", JSON.stringify(target, null, 4), JSON.stringify(matches, toJSONInterceptor, 4));
throw errors.Error("E3012")
}
},
_resolveLayoutControllerImpl: function(viewInfo) {
var templateInfo = viewInfo.viewTemplateInfo || {},
navigateOptions = viewInfo.navigateOptions || {},
target = extendUtils.extend({
root: !viewInfo.canBack,
customResolveRequired: false,
pane: templateInfo.pane,
modal: void 0 !== navigateOptions.modal ? navigateOptions.modal : templateInfo.modal || false
}, devices.current());
var matches = commonUtils.findBestMatches(target, this._availableLayoutControllers);
this._ensureOneLayoutControllerFound(target, matches);
return matches[0].controller
},
_onNavigatingBack: function(args) {
this.callBase.apply(this, arguments);
if (!args.cancel && !this.canBack() && this._activeLayoutControllersStack.length > 1) {
var previousActiveLayoutController = this._activeLayoutControllersStack[this._activeLayoutControllersStack.length - 2],
previousViewInfo = previousActiveLayoutController.activeViewInfo();
args.cancel = true;
this._activateLayoutController(previousActiveLayoutController, void 0, "backward");
this.navigationManager.currentItem(previousViewInfo.key)
}
},
_activeLayoutController: function() {
return this._activeLayoutControllersStack.length ? this._activeLayoutControllersStack[this._activeLayoutControllersStack.length - 1] : void 0
},
_getTargetNode: function(viewInfo) {
var dxEvent = (viewInfo.navigateOptions || {}).event;
return dxEvent ? $(dxEvent.target) : void 0
},
_activateLayoutController: function(layoutController, targetNode, direction) {
var that = this,
previousLayoutController = that._activeLayoutController();
if (previousLayoutController === layoutController) {
return $.Deferred().resolve().promise()
}
var d = $.Deferred();
layoutController.ensureActive(targetNode).done(function(result) {
that._deactivatePreviousLayoutControllers(layoutController, direction, result).done(function() {
that._activeLayoutControllersStack.push(layoutController);
d.resolve()
})
});
return d.promise()
},
_deactivatePreviousLayoutControllers: function(layoutController, direction) {
var that = this,
tasks = [],
controllerToDeactivate = that._activeLayoutControllersStack.pop();
if (!controllerToDeactivate) {
return $.Deferred().resolve().promise()
}
if (layoutController.isOverlay) {
that._activeLayoutControllersStack.push(controllerToDeactivate);
tasks.push(controllerToDeactivate.disable())
} else {
var transitionDeferred = $.Deferred(),
skipAnimation = false;
var getControllerDeactivator = function(controllerToDeactivate, d) {
return function() {
controllerToDeactivate.deactivate().done(function() {
d.resolve()
})
}
};
while (controllerToDeactivate && controllerToDeactivate !== layoutController) {
var d = $.Deferred();
if (controllerToDeactivate.isOverlay) {
skipAnimation = true
} else {
if (!skipAnimation) {
that.transitionExecutor.leave(controllerToDeactivate.element(), LAYOUT_CHANGE_ANIMATION_NAME, {
direction: direction
})
}
}
transitionDeferred.promise().done(getControllerDeactivator(controllerToDeactivate, d));
tasks.push(d.promise());
controllerToDeactivate = that._activeLayoutControllersStack.pop()
}
if (skipAnimation) {
transitionDeferred.resolve()
} else {
that.transitionExecutor.enter(layoutController.element(), LAYOUT_CHANGE_ANIMATION_NAME, {
direction: direction
});
that.transitionExecutor.start().done(function() {
transitionDeferred.resolve()
})
}
}
return when.apply($, tasks)
},
init: function() {
var that = this,
result = this.callBase();
result.done(function() {
that._initLayoutControllers();
that.renderNavigation()
});
return result
},
_disposeView: function(viewInfo) {
if (viewInfo.layoutController.disposeView) {
viewInfo.layoutController.disposeView(viewInfo)
}
this.callBase(viewInfo)
},
viewPort: function() {
return this._$viewPort
},
_createViewInfo: function() {
var viewInfo = this.callBase.apply(this, arguments),
templateInfo = this.getViewTemplateInfo(viewInfo.viewName);
if (!templateInfo) {
throw errors.Error("E3013", "dxView", viewInfo.viewName)
}
viewInfo.viewTemplateInfo = templateInfo;
viewInfo.layoutController = this._resolveLayoutController(viewInfo);
return viewInfo
},
_createViewModel: function(viewInfo) {
this.callBase(viewInfo);
extendUtils.extendFromObject(viewInfo.model, viewInfo.viewTemplateInfo)
},
_initLayoutControllers: function() {
var that = this;
each(that._layoutSet, function(index, controllerInfo) {
var controller = controllerInfo.controller,
target = devices.current();
if (commonUtils.findBestMatches(target, [controllerInfo]).length) {
that._availableLayoutControllers.push(controllerInfo);
if (controller.init) {
controller.init({
app: that,
$viewPort: that._$viewPort,
navigationManager: that.navigationManager,
viewEngine: that.viewEngine,
templateContext: that._templateContext,
commandManager: that.commandManager
})
}
if (controller.on) {
controller.on("viewReleased", function(viewInfo) {
that._onViewReleased(viewInfo)
});
controller.on("viewHidden", function(viewInfo) {
that._onViewHidden(viewInfo)
});
controller.on("viewRendered", function(viewInfo) {
that._processEvent("viewRendered", {
viewInfo: viewInfo
}, viewInfo.model)
});
controller.on("viewShowing", function(viewInfo, direction) {
that._processEvent("viewShowing", {
viewInfo: viewInfo,
direction: direction,
params: viewInfo.routeData
}, viewInfo.model)
});
controller.on("viewShown", function(viewInfo, direction) {
that._processEvent("viewShown", {
viewInfo: viewInfo,
direction: direction,
params: viewInfo.routeData
}, viewInfo.model)
})
}
}
})
},
_onViewReleased: function(viewInfo) {
this._releaseViewLink(viewInfo)
},
renderNavigation: function() {
var that = this;
each(that._availableLayoutControllers, function(index, controllerInfo) {
var controller = controllerInfo.controller;
if (controller.renderNavigation) {
controller.renderNavigation(that.navigation)
}
})
},
getViewTemplate: function(viewName) {
return this.viewEngine.getViewTemplate(viewName)
},
getViewTemplateInfo: function(viewName) {
var viewComponent = this.viewEngine.getViewTemplateInfo(viewName);
return viewComponent && viewComponent.option()
},
loadTemplates: function(source) {
return this.viewEngine.loadTemplates(source)
},
templateContext: function() {
return this._templateContext
}
});
module.exports = HtmlApplication;
module.exports.default = module.exports;