From e063626bdc8d381563710c66329b4e9474b464ee Mon Sep 17 00:00:00 2001 From: rolux Date: Mon, 15 Aug 2011 14:18:14 +0200 Subject: [PATCH] Ox.Dialog rewrite --- source/Ox.UI/css/Ox.UI.css | 124 +++ source/Ox.UI/js/Core/Ox.Element.js | 1 + source/Ox.UI/js/Core/Ox.Focus.js | 2 +- source/Ox.UI/js/Video/Ox.VideoPlayer.js | 3 +- source/Ox.UI/js/Window/Ox.Dialog.js | 915 +++++++++++++------- source/Ox.UI/js/Window/Ox.Window.js | 68 -- source/Ox.UI/themes/classic/css/classic.css | 7 + source/Ox.UI/themes/modern/css/modern.css | 8 + source/Ox.js | 47 +- tests/tests.js | 100 +-- 10 files changed, 762 insertions(+), 513 deletions(-) delete mode 100644 source/Ox.UI/js/Window/Ox.Window.js diff --git a/source/Ox.UI/css/Ox.UI.css b/source/Ox.UI/css/Ox.UI.css index 6ec83b5c..ff53382d 100644 --- a/source/Ox.UI/css/Ox.UI.css +++ b/source/Ox.UI/css/Ox.UI.css @@ -296,6 +296,130 @@ Dialog cursor: se-resize; } +.OxWindow { + position: absolute; + border-radius: 8px; + z-index: 11; +} + +.OxWindow > .OxTitlebar { + position: absolute; + height: 24px; + text-align: center; + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +.OxWindow > .OxTitlebar > .OxButton { + position: absolute; +} + +.OxWindow > .OxTitlebar > .OxTitle { + margin-top: 3px; + font-size: 13px; + font-weight: bold; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.OxWindow > .OxContent { + position: absolute; + left: 0; + top: 24px; + right: 0; + overflow: auto; +} + +.OxWindow > .OxButtonsbar { + position: absolute; + bottom: 0; + height: 24px; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + cursor: move; +} +.OxWindow > .OxButtonsbar > .OxButtonsLeft { + margin-left: 4px; + float: left; +} +.OxWindow > .OxButtonsbar > .OxButtonsRight { + margin-right: 4px; + float: right; +} +.OxWindow > .OxButtonsbar .OxButton { + margin: 4px 2px 4px 2px; +} + +.OxWindow > .OxResize { + position: absolute; +} +.OxWindow > .OxResizeTopLeft { + left: -2px; + top: -2px; + width: 10px; + height: 10px; + cursor: nwse-resize; +} +.OxWindow > .OxResizeTop { + left: 8px; + top: -2px; + right: 8px; + height: 5px; + cursor: ns-resize; +} +.OxWindow > .OxResizeTopRight { + right: -2px; + top: -2px; + width: 10px; + height: 10px; + cursor: nesw-resize; +} +.OxWindow > .OxResizeLeft { + left: -2px; + top: 8px; + width: 5px; + bottom: 8px; + cursor: ew-resize; +} +.OxWindow > .OxResizeRight { + right: -2px; + top: 8px; + width: 5px; + bottom: 8px; + cursor: ew-resize; +} +.OxWindow > .OxResizeBottomLeft { + left: -2px; + bottom: -2px; + width: 10px; + height: 10px; + cursor: nesw-resize; +} +.OxWindow > .OxResizeBottom { + left: 8px; + bottom: -2px; + right: 8px; + height: 5px; + cursor: ns-resize; +} +.OxWindow > .OxResizeBottomRight { + right: -2px; + bottom: -2px; + width: 10px; + height: 10px; + cursor: nwse-resize; +} + +.OxDialogBox { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + overflow: hidden; +} + /* ================================================================================ Document diff --git a/source/Ox.UI/js/Core/Ox.Element.js b/source/Ox.UI/js/Core/Ox.Element.js index 34a4aee1..110fa76b 100644 --- a/source/Ox.UI/js/Core/Ox.Element.js +++ b/source/Ox.UI/js/Core/Ox.Element.js @@ -318,6 +318,7 @@ Ox.Element = function(options, self) { that.loseFocus(); delete self.$eventHandler; that.remove(); + // fixme: ok to comment out the following line? delete Ox.UI.elements[that.id]; return that; }; diff --git a/source/Ox.UI/js/Core/Ox.Focus.js b/source/Ox.UI/js/Core/Ox.Focus.js index e9e3dd5c..37a6f450 100644 --- a/source/Ox.UI/js/Core/Ox.Focus.js +++ b/source/Ox.UI/js/Core/Ox.Focus.js @@ -38,8 +38,8 @@ Ox.Focus = function() { index > -1 && stack.splice(index, 1); stack.push(id); $('.OxFocus').removeClass('OxFocus'); // fixme: see above - Ox.UI.elements[id].addClass('OxFocus'); Ox.print('focus', id, stack); + Ox.UI.elements[id].addClass('OxFocus'); } }, /*@ diff --git a/source/Ox.UI/js/Video/Ox.VideoPlayer.js b/source/Ox.UI/js/Video/Ox.VideoPlayer.js index d2d7fff6..d7f27309 100644 --- a/source/Ox.UI/js/Video/Ox.VideoPlayer.js +++ b/source/Ox.UI/js/Video/Ox.VideoPlayer.js @@ -9,7 +9,8 @@ Ox.VideoPlayer Generic Video Player data <[o]> Annotation data in In point (sec) out Out point (sec) - text Text + text Text + # fixme: documentation is wrong controls <[[s]]|[[][]]> Controls, first top, then bottom, from left to right Can be 'fullscreen', 'scale', 'title', 'find', 'menu', 'play', 'playInToOut', 'mute', 'volume', 'size', 'timeline', 'space', diff --git a/source/Ox.UI/js/Window/Ox.Dialog.js b/source/Ox.UI/js/Window/Ox.Dialog.js index afb9a4f7..21196f0d 100644 --- a/source/Ox.UI/js/Window/Ox.Dialog.js +++ b/source/Ox.UI/js/Window/Ox.Dialog.js @@ -1,401 +1,678 @@ // vim: et:ts=4:sw=4:sts=4:ft=javascript - /*@ -Ox.Dialog Dialog Object - () -> Dialog Object - (options) -> Dialog Object - (options, self) -> Dialog Object +Ox.Dialog Window object + () -> Window object + (options) -> Window object + (options, self) -> Window object options Options object - self shared private variable + draggable is window draggable + fullscreenable fixme: silly name + height height + resizeable resizeable + scaleable sccaleable + width width + self Shared private variable @*/ Ox.Dialog = function(options, self) { - // fixme: dialog should be derived from a generic draggable - // fixme: buttons should have a close attribute, or the dialog a close id + // fixme: use controlsTop/controlsBottom options, like in VideoPlayer (????) + self = self || {}; var that = Ox.Element({}, self) .defaults({ - title: '', buttons: [], + controlsBottom: [], + controlsTop: [], + closeButton: false, content: null, + fixedCenter: false, + fixedSize: false, + fixedRatio: false, focus: true, - height: 216, - keys: {}, - minHeight: 144, - minWidth: 256, - movable: true, - padding: 16, - resizable: true, - width: 384 + height: 200, + maxHeight: Infinity, + maximizeButton: false, + maxWidth: Infinity, + minHeight: 64, + minWidth: 128, + title: '', + width: 400 }) .options(options || {}) - .addClass('OxDialog') - .bindEvent({ - key_enter: function() { - keypress('enter'); - }, - key_escape: function() { - //Ox.print('KEY ESCAPE') - keypress('escape'); - } - }); + .addClass('OxWindow') + .hide() + .appendTo(Ox.UI.$body); - $.extend(self, { - initialWidth: self.options.width, - initialHeight: self.options.height + self.hasButtons = !!self.options.buttons.length; + self.barsHeight = 24 + 24 * self.hasButtons; + self.initialHeight = self.options.height; + self.initialWidth = self.options.width; + self.initialMaxHeight = self.options.maxHeight; + self.initialMaxWidth = self.options.maxWidth; + self.titleMargin = 8 + (self.options.closeButton ? 20 : 0) + + (self.options.maximizeButton ? 20 : 0); + + if (self.options.focus) { + self.$layer = Ox.Element() // fixme: Layer widget that would handle click? + .addClass('OxLayer') + .mousedown(mousedownLayer) + .mouseup(mouseupLayer) + .hide() + .appendTo(Ox.UI.$body); + } + + self.$box = $('
') + .addClass('OxDialogBox') + .css({zIndex: 11}); + + self.$titlebar = Ox.Bar({ + size: 24 + }) + .addClass('OxTitlebar') + .appendTo(that); + + if (self.options.closeButton) { + self.$closeButton = Ox.Button({ + title: 'close', + type: 'image' + }) + .css({ + top: '4px', + left: '4px' + }) + .bindEvent({ + click: function() { + that.close(); + } + }) + .appendTo(self.$titlebar); + } + + if (self.options.maximizeButton) { + self.$maximizeButton = Ox.Button({ + title: [ + {id: 'add', title: 'add'}, + {id: 'remove', title: 'remove'} + ], + type: 'image' + }) + .css({ + top: '4px', + left: '24px' + }) + .bindEvent({ + click: maximize + }) + .appendTo(self.$titlebar); + } + + self.$title = Ox.Element() + .addClass('OxTitle') + .css({ + marginLeft: self.titleMargin + 'px', + marginRight: self.titleMargin + 'px' + }) + .html(self.options.title) + .appendTo(self.$titlebar); + + setContent(); + + if (self.hasButtons) { + self.$buttonsbar = Ox.Bar({ + size: 24 + }) + .addClass('OxButtonsbar') + .appendTo(that); + self.$buttonsLeft = $('
') + .addClass('OxButtonsLeft') + .appendTo(self.$buttonsbar.$element); + self.$buttonsRight = $('
') + .addClass('OxButtonsRight') + .appendTo(self.$buttonsbar.$element); + var buttonsLeft, + buttonsRight, + index = Ox.map(self.options.buttons, function(v, i) { + return Ox.isEmpty(v) ? i : null; + })[0]; + if (index) { + buttonsLeft = Ox.sub(self.options.buttons, 0, index); + buttonsRight = Ox.sub(self.options.buttons, index + 1); + } else { + buttonsLeft = []; + buttonsRight = self.options.buttons; + } + buttonsLeft.forEach(function($button) { + $button.addClass('OxLeft').appendTo(self.$buttonsLeft); + }); + buttonsRight.forEach(function($button) { + $button.addClass('OxRight').appendTo(self.$buttonsRight); + }); + } + + if (!self.options.fixedCenter) { + self.$titlebar.css({ + cursor: 'move' + }) + .bindEvent({ + doubleclick: function() { + !self.centered && center(true); + }, + dragstart: dragstart, + drag: drag, + dragend: dragend, + }); + self.hasButtons && self.$buttonsbar.css({ + cursor: 'move' + }) + .bindEvent({ + doubleclick: function() { + !self.centered && center(true); + }, + dragstart: dragstart, + drag: drag, + dragend: dragend + }); + } + + !self.options.fixedSize && [ + 'TopLeft', 'Top', 'TopRight', 'Left', 'Right', 'BottomLeft', 'Bottom', 'BottomRight' + ].forEach(function(edge) { + Ox.Element() + .addClass('OxResize OxResize' + edge) + .bindEvent({ + doubleclick: function() { + reset(true); + }, + dragstart: resizestart, + drag: resize, + dragend: resizeend + }) + .appendTo(that); }); - that.$titlebar = Ox.Bar({ - size: 'medium' - }) - .addClass('OxTitleBar') - .appendTo(that); - self.options.movable && that.$titlebar - .dblclick(center) - .bindEvent({ - dragstart: dragstart, - drag: drag - }); - - that.$title = Ox.Element() - .addClass('OxTitle') - .html(self.options.title) - .appendTo(that.$titlebar); - - that.$content = Ox.Element() - .addClass('OxContent') - .css({ - padding: self.options.padding + 'px', - overflow: 'auto' - }) - .append(self.options.content) - .appendTo(that); - - that.$buttonsbar = Ox.Bar({}) - .addClass('OxButtonsBar') - .appendTo(that); - loadButtons(); - - //that.$buttons[0].focus(); - - that.$layer = Ox.Element() // fixme: Layer widget that would handle click? - .addClass('OxLayer') - .mousedown(mousedownLayer) - .mouseup(mouseupLayer); - - function center() { - var documentHeight = Ox.UI.$document.height(); - that.css({ - left: 0, - top: Math.max(parseInt(-documentHeight / 10), self.options.height - documentHeight + 40) + 'px', - right: 0, - bottom: 0, - margin: 'auto' + function center(animate) { + var ms = animate ? 100 : 0; + self.centered && decenter(); + that.animate({ + left: Math.round( + (window.innerWidth - self.options.width) / 2 + ) + 'px', + top: Math.round( + (window.innerHeight - self.options.height - self.barsHeight) * 0.4 + ) + 'px', + width: self.options.width + 'px', + height: self.options.height + self.barsHeight + 'px' + }, ms, function() { + that.css({ + left: 0, + top: 0, + right: 0, + bottom: Math.round( + (window.innerHeight - self.options.height - self.barsHeight) * 0.2 + ) + 'px', + margin: 'auto' + }); + self.centered = true; + Ox.isFunction(animate) && animate(); }); } - function dragstart(event, e) { - self.drag = { - bodyWidth: Ox.UI.$body.width(), - bodyHeight: Ox.UI.$document.height(), - elementWidth: that.width(), - offset: that.offset(), - x: e.clientX, - y: e.clientY - }; - that.css({ - margin: 0 - }); + function decenter() { + var offset = that.offset(); + if (self.centered) { + that.css({ + left: offset.left + 'px', + top: offset.top + 'px', + margin: 0 + }); + self.centered = false; + } } - function drag(event, e) { + function dragstart(event) { + var offset; + if (!$(event.target).is('.OxButton')) { + offset = that.offset(); + self.drag = { + left: offset.left, + top: offset.top, + x: event.clientX, + y: event.clientY + }; + decenter(); + that.wrap(self.$box); + } + } + + function drag(event) { + Ox.print(document.body.scrollTop, '...') var left = Ox.limit( - self.drag.offset.left - self.drag.x + e.clientX, - 24 - self.drag.elementWidth, self.drag.bodyWidth - 24 - //0, self.drag.documentWidth - self.drag.elementWidth + self.drag.left - self.drag.x + event.clientX, + self.minLeft, self.maxLeft ), top = Ox.limit( - self.drag.offset.top - self.drag.y + e.clientY, - 24, self.drag.bodyHeight - 24 - //24, self.drag.documentHeight - self.drag.elementHeight + self.drag.top - self.drag.y + event.clientY, + self.minTop, self.maxTop ); - that.css({ - left: left + 'px', - top: top + 'px' + setCSS({ + left: left, + top: top }); } - function dragstartResize(event, e) { - self.drag = { - documentWidth: Ox.UI.$document.width(), - documentHeight: Ox.UI.$document.height(), - elementWidth: that.width(), - elementHeight: that.height(), - offset: that.offset(), - x: e.clientX, - y: e.clientY - }; - $.extend(self.drag, { - ratio: self.drag.elementWidth / self.drag.elementHeight - }); - that.css({ - left: self.drag.offset.left, - top: self.drag.offset.top, - margin: 0 - }); + function dragend() { + that.unwrap(); } - function dragResize(event, e) { - if (!e.shiftKey) { - self.drag.ratio = self.options.width / self.options.height; + function maximize() { + var offset = that.offset(); + decenter(); + if (!self.maximized) { + self.originalLeft = offset.left; + self.originalTop = offset.top; + self.originalWidth = self.options.width; + self.originalHeight = self.options.height; } - self.options.width = Ox.limit( - self.drag.elementWidth - self.drag.x + e.clientX, - self.options.minWidth, - Math.min( - self.drag.documentWidth, - self.drag.documentWidth - self.drag.offset.left - ) - ); - self.options.height = Ox.limit( - self.drag.elementHeight - self.drag.y + e.clientY, - self.options.minHeight, - Math.min( - self.drag.documentHeight, - self.drag.documentHeight - self.drag.offset.top - ) - ); - if (e.shiftKey) { - self.options.height = Ox.limit( - self.options.width / self.drag.ratio, - self.options.minHeight, - Math.min( - self.drag.documentHeight, - self.drag.documentHeight - self.drag.offset.top - ) - ); - self.options.width = self.options.height * self.drag.ratio; - } - that.width(self.options.width); - that.height(self.options.height); - that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically - } - - function dragendResize(event, e) { - triggerResizeEvent(); - } - - function getButtonById(id) { - var ret = null; - //Ox.print('that.$buttons', that.$buttons, id) - Ox.forEach(that.$buttons, function(button) { - if (button.options('id') == id) { - ret = button; - return false; - } - }); - return ret; - } - - function keypress(key) { - var id = self.options.keys[key]; - //Ox.print('X', key, self.options.keys) - id && getButtonById(id).$element.trigger('click'); - } - - function loadButtons() { - /*Ox.print('loadButtons', $.map(self.options.buttons, function(v) { - return v; - }));*/ - if (that.$buttons) { - that.$buttons.forEach(function($button) { - $button.removeElement(); - }); - that.$resize.removeElement(); - // that.$buttonsbar.empty(); - } - that.$buttons = []; - if (!Ox.isArray(self.options.buttons[0])) { - self.options.buttons = [[], self.options.buttons]; - } - self.options.buttons[0].forEach(function(button, i) { - that.$buttons[i] = button - .addClass('OxLeft') - .appendTo(that.$buttonsbar); - }); - if (self.options.resizable) { - that.$resize = Ox.Element() - .addClass('OxResize') - .dblclick(reset) - .bindEvent({ - dragstart: dragstartResize, - drag: dragResize, - dragend: dragendResize - }) - .appendTo(that.$buttonsbar); - } - self.options.buttons[1].reverse().forEach(function(button) { - that.$buttons[that.$buttons.length] = button - .addClass('OxRight') - .appendTo(that.$buttonsbar); - }); + setCSS(self.maximized ? { + left: self.originalLeft, + top: self.originalTop, + width: self.originalWidth, + height: self.originalHeight + } : { + left: Math.round((window.innerWidth - self.options.maxWidth) / 2), + top: Math.round((window.innerHeight - self.options.maxHeight - self.barsHeight) / 2), + width: self.options.maxWidth, + height: self.options.maxHeight + }, true); + self.maximized = !self.maximized; } function mousedownLayer() { - that.$layer.stop().animate({ + self.$layer.stop().animate({ opacity: 0.5 }, 0); } function mouseupLayer() { - that.$layer.stop().animate({ + self.$layer.stop().animate({ opacity: 0 }, 0); } - function reset() { - $.extend(self.options, { - height: self.initialHeight, - width: self.initialWidth - }); - that/*.css({ - left: Math.max(that.offset().left, 24 - that.width()) - })*/ - .width(self.options.width) - .height(self.options.height); - that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically - triggerResizeEvent(); + function reset(animate) { + var offset, left, top; + if (!self.centered) { + offset = that.offset(); + left = Ox.limit( + offset.left + Math.round((self.options.width - self.initialWidth) / 2), + self.minLeft, self.maxLeft + ); + top = Ox.limit( + offset.top + Math.round((self.options.height - self.initialHeight) / 2), + self.minTop, self.maxTop + ); + } + setCSS(Ox.extend({ + width: self.initialWidth, + height: self.initialHeight + }, self.centered ? {} : { + left: left, + top: top + }), animate); } - function triggerResizeEvent() { + function resizestart(event) { + var edge = event.target.className.substr(17).toLowerCase(), + offset = that.offset(); + self.drag = { + edge: edge, + height: self.options.height, + isLeft: edge.indexOf('left') > -1, + isTop: edge.indexOf('top') > -1, + isRight: edge.indexOf('right') > -1, + isBottom: edge.indexOf('bottom') > -1, + left: offset.left, + top: offset.top, + width: self.options.width + }; + decenter(); + if (self.maximized) { + self.$maximizeButton.toggleTitle(); + self.maximized = false; + } + that.wrap(self.$box); + } + + function resize(event) { + var ratio = self.drag.width / self.drag.height, horizontal, vertical; + if (!self.drag.fixedRatio && event.shiftKey) { + self.drag.centerX = Math.round(self.drag.left + self.drag.width / 2); + self.drag.centerY = Math.round(self.drag.top + self.drag.height / 2); + } + self.drag.fixedCenter = self.options.fixedCenter || event.altKey; + self.drag.fixedRatio = self.options.fixedRatio || event.shiftKey; + horizontal = self.drag.edge == 'left' || self.drag.edge == 'right' + || ratio >= 1 || !self.drag.fixedRatio; + vertical = self.drag.edge == 'top' || self.drag.edge == 'bottom' + || ratio < 1 || !self.drag.fixedRatio; + if (self.drag.isLeft && horizontal) { + self.options.width = Ox.limit( + self.options.width + ( + self.drag.left - Math.min(event.clientX, self.maxLeft) + ) * (self.drag.fixedCenter ? 2 : 1), + self.options.minWidth, + self.options.maxWidth + ); + if (self.drag.fixedRatio) { + self.options.height = Ox.limit( + Math.round(self.options.width / ratio), + self.options.minHeight, + self.options.maxHeight + ); + self.options.width = Math.round(self.options.height * ratio); + } + setCSS(Ox.extend({ + left: self.drag.left + ( + self.drag.width - self.options.width + ) / (self.drag.fixedCenter ? 2 : 1), + width: self.options.width, + }, self.drag.fixedRatio ? { + top: Math.max(Math.round( + self.drag.edge == 'topleft' + ? self.drag.top + self.drag.height - self.options.height + : self.drag.edge == 'bottomleft' + ? self.drag.top + : self.drag.centerY - self.options.height / 2 + ), self.minTop), + height: self.options.height + } : {})); + } + if (self.drag.isTop && vertical) { + self.options.height = Ox.limit( + self.options.height + ( + self.drag.top - Ox.limit(event.clientY, self.minTop, self.maxTop) + ) * (self.drag.fixedCenter ? 2 : 1), + self.options.minHeight, + self.options.maxHeight + ); + if (self.drag.fixedRatio) { + self.options.width = Ox.limit( + Math.round(self.options.height * ratio), + self.options.minWidth, + self.options.maxWidth + ); + self.options.height = Math.round(self.options.width / ratio); + } + setCSS(Ox.extend({ + top: Math.max(self.drag.top + ( + self.drag.height - self.options.height + ) / (self.drag.fixedCenter ? 2 : 1), self.minTop), + height: self.options.height, + }, (self.drag.fixedRatio) ? { + left: Math.round( + self.drag.edge == 'topleft' + ? self.drag.left + self.drag.width - self.options.width + : self.drag.edge == 'topright' + ? self.drag.left + : self.drag.centerX - self.options.width / 2 + ), + width: self.options.width + } : {})); + } + if (self.drag.isRight && horizontal) { + self.options.width = Ox.limit( + self.options.width + ( + Math.max(event.clientX, 24) - self.drag.left - self.drag.width + ) * (self.drag.fixedCenter ? 2 : 1), + self.options.minWidth, + self.options.maxWidth + ); + if (self.drag.fixedRatio) { + self.options.height = Ox.limit( + Math.round(self.options.width / ratio), + self.options.minHeight, + self.options.maxHeight + ); + self.options.width = Math.round(self.options.height * ratio); + } + setCSS(Ox.extend({ + width: self.options.width, + }, self.drag.fixedCenter ? { + left: self.drag.left + ( + self.drag.width - self.options.width + ) / 2 + } : {}, self.drag.fixedRatio ? { + top: Math.max(Math.round( + self.drag.edge == 'topright' + ? self.drag.top + self.drag.height - self.options.height + : self.drag.edge == 'bottomright' + ? self.drag.top + : self.drag.centerY - self.options.height / 2 + ), self.minTop), + height: self.options.height + } : {})); + } + if (self.drag.isBottom && vertical) { + self.options.height = Ox.limit( + self.options.height + ( + Math.max(event.clientY, 24) - self.drag.top - self.drag.height - self.barsHeight + ) * (self.drag.fixedCenter ? 2 : 1), + self.options.minHeight, + self.options.maxHeight + ); + if (self.drag.fixedRatio) { + self.options.width = Ox.limit( + Math.round(self.options.height * ratio), + self.options.minWidth, + self.options.maxWidth + ); + self.options.height = Math.round(self.options.width / ratio); + } + setCSS(Ox.extend({ + height: self.options.height, + }, self.drag.fixedCenter ? { + top: Math.max(self.drag.top + ( + self.drag.height - self.options.height + ) / 2, self.minTop) + } : {}, self.drag.fixedRatio && self.drag.edge == 'bottom' ? { + left: Math.round( + self.drag.edge == 'bottomleft' + ? self.drag.left + self.drag.width - self.options.width + : self.drag.edge == 'bottomright' + ? self.drag.left + : self.drag.centerX - self.options.width / 2 + ), + width: self.options.width + } : {})); + } + var offset = that.offset(); + self.drag.left = offset.left; + self.drag.top = offset.top; + self.drag.width = self.options.width; + self.drag.height = self.options.height; + self.drag.minLeft = 24 - self.options.width; + self.drag.minTop = self.hasButtons ? 24 - self.options.height - self.barsHeight : 0; that.triggerEvent('resize', { width: self.options.width, height: self.options.height }); } - self.setOption = function(key, value) { - if (key == 'buttons') { - loadButtons(); - /* - that.$buttonsbar.children().animate({ + function resizeend() { + that.unwrap(); + } + + function setContent() { + var animate = !!self.$content + isImage = !self.options.content.ox && self.options.content.is('img'); + if (animate) { + self.$content.animate({ opacity: 0 }, 100, function() { - loadButtons(); - that.$buttonsbar.children().animate({ - opacity: 1 - }, 100); + $(this).remove() // fixme: removeElement? }); - */ - } else if (key == 'content') { - that.$content.html(value); - } else if (key == 'height' || key == 'width') { - that.animate({ - height: self.options.height + 'px', - width: self.options.width + 'px' - }, 100); - that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically - } else if (key == 'title') { - that.$title.animate({ + self.options.content.css({ opacity: 0 - }, 100, function() { - that.$title.html(value).animate({ - opacity: 1 - }, 100); }); } - }; + self.$content = (isImage ? self.options.content : Ox.Element()) + .addClass('OxContent') + .css(self.hasButtons ? { + bottom: '24px' + } : { + bottom: 0, + borderBottomLeftRadius: '8px', + borderBottomRightRadius: '8px' + }) + .appendTo(that.$element); + !isImage && self.$content.append( + self.options.content.css(self.hasButtons ? {} : { + borderBottomLeftRadius: '8px', + borderBottomRightRadius: '8px' + }) + ); + animate && self.options.content.animate({ + opacity: 1 + }, 100); + } - that.center = function() { - + function setCSS(css, animate) { + var ms = animate ? 100 : 0, + offset = that.offset(), + triggerEvent = (css.width && css.width != self.options.width) + || (css.height && css.height != self.options.height) + css = Ox.extend({ + left: offset.left, + top: offset.top, + width: self.options.width, + height: self.options.height + }, css); + that.animate({ + left: css.left + 'px', + top: css.top + 'px', + width: css.width + 'px', + height: css.height + self.barsHeight + 'px' + }, ms, function() { + self.options.width = css.width; + self.options.height = css.height; + self.minLeft = 24 - self.options.width; + self.minTop = self.hasButtons ? 24 - self.options.height - self.barsHeight : 0; + triggerEvent && that.triggerEvent('resize', { + width: self.options.width, + height: self.options.height + }); + Ox.isFunction(animate) && animate(); + }); + } + + function setMinAndMax() { + var ratio, maxRatio, minRatio; + self.maxLeft = window.innerWidth - 24; + self.maxTop = window.innerHeight - 24; + self.minLeft = 24 - self.options.width; + self.minTop = self.hasButtons ? 24 - self.options.height : 0; + self.options.maxHeight = Ox.limit(self.initialMaxHeight, 64, window.innerHeight - self.barsHeight); + self.options.maxWidth = Ox.limit(self.initialMaxWidth, 128, window.innerWidth); + if (self.options.fixedRatio) { + ratio = self.options.width / self.options.height; + maxRatio = self.options.maxWidth / self.options.maxHeight; + minRatio = self.options.minWidth / self.options.minHeight; + if (maxRatio > ratio) { + self.options.maxWidth = Math.round(self.options.maxHeight * ratio); + } else if (maxRatio < ratio) { + self.options.maxHeight = Math.round(self.options.maxWidth / ratio); + } + if (minRatio > ratio) { + self.options.minWidth = Math.round(self.options.minHeight * ratio); + } else if (minRatio < ratio) { + self.options.minHeight = Math.round(self.options.minWidth / ratio); + } + } + Ox.print('sMM', self, window.innerHeight, maxRatio) + } + + self.setOption = function(key, value) { + if (key == 'content') { + setContent(); + } else if (key == 'title') { + self.$title.animate({ + opacity: 0 + }, 50, function() { + self.$title.html(value).animate({ + opacity: 1 + }, 50); + }); + } }; that.close = function(callback) { - callback = callback || function() {}; that.animate({ opacity: 0 - }, 200, function() { - that.$buttons.forEach(function($button) { - $button.removeElement(); - }); - if (self.options.focus) { - that.loseFocus(); - that.$layer.removeElement(); - } - that.removeElement(); - callback(); + }, 100, function() { + //that.removeElement(); + that.hide(); + callback && callback(); }); - Ox.UI.$window.unbind('mouseup', mouseupLayer); + if (self.maximized) { + self.$maximizeButton.toggleTitle(); + self.maximized = false; + } + if (self.options.focus) { + self.$layer.hide(); + that.loseFocus(); + Ox.UI.$window.unbind({mouseup: mouseupLayer}); + } return that; }; - that.content = function($element) { - that.$content.empty().append($element); - return that; - }; - - that.disable = function() { - // to be used on submit of form, like login - that.$layer.addClass('OxFront'); - return that; - }; - - that.disableButton = function(id) { - getButtonById(id).options({ - disabled: true - }); - return that; - }; - - that.enable = function() { - that.$layer.removeClass('OxFront'); - return that; - }; - - that.enableButton = function(id) { - getButtonById(id).options({ - disabled: false - }); - return that; + that.fullscreen = function() { + }; that.open = function() { - //Ox.print('before open') - if (self.options.focus) { - that.$layer.appendTo(Ox.UI.$body); - } - that.css({ - opacity: 0 - }).appendTo(Ox.UI.$body).animate({ - opacity: 1 - }, 200); + setMinAndMax(); center(); reset(); + that.css({ + opacity: 0 + }).show().animate({ + opacity: 1 + }, 100); if (self.options.focus) { + self.$layer.show(); that.gainFocus(); - Ox.UI.$window.bind('mouseup', mouseupLayer); + Ox.UI.$window.bind({mouseup: mouseupLayer}); } - //Ox.print('after open') - return that; + Ox.UI.$window.bind({ + resize: function() { + var offset = that.offset(); + setMinAndMax(); + if (self.centered) { + center(); + } else { + that.css({ + left: Math.min(offset.left, self.maxLeft) + 'px', + top: Math.min(offset.top, self.maxTop) + 'px' + }); + } + } + }); + return that; }; - that.size = function(width, height, callback) { - $.extend(self, { - initialWidth: width, - initialHeight: height - }); - $.extend(self.options, { - width: width, - height: height - }); - // fixme: duplicated - that.animate({ - height: self.options.height + 'px', - width: self.options.width + 'px' - }, 100, function() { - that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically - callback(); + that.setSize = function(width, height) { + self.options.width = width; + self.options.height = height; + setMinAndMax(); + if (self.maximized) { + self.options.width = self.options.maxWidth; + self.options.height = self.options.maxHeight; + } + center(true); + that.triggerEvent('resize', { + width: self.options.width, + height: self.options.height }); }; diff --git a/source/Ox.UI/js/Window/Ox.Window.js b/source/Ox.UI/js/Window/Ox.Window.js deleted file mode 100644 index 8ba2c958..00000000 --- a/source/Ox.UI/js/Window/Ox.Window.js +++ /dev/null @@ -1,68 +0,0 @@ -// vim: et:ts=4:sw=4:sts=4:ft=javascript -/*@ -Ox.Window Window object - () -> Window object - (options) -> Window object - (options, self) -> Window object - options Options object - draggable is window draggable - fullscreenable fixme: silly name - height height - resizeable resizeable - scaleable sccaleable - width width - self Shared private variable -@*/ -Ox.Window = function(options, self) { - - self = self || {}; - var that = Ox.Element({}, self) - .defaults({ - draggable: true, - fullscreenable: true, // fixme: silly name - height: 225, - resizeable: true, - scaleable: true, - width: 400 - }) - .options(options || {}); - - self.center = function() { - - }; - - self.drag = function() { - - }; - - self.fullscreen = function() { - - }; - - self.setOption = function() { - - }; - - self.reset = function() { - - }; - - self.resize = function() { - - }; - - self.scale = function() { - - }; - - that.close = function() { - - }; - - that.open = function() { - - }; - - return that; - -}; diff --git a/source/Ox.UI/themes/classic/css/classic.css b/source/Ox.UI/themes/classic/css/classic.css index b4211077..0cf3ba96 100644 --- a/source/Ox.UI/themes/classic/css/classic.css +++ b/source/Ox.UI/themes/classic/css/classic.css @@ -99,6 +99,13 @@ Dialog background: rgb(255, 255, 255); } +.OxThemeClassic .OxWindow { + -moz-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.75); + -webkit-box-shadow: 0 2px 8px rgba(0, 0, 0, 0.75); +} +.OxThemeClassic .OxWindow .OxContent { + background: rgba(208, 208, 208, 0.95); +} /* ================================================================================ Document diff --git a/source/Ox.UI/themes/modern/css/modern.css b/source/Ox.UI/themes/modern/css/modern.css index 6f7e3005..64ef90f1 100644 --- a/source/Ox.UI/themes/modern/css/modern.css +++ b/source/Ox.UI/themes/modern/css/modern.css @@ -95,6 +95,14 @@ Dialog background: rgb(0, 0, 0); } +.OxThemeModern .OxWindow > div { + -moz-box-shadow: 2px 2px 8px rgba(0, 0, 0, 1); + -webkit-box-shadow: 2px 2px 8px rgba(0, 0, 0, 1); +} +.OxThemeModern .OxWindow .OxContent { + background: rgba(48, 48, 48, 0.95); +} + /* ================================================================================ diff --git a/source/Ox.js b/source/Ox.js index 0011cad3..df175a68 100644 --- a/source/Ox.js +++ b/source/Ox.js @@ -894,7 +894,7 @@ Ox.some = function(obj, fn) { }; /*@ -Ox.substr Returns a substring or sub-array +Ox.sub Returns a substring or sub-array Ox.sub behaves like collection[start:stop] in Python (or, for strings, like str.substring() with negative values for stop) > Ox.sub([1, 2, 3], 1, -1) @@ -2740,7 +2740,7 @@ Ox.formatUnit = function(num, str) { /*@ Ox.getLatLngByXY Returns lat/lng for a given x/y on a 1x1 mercator projection - > Ox.values(Ox.getLatLngByXY({x: 0.5, y: 0.5})) + > Ox.getLatLngByXY({x: 0.5, y: 0.5}) {lat: 0, lng: 0} @*/ Ox.getLatLngByXY = function(xy) { @@ -3398,7 +3398,7 @@ Ox.test = function(file, callback) { name: item.name, section: item.section, statement: example.statement, - success: Ox.isEqual(eval('Ox.test.result = ' + example.result), actual) + passed: Ox.isEqual(eval('Ox.test.result = ' + example.result), actual) }); } }); @@ -4273,31 +4273,6 @@ Ox.stripTags = function(str) { return str.replace(/<.*?>/g, ''); }; -/*@ -Ox.substr A better substr - Ox.substr behaves like str[start:stop] in Python - (or like str.substring() with negative values for stop) - > Ox.substr('foobar', 1) - "oobar" - > Ox.substr('foobar', -1) - "r" - > Ox.substr('foobar', 1, 5) - "ooba" - > Ox.substr('foobar', 1, -1) - "ooba" - > Ox.substr('foobar', -5, 5) - "ooba" - > Ox.substr('foobar', -5, -1) - "ooba" -@*/ -// deprecated, use Ox.sub() -Ox.substr = function(str, start, stop) { - // fixme: needed? - stop = Ox.isUndefined(stop) ? str.length : stop; - return str.substring( - ); -}; - /*@ Ox.toCamelCase Takes a string with '-', '/' or '_', returns a camelCase string > Ox.toCamelCase('foo-bar-baz') @@ -4382,29 +4357,27 @@ Ox.truncate Truncate a string to a given length (string, length, position, placeholder) -> Truncated string > Ox.truncate('anticonstitutionellement', 16) 'anticonstitut...' - > Ox.truncate('anticonstitutionellement', 16, -1) + > Ox.truncate('anticonstitutionellement', 16, '...', 'left') '...utionellement' > Ox.truncate('anticonstitutionellement', 16, '>') 'anticonstitutio>' - > Ox.truncate("anticonstitutionellement", 16, "...", "center") + > Ox.truncate('anticonstitutionellement', 16, '...', 'center') 'anticon...lement' @*/ Ox.truncate = function(str, len, pad, pos) { - /* - */ - var pad = pad || {}, - pos = pos || "right", + var pad = pad || '...', + pos = pos || 'right', strlen = str.length, padlen = pad.length, left, right; if (strlen > len) { - if (pos == "left") { + if (pos == 'left') { str = pad + str.substr(padlen + strlen - len); - } else if (pos == "center") { + } else if (pos == 'center') { left = Math.ceil((len - padlen) / 2); right = Math.floor((len - padlen) / 2); str = str.substr(0, left) + pad + str.substr(-right); - } else if (pos == "right") { + } else if (pos == 'right') { str = str.substr(0, len - padlen) + pad; } } diff --git a/tests/tests.js b/tests/tests.js index 1fe69f9e..047cc8c3 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -26,8 +26,8 @@ Ox.load('UI', function() { tests(['../build/Ox.js'/*, '../build/js/ox.data.js'*/]); function tests() { - var succeeded = 0, failed = 0, - lines, spaces, command, expected, result, success, + var passed = 0, failed = 0, + lines, spaces, command, expected, result, passed, replace = ['', ''], fns = [], $test Ox.forEach(Ox.isArray(arguments[0]) ? arguments[0] : arguments, function(script, i) { Ox.test(script, function(results) { @@ -37,7 +37,6 @@ Ox.load('UI', function() { tests[result.section][result.name] = tests[result.section][result.name] || []; tests[result.section][result.name].push(result); }); - Ox.print('tests', tests) Ox.forEach(tests, function(functions, section) { Ox.Bar({size: 14}) .css({padding: '1px 0 1px 4px'}) @@ -51,27 +50,27 @@ Ox.load('UI', function() { .appendTo($body); setBackground($test.find('.OxBar'), true); arr.forEach(function(test) { - succeeded += test.success; - failed += !test.success; + passed += test.passed; + failed += !test.passed; $tests.html( - (succeeded + failed) + ' tests, ' - + succeeded + ' succeeded, ' + failed + ' failed' + (passed + failed) + ' tests, ' + + passed + ' passed, ' + failed + ' failed' ); - if (!test.success) { + if (!test.passed) { setBackground($tests, false); setBackground($test.find('.OxBar'), false); } Ox.Element() .css({ padding: '2px 0 2px 4px', - background: 'rgb(' + colors[+test.success][2] + ')' + background: 'rgb(' + colors[+test.passed][2] + ')' }) .html( '' + Ox.encodeHTML(test.statement) + ' ' - + (test.success ? '=' : '!') + '=> ' + + (test.passed ? '=' : '!') + '=> ' + Ox.encodeHTML(test.expected) - + (test.success ? '' : ' ==> ' + Ox.encodeHTML(test.actual)) + + (test.passed ? '' : ' ==> ' + Ox.encodeHTML(test.actual)) + '' ) .appendTo($test.$content); @@ -82,88 +81,15 @@ Ox.load('UI', function() { }); }); }) - /* - $.get(script, function(data) { - Ox.print(script, Ox) - new Ox.Bar({size: 17}) - .css({padding: '3px 0 0 8px'}) - .html(Ox.basename(script)) - .appendTo($body); - lines = data.split('\n'); - $.each(lines, function(l, line) { - if (line.indexOf('/*') > -1 && lines[l + 1].indexOf('====') > -1) { - new Ox.Bar({size: 17}) - .css({padding: '3px 0 0 24px'}) - .html($.trim(lines[l + 2])) - .appendTo($body); - //setBackground(x, 2) - } - spaces = line.indexOf('>>> '); - if (spaces > -1) { - command = $.trim(line).substr(4).split(' // ')[0]; - fn = command.match(/Ox\.\w+/g); - Ox.print('fn', fn) - //Ox.print(lines[l + 1].substr(spaces, lines[l + 1].length), eval(lines[l + 1].substr(spaces, lines[l + 1].length))) - Ox.print(lines[l + 1].substr(spaces, lines[l + 1].length)); - expected = eval(lines[l + 1].substr(spaces, lines[l + 1].length)); - // fixme: if object, recursively check identity - result = eval(command); - if (fn) { - fn = fn[fn.length - 1]; - success = typeof expected == 'object' ? - expected.toString() == result.toString() : expected === result; - Ox.print('command:', command, 'expected:', expected, 'result:', result) - success = Ox.isEqual(expected, result); - succeeded += success; - failed += !success; - $tests.html((succeeded + failed) + ' tests, ' + - succeeded + ' succeeded, ' + failed + ' failed'); - if (!success) { - setBackground($tests, success); - } - if ($.inArray(fn, fns) == -1) { - fns.push(fn); - $test = Ox.CollapsePanel({ - collapsed: true, - title: fn + '()' - }) - .appendTo($body); - setBackground($test.find('.OxBar'), true); - } - new Ox.Bar({size:17}) // fixme: Ox.Object() used to work - .css({ - //padding: '2px 0 2px 8px', - background: 'rgb(' + colors[+success][2] + ')' - }) - .html( - Ox.basename(script) + ', line ' + l + ': ' + - Ox.encodeHTML(command).replace(replace[0], replace[1]) + ' ' + - (success ? '=' : '!') + '=> ' + Ox.encodeHTML(JSON.stringify(expected)) + - (success ? '' : ' ==> ' + Ox.encodeHTML(JSON.stringify(result))) + '' - ) - .appendTo($test.$content); - $test.$content - .css({ - marginTop: -$test.$content.height() + 'px' - }); - } else { - replace = command.split(' = ') - } - } - }); - }, 'html'); - */ }); } - //}); - - function setBackground($element, success) { + function setBackground($element, passed) { ['-moz-linear-gradient', '-webkit-linear-gradient'].forEach(function(v) { $element.css({ background: v + '(top, rgb(' + - colors[+success][0] + '), rgb(' + - colors[+success][1] + '))' + colors[+passed][0] + '), rgb(' + + colors[+passed][1] + '))' }); }); }