UNPKG

dojox

Version:

Dojo eXtensions, a rollup of many useful sub-projects and varying states of maturity – from very stable and robust, to alpha and experimental. See individual projects contain README files for details.

241 lines (221 loc) 9.3 kB
define(["dojo/_base/declare", "dojo/_base/lang", "dojo/_base/array", "dojo/_base/window", "dojo/query", "dojo/dom-geometry", "dojo/dom-attr", "dojo/dom-style", "dijit/registry", "./LayoutBase", "../utils/layout", "../utils/constraints", "dojo/sniff"], function(declare, lang, array, win, query, domGeom, domAttr, domStyle, registry, LayoutBase, layout, constraints, has){ // module: // dojox/app/controllers/Layout // summary: // Extends LayoutBase which binds "app-initLayout", "app-layoutView" and "app-resize" events on application instance. return declare("dojox.app.controllers.Layout", LayoutBase, { constructor: function(app, events){ // summary: // bind "app-initLayout" and "app-layoutView" events on application instance. // // app: // dojox/app application instance. // events: // {event : handler} }, onResize: function(){ this._doResize(this.app); // this is needed to resize the children on an orientation change or a resize of the browser. // it was being done in _doResize, but was not needed for every call to _doResize. this.resizeSelectedChildren(this.app); }, resizeSelectedChildren: function(w){ for(var hash in w.selectedChildren){ // need this to handle all selectedChildren if(w.selectedChildren[hash] && w.selectedChildren[hash].domNode){ this.app.log("in Layout resizeSelectedChildren calling resizeSelectedChildren calling _doResize for w.selectedChildren[hash].id="+w.selectedChildren[hash].id); this._doResize(w.selectedChildren[hash]); // Call resize on child widgets, needed to get the scrollableView to resize correctly initially array.forEach(w.selectedChildren[hash].domNode.children, function(child){ if(registry.byId(child.id) && registry.byId(child.id).resize){ registry.byId(child.id).resize(); } }); this.resizeSelectedChildren(w.selectedChildren[hash]); } } }, initLayout: function(event){ // summary: // Response to dojox/app "app-initLayout" event. // // example: // Use emit to trigger "app-initLayout" event, and this function will respond to the event. For example: // | this.app.emit("app-initLayout", view); // // event: Object // | {"view": view, "callback": function(){}}; this.app.log("in app/controllers/Layout.initLayout event=",event); this.app.log("in app/controllers/Layout.initLayout event.view.parent.name=[",event.view.parent.name,"]"); if (!event.view.domNode.parentNode || (has("ie") == 8 && !event.view.domNode.parentElement)) { if(this.app.useConfigOrder){ event.view.parent.domNode.appendChild(event.view.domNode); }else{ this.addViewToParentDomByConstraint(event); } } domAttr.set(event.view.domNode, "data-app-constraint", event.view.constraint); this.inherited(arguments); }, addViewToParentDomByConstraint: function(event){ // summary: // Insert the view domNode into the parent domNode based upon the constraints. // It should layout the children in this order: top, left, center, right, bottom // Unless it is rtl then it should layout the children in this order: top, right, center, left, bottom // // event: Object // | {"parent":parent, "view":view, "removeView": boolean} var newViewConstraint = event.view.constraint; if(newViewConstraint === "bottom"){ // if new is bottom always place last event.view.parent.domNode.appendChild(event.view.domNode); }else if(newViewConstraint === "top"){ // if new is top always place first event.view.parent.domNode.insertBefore(event.view.domNode, event.view.parent.domNode.firstChild); }else{ // need to compare new constraint to the previous ones if(event.view.parent.domNode.children.length > 0){ // parent node has children, check constraints // in this loop if previous is top or left skip it and look for next child, otherwise process it for(var childIndex in event.view.parent.domNode.children){ var child = event.view.parent.domNode.children[childIndex]; var dir = domStyle.get(event.view.parent.domNode,"direction"); var isltr = (dir === "ltr"); var LEADING_VIEW = isltr ? "left" : "right"; var TRAILING_VIEW = isltr ? "right" : "left"; if(child.getAttribute && child.getAttribute("data-app-constraint")) { var previousViewConstraint = child.getAttribute("data-app-constraint"); // if previous is bottom or previous is Trailing // or previous is not top and newView is Leading we need to insert before this child if(previousViewConstraint === "bottom" || (previousViewConstraint === TRAILING_VIEW) || (previousViewConstraint !== "top" && (newViewConstraint === LEADING_VIEW))){ event.view.parent.domNode.insertBefore(event.view.domNode, child); break; } } } } } // if the domNode was not added to the parent yet add it to the end now if (!event.view.domNode.parentNode || (has("ie") == 8 && !event.view.domNode.parentElement)) { event.view.parent.domNode.appendChild(event.view.domNode); } }, _doResize: function(view){ // summary: // resize view. // // view: Object // view instance needs to do layout. var node = view.domNode; if(!node){ this.app.log("Warning - View has not been loaded, in Layout _doResize view.domNode is not set for view.id="+view.id+" view=",view); return; } // If either height or width wasn't specified by the user, then query node for it. // But note that setting the margin box and then immediately querying dimensions may return // inaccurate results, so try not to depend on it. var mb = {}; if( !("h" in mb) || !("w" in mb) ){ mb = lang.mixin(domGeom.getMarginBox(node), mb); // just use dojo/_base/html.marginBox() to fill in missing values } // Compute and save the size of my border box and content box // (w/out calling dojo/_base/html.contentBox() since that may fail if size was recently set) if(view !== this.app){ var cs = domStyle.getComputedStyle(node); var me = domGeom.getMarginExtents(node, cs); var be = domGeom.getBorderExtents(node, cs); var bb = (view._borderBox = { w: mb.w - (me.w + be.w), h: mb.h - (me.h + be.h) }); var pe = domGeom.getPadExtents(node, cs); view._contentBox = { l: domStyle.toPixelValue(node, cs.paddingLeft), t: domStyle.toPixelValue(node, cs.paddingTop), w: bb.w - pe.w, h: bb.h - pe.h }; }else{ // if we are layouting the top level app the above code does not work when hiding address bar // so let's use similar code to dojo mobile. view._contentBox = { l: 0, t: 0, h: win.global.innerHeight || win.doc.documentElement.clientHeight, w: win.global.innerWidth || win.doc.documentElement.clientWidth }; } this.inherited(arguments); }, layoutView: function(event){ // summary: // Response to dojox/app "app-layoutView" event. // // example: // Use emit to trigger "app-layoutView" event, and this function will response the event. For example: // | this.app.emit("app-layoutView", view); // // event: Object // | {"parent":parent, "view":view, "removeView": boolean} if(event.view){ this.inherited(arguments); // normally when called from transition doResize will be false, and the resize will only be done when the app-resize event is fired if(event.doResize){ this._doResize(event.parent || this.app); this._doResize(event.view); } } }, _doLayout: function(view){ // summary: // do view layout. // // view: Object // view instance needs to do layout. if(!view){ console.warn("layout empty view."); return; } this.app.log("in Layout _doLayout called for view.id="+view.id+" view=",view); var children; // TODO: probably need to handle selectedChildren here, not just selected child... // TODO: why are we passing view here? not parent? This call does not seem logical? var selectedChild = constraints.getSelectedChild(view, view.constraint); if(selectedChild && selectedChild.isFullScreen){ console.warn("fullscreen sceen layout"); /* fullScreenScene=true; children=[{domNode: selectedChild.domNode,constraint: "center"}]; query("> [constraint]",this.domNode).forEach(function(c){ if(selectedChild.domNode!==c.domNode){ dstyle(c.domNode,"display","none"); } }) */ }else{ children = query("> [data-app-constraint]", view.domNode).map(function(node){ var w = registry.getEnclosingWidget(node); if(w){ w._constraint = domAttr.get(node, "data-app-constraint"); return w; } return { domNode: node, _constraint: domAttr.get(node, "data-app-constraint") }; }); if(selectedChild){ children = array.filter(children, function(c){ // do not need to set display none here it is set in select. return c.domNode && c._constraint; }, view); } } // We don't need to layout children if this._contentBox is null for the operation will do nothing. if(view._contentBox){ layout.layoutChildren(view.domNode, view._contentBox, children); } } }); });