/////////////////////////////////////////////////////////////////////////// // Copyright © 2014 - 2018 Esri. All Rights Reserved. // // Licensed under the Apache License Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /////////////////////////////////////////////////////////////////////////// define([ 'dojo/_base/declare', 'dojo/_base/lang', 'dojo/Evented', 'dojo/_base/array', 'dojo/_base/html', 'dojo/_base/fx', 'dojo/on', 'dojo/sniff', 'dojo/touch', 'dojo/query', 'dojo/dnd/move', 'dijit/_WidgetBase', 'jimu/utils' ], function(declare, lang, Evented, array, html, baseFx, on, has, touch, query, Move, _WidgetBase, jimuUtils) { var count = 0; /* global jimuConfig */ return declare([_WidgetBase, Evented], { //summary: // show a popup window declaredClass: 'jimu.dijit.Popup', baseClass: 'jimu-popup', //titleLabel: String // the popup window title. if this property is empty, no title display titleLabel: '', //content: DOM|Dijit|String content: null, //container: String|DOM // this popup parent dom node container: null, customZIndex: null, //custom z-index //buttons: Object[] // this is the object format /*===== //label: String label: '', //onClick: function onClick: null, if this function return false, the popup will not close, or the popup will close after button click. //key: dojo/keys key: optional, if key is set, the button will response to the key event =====*/ buttons: [], //enabledButtons: DOM[] enabledButtons: [], //disabledButton: DOM[] disabledButtons: [], // parseKeyhanles: hashMap // pauseKeys: [], //onClose: function // callback function when click the close button. //If this function return false, the popup will not close onClose: null, _fixedHeight: false, //it's true only when height is numberical value. // the height of Popup depends on the height of content autoHeight: false, isResize: true, // the width of Popup depends on the windows.w. // Its range is (0,1] horizontalWidthRate: 0, maxHeight: 800, maxWidth: 1024, //optional enableMoveable: true, hasTitle: true, //if false, no title bar and close btn contentHasNoMargin: false, //no margin on content except margin-bottom is 3px. hasOverlay: true, //if has overlay moveToCenter: true, //it works when moveToCenter is false. eg: {left: 1px, top: 1px, width: 100, height: 100} //left and top are required, width and height are optional. customPosition: null, hiddenAfterInit: false, constructor: function() { this.buttons = []; this.enabledButtons = []; this.disabledButtons = []; this.pauseKeyHandles = []; this.container = jimuConfig.layoutId; }, postCreate: function() { this._preProcessing(); this.inherited(arguments); // We should not set tabIndex for Popup, there is an issue for Edge browser if we set it. // #4888 // this.domNode.tabIndex = 1; // init dom node this._initDomNode(); this._addStylesByHeightType(); //position the popup this._calcAndSetPosition(); html.place(this.domNode, this.container); //although this function only works when autoHeight is true. //but we need it to trigger event 'popupHasInitedSuccessfully', so keep this setTimeout // if(this.autoHeight){ setTimeout(lang.hitch(this, function() { //tolerance height this._calcAndSetPosition(true, false); }), 50); // }else{ // this._calcAndSetPosition(true, false); // } // this._limitButtonsMaxWidth(); if(this.isResize){ this.own(on(window, 'resize', lang.hitch(this, function() { this._calcAndSetPosition(true, true); }))); } if(this.hasOverlay){ this.overlayNode = html.create('div', { 'class': 'jimu-overlay' }, this.container); } if(this.customZIndex || this.customZIndex === 0){//numberical value could be 0. html.setStyle(this.domNode, 'zIndex', this.customZIndex + 1); if(this.overlayNode){ html.setStyle(this.overlayNode, 'zIndex', this.customZIndex); } }else{ this._increaseZIndex(); } if(this.hiddenAfterInit){ this.hide(); } baseFx.animateProperty({ node: this.domNode, properties: { opacity: 1 }, duration: 200 }).play(); this.domNode.focus(); }, _preProcessing: function() { if (typeof this.width !== 'number') { this.width = this.maxWidth; } if (typeof this.height === 'number') { this._fixedHeight = true; this.autoHeight = false; } if (this.autoHeight) { this.maxHeight = 598; } }, _createTitleNode: function() { this.titleNode = html.create('div', { 'class': 'title' }, this.domNode); this.titleLabeNode = html.create('span', { 'class': 'title-label jimu-float-leading', innerHTML: this.titleLabel || ' ' }, this.titleNode); this.closeBtnNode = html.create('div', { 'class': 'close-btn jimu-icon jimu-icon-close jimu-float-trailing' }, this.titleNode); var eventName = null; if ('ontouchstart' in document) { eventName = touch.press; } else { eventName = 'click'; } this.own(on(this.closeBtnNode, eventName, lang.hitch(this, this.close))); }, _initDomNode: function() { if(this.hasTitle){ this._createTitleNode(); } this.contentContainerNode = html.create('div', { 'class': 'content' }, this.domNode); if (this.content) { if (typeof this.content === 'string') { this.contentContainerNode.innerHTML = this.content; } else if (this.content.domNode) { this.content.placeAt(this.contentContainerNode); this.content.popup = this; } else if (this.content.nodeType === 1) { html.place(this.content, this.contentContainerNode); } } this.buttonContainer = html.create('div', { 'class': 'button-container' }, this.domNode); if (this.buttons.length === 0) { html.setStyle(this.buttonContainer, 'display', 'none'); } for (var i = this.buttons.length - 1; i > -1; i--) { this._createButton(this.buttons[i]); if (this.buttons[i].disable) { this.disableButton(i); } } }, _limitButtonsMaxWidth: function() { var btnLength = this.enabledButtons.length; if (btnLength === 0) { return; } var btnContainerBox = html.getContentBox(this.buttonContainer); var btnMarginBox = html.getMarginExtents(this.enabledButtons[0]); var btnPbBox = html.getPadBorderExtents(this.enabledButtons[0]); var btnMaxWidth = 0; //it seems IE 8 ignores border-box when using min-height/width on the same element var _ie8hackWidth = has('ie') === 8 ? btnPbBox.l + btnPbBox.r : 0; btnMaxWidth = (btnContainerBox.w - (btnMarginBox.l + btnMarginBox.r + _ie8hackWidth) * btnLength) / btnLength; if (btnMaxWidth > 0) { array.forEach(this.enabledButtons, lang.hitch(this, function(btn) { html.setStyle(btn, 'maxWidth', btnMaxWidth + 'px'); })); array.forEach(this.disabledButtons, lang.hitch(this, function(btn) { html.setStyle(btn, 'maxWidth', btnMaxWidth + 'px'); })); } }, _moveableNode: function(width, tolerance) { if (this.moveable) { this.moveable.destroy(); this.moveable = null; } var containerBox = html.getMarginBox(this.container); containerBox.l = containerBox.l - width + tolerance; containerBox.w = containerBox.w + 2 * (width - tolerance); this.moveable = new Move.boxConstrainedMoveable(this.domNode, { box: containerBox, handle: this.titleNode || this.contentContainerNode, within: true }); this.own(on(this.moveable, 'Moving', lang.hitch(this, this.onMoving))); this.own(on(this.moveable, 'MoveStop', lang.hitch(this, this.onMoveStop))); }, _getHeaderBox: function() { var headerBox; if (query('#header').length === 0) { headerBox = { t: 0, l: 0, w: 0, h: 0 }; } else { headerBox = html.getMarginBox('header'); } return headerBox; }, _getFooterBox: function() { var footerBox; if (query('.footer', this.container).length === 0) { footerBox = { t: 0, l: 0, w: 0, h: 0 }; } else { footerBox = html.getMarginBox(query('.footer', this.container)[0]); } return footerBox; }, _calcAndSetPosition: function(ifSendEvent, ifResize) { var selfBox = html.getMarginBox(this.domNode); //because this method is called async, so the container may be destoryed before this. if(typeof this.container === 'string' && !html.byId(this.container)){ return; } var box = html.getContentBox(this.container); var headerBox = this._getHeaderBox(), footerBox = this._getFooterBox(); var flexHeight = box.h - headerBox.h - footerBox.h - 40; var width = 0, height = 0; if(this.customPosition && this.customPosition.height){ this.height = this.customPosition.height; height = (typeof this.height === 'number') ? this.height + 'px' : this.height; }else{ if (this._fixedHeight) { //use flexH when height is out of bounds this.height = this.height > flexHeight ? flexHeight : this.height; } else if (this.autoHeight) { var selfBoxH = selfBox.h > flexHeight ? flexHeight : selfBox.h;//out of bounds this.height = selfBoxH || flexHeight - 100 * 2; // tolerance } else { this.height = flexHeight > this.maxHeight ? this.maxHeight : flexHeight; } height = this.height + 'px'; } if(this.customPosition && this.customPosition.width){ this.width = this.customPosition.width; width = (typeof this.width === 'number') ? this.width + 'px' : this.width; }else{ // this.width = this.width || this.maxWidth; // _calculateWidth if (typeof this.horizontalWidthRate === 'number' && this.horizontalWidthRate > 0) { var popupWidth = (html.getMarginBox(window.document.body).w) * this.horizontalWidthRate; popupWidth = popupWidth > this.maxWidth ? popupWidth : this.maxWidth; this.width = popupWidth; }else{ this.width = this.width || this.maxWidth; } width = this.width + 'px'; } var left = 0, top = 0; if(this.customPosition){ left = (typeof this.customPosition.left === 'number') ? this.customPosition.left + 'px' : this.customPosition.left; top = (typeof this.customPosition.top === 'number') ? this.customPosition.top + 'px' : this.customPosition.top; }else if(this.moveToCenter){ top = (flexHeight - this.height) / 2 + headerBox.h + 20; top = top < headerBox.h ? headerBox.h : top; left = (box.w - this.width) / 2 + 'px'; top = top + 'px'; } html.setStyle(this.domNode, { width: width, height: this.autoHeight ? 'auto' : height, left: left, top: top }); if(this.enableMoveable){ this._moveableNode(this.width, 100); } // console.log("calc popup's position"); if(!this.moveToCenter && ifSendEvent && html.getStyle(this.domNode, 'display') === 'block'){ this.emit('popupHasInitedSuccessfully', ifResize); } }, setDomNodeStyls: function(stylesObj){ html.setStyle(this.domNode, stylesObj); }, setCustomPosition: function(left, top, width, height) { this.width = width || this.width || this.maxWidth; this.height = height || this.height || this.maxHeight; left = (typeof left === 'number') ? left + 'px' : left; top = (typeof top === 'number') ? top + 'px' : top; width = (typeof this.width === 'number') ? this.width + 'px' : this.width; height = (typeof this.height === 'number') ? this.height + 'px' : this.height; html.setStyle(this.domNode, { left: left, top: top, width: width, height: this.autoHeight ? 'auto' : height }); }, _addStylesByHeightType: function() { if (!this.autoHeight) { // position: absolute html.addClass(this.contentContainerNode, 'content-absolute'); if(!this.hasTitle){ html.addClass(this.contentContainerNode, 'no-popup-title-content-absolute'); } html.addClass(this.buttonContainer, 'button-container-absolute'); if (this.buttons.length === 0) { html.setStyle(this.contentContainerNode, { bottom: '15px' }); } } else { // position: static html.addClass(this.contentContainerNode, 'content-static'); if (this.buttons.length === 0) { html.setStyle(this.contentContainerNode, { marginBottom: '15px' }); } } if(this.contentHasNoMargin){ html.addClass(this.contentContainerNode, 'content-fill-Popup'); } }, _increaseZIndex: function() { var baseIndex = 200; html.setStyle(this.domNode, 'zIndex', count + baseIndex + 1); if(this.overlayNode){ html.setStyle(this.overlayNode, 'zIndex', count + baseIndex); } count++; }, setTitleLabel: function(titleLabel) { this.titleNode.innerHTML = jimuUtils.stripHTML(titleLabel); }, onMoving: function(mover) { html.setStyle(mover.node, 'opacity', 0.9); }, onMoveStop: function(mover) { html.setStyle(mover.node, 'opacity', 1); }, show: function(){ if(this.overlayNode){ html.setStyle(this.overlayNode, 'display', 'block'); } html.setStyle(this.domNode, 'display', 'block'); }, hide: function(){ if(this.overlayNode){ html.setStyle(this.overlayNode, 'display', 'none'); } html.setStyle(this.domNode, 'display', 'none'); }, close: function() { if (this.onClose && this.onClose() === false) { return; } var parent = this.domNode.parentNode; var cloneNode = lang.clone(this.domNode); html.setStyle(this.domNode, 'display', 'none'); if(this.overlayNode){ html.destroy(this.overlayNode); } this.destroy(); if(this.moveable) { this.moveable.destroy(); } html.place(cloneNode, parent); baseFx.animateProperty({ node: cloneNode, properties: { opacity: 0 }, duration: 200, onEnd: function() { html.destroy(cloneNode); } }).play(); }, addButton: function(btn) { this._createButton(btn); }, _createButton: function(button) { var appendedClasses = " "; if(button.classNames && button.classNames.length > 0){ if(typeof button.classNames.join === 'function'){ appendedClasses += button.classNames.join(" "); } } var node = html.create('div', { 'class': 'jimu-btn jimu-popup-action-btn jimu-float-trailing jimu-trailing-margin1 ' + appendedClasses, 'innerHTML': button.label, 'title': button.title || button.label }, this.buttonContainer); this.enabledButtons.unshift(node); var disableNode = html.create('div', { 'class': 'jimu-btn jimu-state-disabled jimu-float-trailing jimu-trailing-margin1 ' + appendedClasses, 'title': button.title || button.label, 'innerHTML': button.label, 'style': { display: 'none' } }, this.buttonContainer); this.disabledButtons.unshift(disableNode); this.own(on(node, 'click', lang.hitch(this, function(evt) { //we don't close popup because that maybe the //listener function is async if (button.onClick) { button.onClick(evt); } else { this.close(); } }))); // var existKey = false; // if (typeof button.key === 'number') { // for (var attr in keys) { // if (keys[attr] === button.key) { // existKey = true; // break; // } // } // } // if (existKey) { // this.own(on(this.domNode, 'keydown', lang.hitch(this, function(event) { // var keyCode = event.keyCode !== undefined ? event.keyCode : event.which; // if (keyCode === button.key && this.pauseKeys.indexOf(keyCode) === -1) { // node.click(); // } // }))); // } }, setButtonProps: function(idx, props) { if (typeof idx === 'number' && isFinite(idx)) { idx = idx; } else { props = idx; idx = 0; } if (!props || this.enabledButtons.length === 0) { return; } for (var p in props) { if (p === 'title') { html.setAttr(this.enabledButtons[idx], 'title', props[p]); html.setAttr(this.disabledButtons[idx], 'title', props[p]); } else if (p === 'label') { html.setProp(this.enabledButtons[idx], 'innerHTML', props[p]); html.setProp(this.disabledButtons[idx], 'innerHTML', props[p]); } } }, enableButton: function(idx) { // var btn = null; if (typeof idx === 'number' && isFinite(idx) && idx < this.enabledButtons.length) { html.setStyle(this.enabledButtons[idx], 'display', 'inline-block'); html.setStyle(this.disabledButtons[idx], 'display', 'none'); // btn = this.buttons[idx]; // if (btn && btn.key && this.pauseKeys.indexOf(btn.key) > -1) { // this.pauseKeys.splice(this.pauseKeys.indexOf(btn.key), 1); // } } else { array.forEach(this.enabledButtons[idx], lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'inline-block'); })); array.forEach(this.disabledButtons[idx], lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'none'); })); // this.pauseKeys.splice(0, this.pauseKeys.length); } }, disableButton: function(idx) { // var btn = null; if (typeof idx === 'number' && isFinite(idx) && idx < this.disabledButtons.length) { html.setStyle(this.disabledButtons[idx], 'display', 'inline-block'); html.setStyle(this.enabledButtons[idx], 'display', 'none'); // btn = this.buttons[idx]; // if (btn && btn.key && this.pauseKeys.indexOf(btn.key) === -1) { // this.pauseKeys.push(btn.key); // } } else { array.forEach(this.disabledButtons, lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'inline-block'); })); array.forEach(this.enabledButtons, lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'none'); })); // array.forEach(this.buttons, lang.hitch(this, function(btn) { // if (btn && btn.key && this.pauseKeys.indexOf(btn.key) === -1) { // this.pauseKeys.push(btn.key); // } // })); } }, showButton: function(idx) { // var btn = null; // if (typeof idx === 'number' && isFinite(idx) && idx < this.enabledButtons.length) { // html.setStyle(this.enabledButtons[idx], 'display', 'inline-block'); // html.setStyle(this.disabledButtons[idx], 'display', 'none'); // // btn = this.buttons[idx]; // // if (btn && btn.key && this.pauseKeys.indexOf(btn.key) > -1) { // // this.pauseKeys.splice(this.pauseKeys.indexOf(btn.key), 1); // // } // } else { // array.forEach(this.enabledButtons[idx], lang.hitch(this, function(itm) { // html.setStyle(itm, 'display', 'inline-block'); // })); // array.forEach(this.disabledButtons[idx], lang.hitch(this, function(itm) { // html.setStyle(itm, 'display', 'none'); // })); // // this.pauseKeys.splice(0, this.pauseKeys.length); // } this.enableButton(idx); }, hideButton: function(idx) { if (typeof idx === 'number' && isFinite(idx) && idx < this.disabledButtons.length) { html.setStyle(this.disabledButtons[idx], 'display', 'none'); html.setStyle(this.enabledButtons[idx], 'display', 'none'); } else { array.forEach(this.disabledButtons, lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'none'); })); array.forEach(this.enabledButtons, lang.hitch(this, function(itm) { html.setStyle(itm, 'display', 'none'); })); } }, //custom resize popup's width and height resize: function(size){ // console.log('function - resize'); if(size){ this.width = size.w; this.height = size.h; } this._calcAndSetPosition(); if (this.content && this.content.domNode && this.content.resize){ this.content.resize(); } } }); });