diff --git a/bower.json b/bower.json index 7f8f98fd4..1b6b603eb 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ "dist/tui-calendar.js", "dist/tui-calendar.css" ], - "version": "1.12.12", + "version": "1.12.13", "description": "TOAST UI Calendar", "ignore": [ "**/.*", diff --git a/dist/tui-calendar.css b/dist/tui-calendar.css index 8e726464e..af58ae5cc 100644 --- a/dist/tui-calendar.css +++ b/dist/tui-calendar.css @@ -1,6 +1,6 @@ /*! * TOAST UI Calendar - * @version 1.12.12 | Mon Mar 23 2020 + * @version 1.12.13 | Tue Apr 28 2020 * @author NHN FE Development Lab * @license MIT */ diff --git a/dist/tui-calendar.js b/dist/tui-calendar.js index 403402af5..a6d9f0b70 100644 --- a/dist/tui-calendar.js +++ b/dist/tui-calendar.js @@ -1,6 +1,6 @@ /*! * TOAST UI Calendar - * @version 1.12.12 | Mon Mar 23 2020 + * @version 1.12.13 | Tue Apr 28 2020 * @author NHN FE Development Lab * @license MIT */ @@ -3166,7 +3166,7 @@ module.exports = { if (datetime.compare(schedule[propName], data[propName])) { changes[propName] = data[propName]; } - } else if (data[propName] && schedule[propName] !== data[propName]) { + } else if (!util.isUndefined(data[propName]) && schedule[propName] !== data[propName]) { changes[propName] = data[propName]; } }); @@ -3799,6 +3799,7 @@ datetime = { var date = new TZDate(d); if (datetime.isStartOfDay(d)) { date.setDate(date.getDate() - 1); + date.setHours(23, 59, 59); } return date; @@ -4480,6 +4481,16 @@ var domevent = { } return -1; + }, + + /** + * Get target from event object + * + * @param {Event} event - The event object + * @returns {object} - The event target object + */ + getEventTarget: function(event) { + return event.target || event.srcElement; } }; @@ -4661,7 +4672,13 @@ domutil = { * @returns {HTMLElement} - element finded or null. */ closest: function(el, selector, excludeEl) { - var parent = el.parentNode; + var parent; + + if (!el) { + return null; + } + + parent = el.parentNode; if (!excludeEl && domutil._matcher(el, selector)) { return el; @@ -5082,7 +5099,7 @@ domutil = { } }; -/*eslint-disable*/ +/* eslint-disable */ var userSelectProperty = domutil.testProp([ 'userSelect', 'WebkitUserSelect', @@ -5092,7 +5109,8 @@ var userSelectProperty = domutil.testProp([ ]); var supportSelectStart = 'onselectstart' in document; var prevSelectStyle = ''; -/* eslint-enable*/ + +/* eslint-enable */ /** * Disable browser's text selection behaviors. @@ -5100,8 +5118,8 @@ var prevSelectStyle = ''; */ domutil.disableTextSelection = (function() { if (supportSelectStart) { - return function(dom) { - domevent.on(dom, 'selectstart', domevent.preventDefault); + return function(dom, onSelectstartHandler) { + domevent.on(dom, 'selectstart', onSelectstartHandler || domevent.preventDefault); }; } @@ -5118,8 +5136,8 @@ domutil.disableTextSelection = (function() { */ domutil.enableTextSelection = (function() { if (supportSelectStart) { - return function() { - domevent.off(window, 'selectstart', domevent.preventDefault); + return function(dom, onSelectstartHandler) { + domevent.off(window, 'selectstart', onSelectstartHandler || domevent.preventDefault); }; } @@ -7253,12 +7271,17 @@ Base.prototype.createSchedules = function(dataList, silent) { * @param {object} options updated object data. * @returns {Schedule} updated schedule instance */ +// eslint-disable-next-line complexity Base.prototype.updateSchedule = function(schedule, options) { var start = options.start || schedule.start; var end = options.end || schedule.end; options = options || {}; + if (['milestone', 'task', 'allday', 'time'].indexOf(options.category) > -1) { + schedule.set('category', options.category); + } + if (options.category === 'allday') { options.isAllDay = true; } @@ -8250,7 +8273,7 @@ var Week = { var model = viewModel.model; viewModel.hasMultiDates = true; viewModel.renderStarts = datetime.start(model.getStarts()); - viewModel.renderEnds = datetime.end(model.getEnds()); + viewModel.renderEnds = datetime.convertStartDayToLastDay(model.getEnds()); }); }, @@ -12889,6 +12912,7 @@ module.exports = DayGridResizeGuide; var util = __webpack_require__(/*! tui-code-snippet */ "tui-code-snippet"); var domutil = __webpack_require__(/*! ../common/domutil */ "./src/js/common/domutil.js"); var domevent = __webpack_require__(/*! ../common/domevent */ "./src/js/common/domevent.js"); +var config = __webpack_require__(/*! ../config */ "./src/js/config.js"); /** * @constructor @@ -12976,8 +13000,8 @@ Drag.prototype._toggleDragEvent = function(toBind) { method = 'enable'; } - domutil[method + 'TextSelection'](container); - domutil[method + 'ImageDrag'](container); + domutil[method + 'TextSelection'](container, preventDefaultWhenNotPopup); + domutil[method + 'ImageDrag'](container, preventDefaultWhenNotPopup); domevent[domMethod](global.document, { mousemove: this._onMouseMove, mouseup: this._onMouseUp @@ -12991,7 +13015,7 @@ Drag.prototype._toggleDragEvent = function(toBind) { */ Drag.prototype._getEventData = function(mouseEvent) { return { - target: mouseEvent.target || mouseEvent.srcElement, + target: domevent.getEventTarget(mouseEvent), originEvent: mouseEvent }; }; @@ -13002,7 +13026,7 @@ Drag.prototype._getEventData = function(mouseEvent) { */ Drag.prototype._onMouseDown = function(mouseDownEvent) { var opt = this.options, - target = (mouseDownEvent.srcElement || mouseDownEvent.target); + target = domevent.getEventTarget(mouseDownEvent); // only primary button can start drag. if (domevent.getMouseButton(mouseDownEvent) !== 0) { @@ -13047,7 +13071,7 @@ Drag.prototype._onMouseMove = function(mouseMoveEvent) { distance = this.options.distance; // prevent automatic scrolling. - domevent.preventDefault(mouseMoveEvent); + preventDefaultWhenNotPopup(mouseMoveEvent); if (this._distance < distance) { this._distance += 1; @@ -13122,6 +13146,19 @@ Drag.prototype._onMouseUp = function(mouseUpEvent) { this._clearData(); }; +/** + * If the target is not a popup, it prevents the default. + * @method + * @param {MouseEvent} event - Mouse event object + */ +function preventDefaultWhenNotPopup(event) { + var popup = domutil.closest(event.target, config.classname('.popup')); + + if (!popup) { + domevent.preventDefault(event); + } +} + util.CustomEvents.mixin(Drag); module.exports = Drag; @@ -15641,7 +15678,7 @@ var timeCore = { ); return util.extend({ - target: mouseEvent.target || mouseEvent.srcElement, + target: domevent.getEventTarget(mouseEvent), relatedView: timeView, originEvent: mouseEvent, mouseY: mouseY, @@ -19343,7 +19380,7 @@ util.inherit(More, View); * @param {MouseEvent} clickEvent - mouse event object */ More.prototype._onClick = function(clickEvent) { - var target = (clickEvent.target || clickEvent.srcElement); + var target = domevent.getEventTarget(clickEvent); var className = config.classname('month-more-close'); if (!domutil.hasClass(target, className) && !domutil.closest(target, '.' + className)) { @@ -19359,7 +19396,7 @@ More.prototype._onClick = function(clickEvent) { * @param {MouseEvent} mouseDownEvent - mouse event object */ More.prototype._onMouseDown = function(mouseDownEvent) { - var target = (mouseDownEvent.target || mouseDownEvent.srcElement), + var target = domevent.getEventTarget(mouseDownEvent), moreLayer = domutil.closest(target, config.classname('.month-more')); if (moreLayer) { @@ -19775,10 +19812,10 @@ var config = __webpack_require__(/*! ../../config */ "./src/js/config.js"); var domevent = __webpack_require__(/*! ../../common/domevent */ "./src/js/common/domevent.js"); var domutil = __webpack_require__(/*! ../../common/domutil */ "./src/js/common/domutil.js"); var common = __webpack_require__(/*! ../../common/common */ "./src/js/common/common.js"); +var datetime = __webpack_require__(/*! ../../common/datetime */ "./src/js/common/datetime.js"); var tmpl = __webpack_require__(/*! ../template/popup/scheduleCreationPopup.hbs */ "./src/js/view/template/popup/scheduleCreationPopup.hbs"); var TZDate = timezone.Date; var MAX_WEEK_OF_MONTH = 6; -var ARROW_WIDTH_HALF = 8; /** * @constructor @@ -19825,7 +19862,7 @@ util.inherit(ScheduleCreationPopup, View); * @param {MouseEvent} mouseDownEvent - mouse event object */ ScheduleCreationPopup.prototype._onMouseDown = function(mouseDownEvent) { - var target = (mouseDownEvent.target || mouseDownEvent.srcElement), + var target = domevent.getEventTarget(mouseDownEvent), popupLayer = domutil.closest(target, config.classname('.floating-layer')); if (popupLayer) { @@ -19852,7 +19889,7 @@ ScheduleCreationPopup.prototype.destroy = function() { * @param {MouseEvent} clickEvent - mouse event object */ ScheduleCreationPopup.prototype._onClick = function(clickEvent) { - var target = (clickEvent.target || clickEvent.srcElement); + var target = domevent.getEventTarget(clickEvent); util.forEach(this._onClickListeners, function(listener) { return !listener(target); @@ -20002,12 +20039,16 @@ ScheduleCreationPopup.prototype._toggleIsPrivate = function(target) { * @param {HTMLElement} target click event target * @returns {boolean} whether save button is clicked or not */ +// eslint-disable-next-line complexity ScheduleCreationPopup.prototype._onClickSaveSchedule = function(target) { var className = config.classname('popup-save'); var cssPrefix = config.cssPrefix; - var title, isPrivate, location, isAllDay, startDate, endDate, state; - var start, end, calendarId; - var changes; + var title; + var startDate; + var endDate; + var rangeDate; + var form; + var isAllDay; if (!domutil.hasClass(target, className) && !domutil.closest(target, '.' + className)) { return false; @@ -20017,78 +20058,32 @@ ScheduleCreationPopup.prototype._onClickSaveSchedule = function(target) { startDate = new TZDate(this.rangePicker.getStartDate()).toLocalTime(); endDate = new TZDate(this.rangePicker.getEndDate()).toLocalTime(); - if (!title.value) { - title.focus(); - - return true; - } + if (!this._validateForm(title, startDate, endDate)) { + if (!title.value) { + title.focus(); + } - if (!startDate && !endDate) { - return true; + return false; } - isPrivate = !domutil.hasClass(domutil.get(cssPrefix + 'schedule-private'), config.classname('public')); - location = domutil.get(cssPrefix + 'schedule-location'); - state = domutil.get(cssPrefix + 'schedule-state'); isAllDay = !!domutil.get(cssPrefix + 'schedule-allday').checked; + rangeDate = this._getRangeDate(startDate, endDate, isAllDay); - if (isAllDay) { - startDate.setHours(0, 0, 0); - endDate.setHours(23, 59, 59); - } - - start = new TZDate(startDate); - end = new TZDate(endDate); - - if (this._selectedCal) { - calendarId = this._selectedCal.id; - } + form = { + calendarId: this._selectedCal ? this._selectedCal.id : null, + title: title, + location: domutil.get(cssPrefix + 'schedule-location'), + start: rangeDate.start, + end: rangeDate.end, + isAllDay: isAllDay, + state: domutil.get(cssPrefix + 'schedule-state').innerText, + isPrivate: !domutil.hasClass(domutil.get(cssPrefix + 'schedule-private'), config.classname('public')) + }; if (this._isEditMode) { - changes = common.getScheduleChanges( - this._schedule, - ['calendarId', 'title', 'location', 'start', 'end', 'isAllDay', 'state'], - { - calendarId: calendarId, - title: title.value, - location: location.value, - start: start, - end: end, - isAllDay: isAllDay, - state: state.innerText - } - ); - - this.fire('beforeUpdateSchedule', { - schedule: util.extend({ - raw: { - class: isPrivate ? 'private' : 'public' - } - }, this._schedule), - changes: changes, - start: start, - end: end, - calendar: this._selectedCal, - triggerEventName: 'click' - }); + this._onClickUpdateSchedule(form); } else { - /** - * @event ScheduleCreationPopup#beforeCreateSchedule - * @type {object} - * @property {Schedule} schedule - new schedule instance to be added - */ - this.fire('beforeCreateSchedule', { - calendarId: calendarId, - title: title.value, - location: location.value, - raw: { - class: isPrivate ? 'private' : 'public' - }, - start: start, - end: end, - isAllDay: isAllDay, - state: state.innerText - }); + this._onClickCreateSchedule(form); } this.hide(); @@ -20189,20 +20184,9 @@ ScheduleCreationPopup.prototype._setPopupPositionAndArrowDirection = function(gu width: layer.offsetWidth, height: layer.offsetHeight }; - var windowSize = { - right: window.innerWidth, - bottom: window.innerHeight - }; - var parentRect = this.layer.parent.getBoundingClientRect(); - var parentBounds = { - left: parentRect.left, - top: parentRect.top - }; - var pos; + var containerBound = this.container.getBoundingClientRect(); + var pos = this._calcRenderingData(layerSize, containerBound, guideBound); - pos = this._calcRenderingData(layerSize, windowSize, guideBound); - pos.x -= parentBounds.left; - pos.y -= (parentBounds.top + 6); this.layer.setPosition(pos.x, pos.y); this._setArrowDirection(pos.arrow); }; @@ -20254,36 +20238,116 @@ ScheduleCreationPopup.prototype._getBoundOfFirstRowGuideElement = function(guide }; /** - * Calculate rendering position usering guide elements - * @param {{width: {number}, height: {number}}} layerSize - popup layer's width and height - * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} parentSize - width and height of the upper layer, that acts as a border of popup - * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} guideBound - guide element bound data - * @returns {PopupRenderingData} rendering position of popup and popup arrow + * Get calculate rendering positions of y and arrow direction by guide block elements + * @param {number} guideBoundTop - guide block's top + * @param {number} guideBoundBottom - guide block's bottom + * @param {number} layerHeight - popup layer's height + * @param {number} containerTop - container's top + * @param {number} containerBottom - container's bottom + * @returns {YAndArrowDirection} y and arrowDirection */ -ScheduleCreationPopup.prototype._calcRenderingData = function(layerSize, parentSize, guideBound) { - var guideHorizontalCenter = (guideBound.left + guideBound.right) / 2; - var x = guideHorizontalCenter - (layerSize.width / 2); - var y = guideBound.top - layerSize.height + 3; +ScheduleCreationPopup.prototype._getYAndArrowDirection = function( + guideBoundTop, + guideBoundBottom, + layerHeight, + containerTop, + containerBottom +) { var arrowDirection = 'arrow-bottom'; - var arrowLeft; + var MARGIN = 3; + var y = guideBoundTop - layerHeight; - if (y < 0) { - y = guideBound.bottom + 9; + if (y < containerTop) { + y = guideBoundBottom - containerTop + MARGIN; arrowDirection = 'arrow-top'; + } else { + y = y - containerTop - MARGIN; } - if (x > 0 && (x + layerSize.width > parentSize.right)) { - x = parentSize.right - layerSize.width; + if (y + layerHeight > containerBottom) { + y = containerBottom - layerHeight - containerTop - MARGIN; } - if (x < 0) { - x = 0; + /** + * @typedef {Object} YAndArrowDirection + * @property {number} y - top position of popup layer + * @property {string} [arrowDirection] - direction of popup arrow + */ + return { + y: y, + arrowDirection: arrowDirection + }; +}; + +/** +* Get calculate rendering x position and arrow left by guide block elements +* @param {number} guideBoundLeft - guide block's left +* @param {number} guideBoundRight - guide block's right +* @param {number} layerWidth - popup layer's width +* @param {number} containerLeft - container's left +* @param {number} containerRight - container's right +* @returns {XAndArrowLeft} x and arrowLeft +*/ +ScheduleCreationPopup.prototype._getXAndArrowLeft = function( + guideBoundLeft, + guideBoundRight, + layerWidth, + containerLeft, + containerRight +) { + var guideHorizontalCenter = (guideBoundLeft + guideBoundRight) / 2; + var x = guideHorizontalCenter - (layerWidth / 2); + var ARROW_WIDTH_HALF = 8; + var arrowLeft; + + if (x + layerWidth > containerRight) { + x = guideBoundRight - layerWidth + ARROW_WIDTH_HALF; + arrowLeft = guideHorizontalCenter - x; + } else { + x += ARROW_WIDTH_HALF; } - if (guideHorizontalCenter - x !== layerSize.width / 2) { - arrowLeft = guideHorizontalCenter - x - ARROW_WIDTH_HALF; + if (x < containerLeft) { + x = 0; + arrowLeft = guideHorizontalCenter - containerLeft - ARROW_WIDTH_HALF; + } else { + x = x - containerLeft - ARROW_WIDTH_HALF; } + /** + * @typedef {Object} XAndArrowLeft + * @property {number} x - left position of popup layer + * @property {numbe3er} arrowLeft - relative position of popup arrow, if it is not set, arrow appears on the middle of popup + */ + return { + x: x, + arrowLeft: arrowLeft + }; +}; + +/** + * Calculate rendering position usering guide elements + * @param {{width: {number}, height: {number}}} layerSize - popup layer's width and height + * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} containerBound - width and height of the upper layer, that acts as a border of popup + * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} guideBound - guide element bound data + * @returns {PopupRenderingData} rendering position of popup and popup arrow + */ +ScheduleCreationPopup.prototype._calcRenderingData = function(layerSize, containerBound, guideBound) { + var yPosInfo = this._getYAndArrowDirection( + guideBound.top, + guideBound.bottom, + layerSize.height, + containerBound.top, + containerBound.bottom + ); + var xPosInfo = this._getXAndArrowLeft( + guideBound.left, + guideBound.right, + layerSize.width, + containerBound.left, + containerBound.right + ); + /** * @typedef {Object} PopupRenderingData * @property {number} x - left position @@ -20292,11 +20356,11 @@ ScheduleCreationPopup.prototype._calcRenderingData = function(layerSize, parentS * @property {number} [arrow.position] - relative position of popup arrow, if it is not set, arrow appears on the middle of popup */ return { - x: x, - y: y, + x: xPosInfo.x, + y: yPosInfo.y, arrow: { - direction: arrowDirection, - position: arrowLeft + direction: yPosInfo.arrowDirection, + position: xPosInfo.arrowLeft } }; }; @@ -20380,6 +20444,136 @@ ScheduleCreationPopup.prototype.setCalendars = function(calendars) { this.calendars = calendars || []; }; +/** + * Validate the form + * @param {string} title title of then entered schedule + * @param {TZDate} startDate start date time from range picker + * @param {TZDate} endDate end date time from range picker + * @returns {boolean} Returns false if the form is not valid for submission. + */ +ScheduleCreationPopup.prototype._validateForm = function(title, startDate, endDate) { + if (!title.value) { + return false; + } + + if (!startDate && !endDate) { + return false; + } + + if (datetime.compare(startDate, endDate) === 1) { + return false; + } + + return true; +}; + +/** + * Get range date from range picker + * @param {TZDate} startDate start date time from range picker + * @param {TZDate} endDate end date time from range picker + * @param {boolean} isAllDay whether it is an all-day schedule + * @returns {RangeDate} Returns the start and end time data that is the range date + */ +ScheduleCreationPopup.prototype._getRangeDate = function(startDate, endDate, isAllDay) { + if (isAllDay) { + startDate.setHours(0, 0, 0); + endDate = datetime.isStartOfDay(endDate) ? + datetime.convertStartDayToLastDay(endDate) : + endDate.setHours(23, 59, 59); + } + + /** + * @typedef {object} RangeDate + * @property {TZDate} start start time + * @property {TZDate} end end time + */ + return { + start: new TZDate(startDate), + end: new TZDate(endDate) + }; +}; + +/** + * Request schedule model creation to controller by custom schedules. + * @fires {ScheduleCreationPopup#beforeUpdateSchedule} + * @param {{ + calendarId: {string}, + title: {string}, + location: {string}, + start: {TZDate}, + end: {TZDate}, + isAllDay: {boolean}, + state: {string}, + isPrivate: {boolean} + }} form schedule input form data +*/ +ScheduleCreationPopup.prototype._onClickUpdateSchedule = function(form) { + var changes = common.getScheduleChanges( + this._schedule, + ['calendarId', 'title', 'location', 'start', 'end', 'isAllDay', 'state'], + { + calendarId: form.calendarId, + title: form.title.value, + location: location.value, + start: form.start, + end: form.end, + isAllDay: form.isAllDay, + state: form.state + } + ); + + /** + * @event ScheduleCreationPopup#beforeUpdateSchedule + * @type {object} + * @property {Schedule} schedule - schedule object to be updated + */ + this.fire('beforeUpdateSchedule', { + schedule: util.extend({ + raw: { + class: form.isPrivate ? 'private' : 'public' + } + }, this._schedule), + changes: changes, + start: form.start, + end: form.end, + calendar: this._selectedCal, + triggerEventName: 'click' + }); +}; + +/** + * Request the controller to update the schedule model according to the custom schedule. + * @fires {ScheduleCreationPopup#beforeCreateSchedule} + * @param {{ + calendarId: {string}, + title: {string}, + location: {string}, + start: {TZDate}, + end: {TZDate}, + isAllDay: {boolean}, + state: {string} + }} form schedule input form data + */ +ScheduleCreationPopup.prototype._onClickCreateSchedule = function(form) { + /** + * @event ScheduleCreationPopup#beforeCreateSchedule + * @type {object} + * @property {Schedule} schedule - new schedule instance to be added + */ + this.fire('beforeCreateSchedule', { + calendarId: form.calendarId, + title: form.title.value, + location: location.value, + raw: { + class: form.isPrivate ? 'private' : 'public' + }, + start: form.start, + end: form.end, + isAllDay: form.isAllDay, + state: form.state + }); +}; + module.exports = ScheduleCreationPopup; @@ -20406,7 +20600,6 @@ var config = __webpack_require__(/*! ../../config */ "./src/js/config.js"), domevent = __webpack_require__(/*! ../../common/domevent */ "./src/js/common/domevent.js"), domutil = __webpack_require__(/*! ../../common/domutil */ "./src/js/common/domutil.js"); var tmpl = __webpack_require__(/*! ../template/popup/scheduleDetailPopup.hbs */ "./src/js/view/template/popup/scheduleDetailPopup.hbs"); -var ARROW_WIDTH_HALF = 8; /** * @constructor @@ -20439,7 +20632,7 @@ util.inherit(ScheduleDetailPopup, View); * @param {MouseEvent} mouseDownEvent - mouse event object */ ScheduleDetailPopup.prototype._onMouseDown = function(mouseDownEvent) { - var target = (mouseDownEvent.target || mouseDownEvent.srcElement), + var target = domevent.getEventTarget(mouseDownEvent), popupLayer = domutil.closest(target, config.classname('.floating-layer')); if (popupLayer) { @@ -20466,7 +20659,7 @@ ScheduleDetailPopup.prototype.destroy = function() { * @param {MouseEvent} clickEvent - mouse event object */ ScheduleDetailPopup.prototype._onClick = function(clickEvent) { - var target = (clickEvent.target || clickEvent.srcElement); + var target = domevent.getEventTarget(clickEvent); this._onClickEditSchedule(target); @@ -20540,16 +20733,9 @@ ScheduleDetailPopup.prototype._setPopupPositionAndArrowDirection = function(even width: layer.offsetWidth, height: layer.offsetHeight }; - var windowSize = { - right: window.innerWidth, - bottom: window.innerHeight - }; - var parentRect = this.layer.parent.getBoundingClientRect(); - var parentBounds = { - left: parentRect.left, - top: parentRect.top - }; - var scheduleEl = event.target || event.srcElement; + + var containerBound = this.container.getBoundingClientRect(); + var scheduleEl = domevent.getEventTarget(event); var blockEl = domutil.closest(scheduleEl, config.classname('.time-date-schedule-block')) || domutil.closest(scheduleEl, config.classname('.weekday-schedule')) || scheduleEl; @@ -20558,43 +20744,125 @@ ScheduleDetailPopup.prototype._setPopupPositionAndArrowDirection = function(even this._scheduleEl = blockEl; - pos = this._calcRenderingData(layerSize, windowSize, scheduleBound); - pos.x -= parentBounds.left + 4; - pos.y -= (parentBounds.top + ARROW_WIDTH_HALF); + pos = this._calcRenderingData(layerSize, containerBound, scheduleBound); this.layer.setPosition(pos.x, pos.y); this._setArrowDirection(pos.arrow); }; /** - * Calculate rendering position usering guide elements - * @param {{width: {number}, height: {number}}} layerSize - popup layer's width and height - * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} parentSize - width and height of the upper layer, that acts as a border of popup - * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} guideBound - guide element bound data - * @returns {PopupRenderingData} rendering position of popup and popup arrow + * Get calculate rendering positions of y and arrow top by schedule block elements + * @param {number} scheduleBoundTop - schedule block's top + * @param {number} scheduleBoundBottom - schedule block's bottom + * @param {number} layerHeight - popup layer's height + * @param {number} containerTop - container's top + * @param {number} containerBottom - container's bottom + * @returns {YAndArrowTop} y and arrowTop */ -ScheduleDetailPopup.prototype._calcRenderingData = function(layerSize, parentSize, guideBound) { - var guideVerticalCenter = (guideBound.top + guideBound.bottom) / 2; - var x = guideBound.right; - var y = guideVerticalCenter; - var arrowDirection = 'arrow-left'; - var arrowTop; +ScheduleDetailPopup.prototype._getYAndArrowTop = function( + scheduleBoundTop, + scheduleBoundBottom, + layerHeight, + containerTop, + containerBottom +) { + var ARROW_WIDTH_HALF = 8; + var scheduleVerticalCenter, y, arrowTop; + + scheduleBoundTop = scheduleBoundTop < 0 ? 0 : scheduleBoundTop; + scheduleVerticalCenter = (scheduleBoundTop + scheduleBoundBottom) / 2; + y = scheduleVerticalCenter - (layerHeight / 2); - if (y < 0) { - y = y + (layerSize.height / 2) - guideVerticalCenter; + if (y < containerTop) { + y = 0; + arrowTop = scheduleVerticalCenter - containerTop - ARROW_WIDTH_HALF; + } else if (y + layerHeight > containerBottom) { + y = containerBottom - layerHeight - containerTop; + arrowTop = scheduleVerticalCenter - y - containerTop - ARROW_WIDTH_HALF; + } else { + y -= containerTop; } - if (x > 0 && (x + layerSize.width > parentSize.right)) { - x = guideBound.left - layerSize.width - ARROW_WIDTH_HALF - 3; + if (arrowTop < 0 || arrowTop > layerHeight) { + arrowTop = null; + } + + /** + * @typedef {Object} YAndArrowTop + * @property {number} y - top position of popup layer + * @property {number} [arrowTop] - relative position of popup arrow, if it is not set, arrow appears on the middle of popup + */ + return { + y: y, + arrowTop: arrowTop + }; +}; + +/** + * Get calculate rendering x position and arrow direction by schedule block elements + * @param {number} scheduleBoundLeft - schedule block's left + * @param {number} scheduleBoundRight - schedule block's right + * @param {number} layerWidth - popup layer's width + * @param {number} containerLeft - container's left + * @param {number} containerRight - container's right + * @returns {XAndArrowDirection} x and arrowDirection + */ +ScheduleDetailPopup.prototype._getXAndArrowDirection = function( + scheduleBoundLeft, + scheduleBoundRight, + layerWidth, + containerLeft, + containerRight +) { + var arrowDirection = 'arrow-left'; + var x = scheduleBoundRight; + var MARGIN = 4; + + if (x + layerWidth > containerRight) { arrowDirection = 'arrow-right'; + x = scheduleBoundLeft - layerWidth - MARGIN; + } else { + x += MARGIN; } - if (x < 0) { + if (x < containerLeft) { x = 0; + } else { + x -= containerLeft; } - if (guideBound.right > x + layerSize.width) { - arrowDirection = 'arrow-right'; - } + /** + * @typedef {Object} XAndArrowDirection + * @property {number} x - left position of popup layer + * @property {string} arrowDirection - direction of popup arrow + */ + return { + x: x, + arrowDirection: arrowDirection + }; +}; + +/** + * Calculate rendering position usering guide elements + * @param {{width: {number}, height: {number}}} layerSize - popup layer's width and height + * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} containerBound - width and height of the upper layer, that acts as a border of popup + * @param {{top: {number}, left: {number}, right: {number}, bottom: {number}}} scheduleBound - guide element bound data + * @returns {PopupRenderingData} rendering position of popup and popup arrow + */ +ScheduleDetailPopup.prototype._calcRenderingData = function(layerSize, containerBound, scheduleBound) { + var yPosInfo = this._getYAndArrowTop( + scheduleBound.top, + scheduleBound.bottom, + layerSize.height, + containerBound.top, + containerBound.bottom + ); + var xPosInfo = this._getXAndArrowDirection( + scheduleBound.left, + scheduleBound.right, + layerSize.width, + containerBound.left, + containerBound.right + ); /** * @typedef {Object} PopupRenderingData @@ -20604,11 +20872,11 @@ ScheduleDetailPopup.prototype._calcRenderingData = function(layerSize, parentSiz * @property {number} [arrow.position] - relative position of popup arrow, if it is not set, arrow appears on the middle of popup */ return { - x: x + ARROW_WIDTH_HALF, - y: y - (layerSize.height / 2) + ARROW_WIDTH_HALF, + x: xPosInfo.x, + y: yPosInfo.y, arrow: { - direction: arrowDirection, - position: arrowTop + direction: xPosInfo.arrowDirection, + position: yPosInfo.arrowTop } }; }; @@ -20620,7 +20888,7 @@ ScheduleDetailPopup.prototype._calcRenderingData = function(layerSize, parentSiz ScheduleDetailPopup.prototype._setArrowDirection = function(arrow) { var direction = arrow.direction || 'arrow-left'; var arrowEl = domutil.find(config.classname('.popup-arrow'), this.layer.container); - var borderElement = domutil.find(config.classname('.popup-arrow-border', arrowEl)); + var borderElement = domutil.find(config.classname('.popup-arrow-border'), arrowEl); if (direction !== config.classname('arrow-left')) { domutil.removeClass(arrowEl, config.classname('arrow-left')); @@ -22116,21 +22384,21 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co }; return "
  • \n \n " - + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":14,"column":60},"end":{"line":14,"column":68}}}) : helper))) + + alias4(((helper = (helper = lookupProperty(helpers,"name") || (depth0 != null ? lookupProperty(depth0,"name") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"name","hash":{},"data":data,"loc":{"start":{"line":13,"column":60},"end":{"line":13,"column":68}}}) : helper))) + "\n
  • \n"; },"5":function(container,depth0,helpers,partials,data) { var helper, lookupProperty = container.lookupProperty || function(parent, propertyName) { @@ -22141,7 +22409,7 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co }; return " " - + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"CSS_PREFIX") || (depth0 != null ? lookupProperty(depth0,"CSS_PREFIX") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"CSS_PREFIX","hash":{},"data":data,"loc":{"start":{"line":24,"column":135},"end":{"line":24,"column":149}}}) : helper))) + + container.escapeExpression(((helper = (helper = lookupProperty(helpers,"CSS_PREFIX") || (depth0 != null ? lookupProperty(depth0,"CSS_PREFIX") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"CSS_PREFIX","hash":{},"data":data,"loc":{"start":{"line":23,"column":135},"end":{"line":23,"column":149}}}) : helper))) + "public"; },"7":function(container,depth0,helpers,partials,data) { return " checked"; @@ -22153,7 +22421,7 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co return undefined }; - return container.escapeExpression(((helper = (helper = lookupProperty(helpers,"state") || (depth0 != null ? lookupProperty(depth0,"state") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"state","hash":{},"data":data,"loc":{"start":{"line":55,"column":99},"end":{"line":55,"column":108}}}) : helper))); + return container.escapeExpression(((helper = (helper = lookupProperty(helpers,"state") || (depth0 != null ? lookupProperty(depth0,"state") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"state","hash":{},"data":data,"loc":{"start":{"line":54,"column":99},"end":{"line":54,"column":108}}}) : helper))); },"11":function(container,depth0,helpers,partials,data) { var stack1, helper, lookupProperty = container.lookupProperty || function(parent, propertyName) { if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { @@ -22162,7 +22430,7 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co return undefined }; - return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupStateBusy-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupStateBusy-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupStateBusy-tmpl","hash":{},"data":data,"loc":{"start":{"line":55,"column":116},"end":{"line":55,"column":141}}}) : helper))) != null ? stack1 : ""); + return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupStateBusy-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupStateBusy-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupStateBusy-tmpl","hash":{},"data":data,"loc":{"start":{"line":54,"column":116},"end":{"line":54,"column":141}}}) : helper))) != null ? stack1 : ""); },"13":function(container,depth0,helpers,partials,data) { var stack1, helper, lookupProperty = container.lookupProperty || function(parent, propertyName) { if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { @@ -22171,7 +22439,7 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co return undefined }; - return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupUpdate-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupUpdate-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupUpdate-tmpl","hash":{},"data":data,"loc":{"start":{"line":70,"column":163},"end":{"line":70,"column":185}}}) : helper))) != null ? stack1 : ""); + return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupUpdate-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupUpdate-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupUpdate-tmpl","hash":{},"data":data,"loc":{"start":{"line":69,"column":163},"end":{"line":69,"column":185}}}) : helper))) != null ? stack1 : ""); },"15":function(container,depth0,helpers,partials,data) { var stack1, helper, lookupProperty = container.lookupProperty || function(parent, propertyName) { if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { @@ -22180,7 +22448,7 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co return undefined }; - return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupSave-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupSave-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupSave-tmpl","hash":{},"data":data,"loc":{"start":{"line":70,"column":193},"end":{"line":70,"column":213}}}) : helper))) != null ? stack1 : ""); + return ((stack1 = ((helper = (helper = lookupProperty(helpers,"popupSave-tmpl") || (depth0 != null ? lookupProperty(depth0,"popupSave-tmpl") : depth0)) != null ? helper : container.hooks.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"popupSave-tmpl","hash":{},"data":data,"loc":{"start":{"line":69,"column":193},"end":{"line":69,"column":213}}}) : helper))) != null ? stack1 : ""); },"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) { var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=container.hooks.helperMissing, alias3="function", alias4=container.escapeExpression, alias5=container.lambda, lookupProperty = container.lookupProperty || function(parent, propertyName) { if (Object.prototype.hasOwnProperty.call(parent, propertyName)) { @@ -22225,198 +22493,198 @@ module.exports = (Handlebars['default'] || Handlebars).template({"1":function(co + alias4(((helper = (helper = lookupProperty(helpers,"CSS_PREFIX") || (depth0 != null ? lookupProperty(depth0,"CSS_PREFIX") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"CSS_PREFIX","hash":{},"data":data,"loc":{"start":{"line":7,"column":29},"end":{"line":7,"column":43}}}) : helper))) + "icon " + alias4(((helper = (helper = lookupProperty(helpers,"CSS_PREFIX") || (depth0 != null ? lookupProperty(depth0,"CSS_PREFIX") : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"CSS_PREFIX","hash":{},"data":data,"loc":{"start":{"line":7,"column":48},"end":{"line":7,"column":62}}}) : helper))) - + "dropdown-arrow\">\n \n \n