diff --git a/e107_files/jslib/core/dialog.js b/e107_files/jslib/core/dialog.js new file mode 100644 index 000000000..f08bbba99 --- /dev/null +++ b/e107_files/jslib/core/dialog.js @@ -0,0 +1,1203 @@ + +/** + * Global prefs + */ +e107Base.setPrefs('core-dialog', { + id: null, + theme : '', + top: null, + left: null, + zIndex: 2000, + width: 300, + height: 200, + minWidth: 200, + minHeight: 100, + maxHeight: null, + maxWidth: null, + gridX: 1, + gridY: 1, + wired: false, + draggable : true, + resizable : true, + activeOnClick : true, + show: Element.show, + hide: Element.hide, + dialogManager: null, + positionningStrategyOffset: null, + close: 'destroy' // e107Widgets.Dialog method for closing dialog +}); + + +e107Widgets.Dialog = Class.create(e107WidgetAbstract,{ + Version : '1.0', + style : "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;", + wiredElement: null, + events : null, + element : null, // window element + header : null, // window header element + content : null, // window content element + footer : null, // window footer element + visible: false, // window visibility + focused: false, // windows on focus + modal: false, // modal window + zIndex: 0, + + action: function(name) { + var action = this.options[name]; + if (action) + Object.isString(action) ? this[action]() : action.call(this, this); + }, + + initialize : function(options) { + this.events = new e107EventManager(this); + this.events.observe('create', this.createButtons)//bind create and createButtons + .observe('destroy', this.destroyButtons); //bind destroy and destroyButtons + + this.initMod('core-dialog', options); + // TODO - check e107Widgets.DialogManagerDefault presence, create if not found + this.dialogManager = this.options.dialogManager || e107Widgets.DialogManagerDefault; + + if(this.options.id && this.dialogManager.getWindow($(this.options.id))) return; + + this.create(); + this.id = this.element.id; + + this.dialogManager.register(this); + this.render(); + + if (this.options.activeOnClick) + this.overlay.setStyle({ + zIndex: this.lastZIndex + 1 + }).show(); + }, + + getTheme: function() { + return this.options.theme || this.dialogManager.getTheme(); + }, + + setTheme: function(theme, managerTheme) { + this.element.removeClassName(this.getTheme()).addClassName(theme); + // window has it's own theme + if (!managerTheme) + this.options.theme = theme; + + return this; + }, + + // shadows are under construction + getShadowTheme: function() { + return this.options.shadowTheme || this.dialogManager.getShadowTheme(); + }, + + create : function() { + function createDiv(className, options) { + return new Element('div', Object.extend( { + className : className + }, options)); + } + + // Main div FIXME - this.options.className should go to dialogManager.className + this.element = createDiv(this.dialogManager.options.className + " " + this.getTheme(), { + id: this.options.id, + style: "top:-10000px; left:-10000px" + }); + + // Create window HTML code + this.header = createDiv('n move_handle').enableDrag(); + this.content = createDiv('content').appendText(' '); + this.footer = createDiv('s move_handle').enableDrag(); + + var header = createDiv('nw').insert( createDiv('ne').insert(this.header)); + var content = createDiv('w').insert(createDiv('e', { + style : "position:relative" + }).insert(this.content)); + var footer = createDiv('sw').insert(createDiv('se' + (this.options.resizable ? " se_resize_handle" : "")).insert(this.footer)); + + this.element.insert(header).insert(content).insert(footer); + this.options.id = this.element.identify(); + this.header.observe('mousedown', this.activate.bind(this)); + + this.setDraggable(this.options.draggable); + this.setResizable(this.options.resizable); + + this.overlay = new Element('div', { + style : this.style + "display: none" + }).observe('mousedown', this.activate.bind(this)); + + if (this.options.activeOnClick) + this.content.insert( { + before : this.overlay + }); + this.events.notify('create'); + }, + + createWiredElement: function() { + this.wiredElement = this.wiredElement || new Element("div", { + className: this.getTheme() + "_wired", + style: "display: none; position: absolute; top: 0; left: 0" + }); + }, + + createResizeHandles: function() { + $w("n w e s nw ne sw se").each(function(id) { + this.insert(new Element("div", { + className: id + "_sizer resize_handle", + drag_prefix: id + }).enableDrag()); + }, this.element); + this.createResizeHandles = Prototype.emptyFunction; + }, + + // First rendering, pre-compute window border size + render: function() { + this.addElements(); + + this.computeBorderSize(); + this.updateButtonsOrder(); + this.element.hide().remove(); + + // this.options contains top, left, width and height keys + return this.setBounds(this.options); + }, + + show : function(modal) { + if (this.visible) + return this; + + this.fire('showing'); + this.effect('show'); + + if (modal) { + this.dialogManager.startModalSession(this); + this.modalSession = true; + } + + this.addElements(); + this.visible = true; + + new PeriodicalExecuter(function(executer) { + if (!this.element.visible()) + return; + this.fire('shown'); + executer.stop(); + }.bind(this), 0.1); + + return this; + }, + + /* + * Method: hide Hides the window, (removes it from the DOM) + * + * Returns: this + */ + hide: function() { + if (!this.visible) + return this; + + this.fire('hiding'); + this.effect('hide'); + + if (this.modalSession) { + this.dialogManager.endModalSession(this); + this.modalSession = false; + } + + new PeriodicalExecuter(function(executer) { + if (this.element.visible()) + return; + this.visible = false; + this.element.remove(); + this.fire('hidden'); + executer.stop(); + }.bind(this), 0.1); + + return this; + }, + + close: function() { + return this.action('close'); + }, + + activate: function() { + return this.bringToFront().focus(); + }, + + bringToFront: function() { + return this.setAltitude('front'); + }, + + sendToBack: function() { + return this.setAltitude('back'); + }, + + focus: function() { + if (this.focused) + return this; + + this.dialogManager.focus(this); + // Hide the overlay that catch events + this.overlay.hide(); + // Add focused class name + this.element.addClassName(this.getTheme() + '_focused'); + + this.focused = true; + this.fire('focused'); + return this; + }, + + /* + * Method: blur Blurs the window (without changing windows order) + * + * Returns: this + */ + blur: function() { + if (!this.focused) + return this; + + this.dialogManager.blur(this); + this.element.removeClassName(this.getTheme() + '_focused'); + + // Show the overlay to catch events + if (this.options.activeOnClick) + this.overlay.setStyle( { + zIndex: this.lastZIndex + 1 + }).show(); + + this.focused = false; + this.fire('blurred'); + return this; + }, + + /* + * Method: destroy Destructor, closes window, cleans up DOM and memory + */ + destroy: function() { + this.hide(); + if (this.centerOptions) + Event.stopObserving(this.dialogManager.scrollContainer, "scroll", this.centerOptions.handler); + + this.dialogManager.unregister(this); + this.fire('destroyed'); + this.events.notify('destroy'); + }, + + setHeader: function(header) { + this.header.update(header); + return this; + }, + + setContent: function(content) { + this.content.update(content); + return this; + }, + + setAjaxContent: function(url, options) { + if (!options) + options = {}; + + // bind all callbacks to the window + Object.keys(options).each(function(name) { + if (Object.isFunction(options[name])) + options[name] = options[name].bind(this); + }, this); + + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.setContent(response.responseText); + if (Object.isFunction(onComplete)) + onComplete(response, json); + }).bind(this); + + new e107Ajax.Request(url, options); + return this; + }, + + setFooter : function(footer) { + this.footer.update(footer); + return this; + }, + + getPosition: function() { + return { + left: this.options.left, + top: this.options.top + }; + }, + + setPosition: function(top, left) { + var pos = this.computePosition(top, left); + this.options.top = pos.top; + this.options.left = pos.left; + + var elementStyle = this.element.style; + elementStyle.top = pos.top + 'px'; + elementStyle.left = pos.left + 'px'; + + this.fire('position:changed'); + return this; + }, + + center: function(options) { + var size = this.getSize(), dManager = this.dialogManager, viewport = dManager.viewport; + viewportArea = viewport.getDimensions(), offset = viewport.getScrollOffset(); + + if (options && options.auto) { + this.centerOptions = Object.extend( { + handler: this.recenter.bind(this) + }, options); + Event.observe(dManager.scrollContainer, "scroll", this.centerOptions.handler); + Event.observe(window, "resize", this.centerOptions.handler); + } + + options = Object.extend( { + top: (viewportArea.height - size.height) / 2, + left: (viewportArea.width - size.width) / 2 + }, options || {}); + + return this.setPosition(options.top + offset.top, options.left + offset.left); + }, + + getSize: function(innerSize) { + if (innerSize) + return { + width: this.options.width - this.borderSize.width, + height: this.options.height - this.borderSize.height + } + else + return { + width: this.options.width, + height: this.options.height + }; + }, + + /* + * innerSize: if true change set content size, else set window size + * (defaults to false) + */ + setSize: function(width, height, innerSize) { + var size = this.computeSize(width, height, innerSize); + var elementStyle = this.element.style, contentStyle = this.content.style; + + this.options.width = size.outerWidth; + this.options.height = size.outerHeight; + + elementStyle.width = size.outerWidth + "px", elementStyle.height = size.outerHeight + "px"; + contentStyle.width = size.innerWidth + "px", contentStyle.height = size.innerHeight + "px"; + this.overlay.style.height = size.innerHeight + "px"; + + this.fire('size:changed'); + return this; + }, + + /* + * innerSize: returns content size if true, window size + * otherwise + */ + getBounds: function(innerSize) { + return Object.extend(this.getPosition(), this.getSize(innerSize)); + }, + + /* + * Method: setBounds Sets window bounds (in pixels), fires + * position:changed and size:changed + * + * Parameters bounds: Hash {top:, left:, width:, height:} where all + * values are optional + * innerSize: sets content size if true, window size + * otherwise + * + * Returns: Hash {top:, left:, width:, height:} + */ + setBounds: function(bounds, innerSize) { + return this.setPosition(bounds.top, bounds.left).setSize(bounds.width, bounds.height, innerSize); + }, + + /* + * Method: getAltitude Returns window altitude, an integer between 0 and + * the number of windows, the higher the altitude number - the higher + * the window position. + */ + getAltitude: function() { + return this.dialogManager.getAltitude(this); + }, + + /* + * Method: setAltitude Sets window altitude, fires 'altitude:changed' if + * altitude was changed + */ + setAltitude: function(altitude) { + if (this.dialogManager.setAltitude(this, altitude)) + this.fire('altitude:changed'); + return this; + }, + + setDraggable : function(draggable) { + this.options.draggable = draggable; + this.element[(draggable ? 'add' : 'remove') + 'ClassName']('draggable'); + return this; + }, + + setResizable: function(resizable) { + this.options.resizable = resizable; + + var toggleClassName = (resizable ? 'add' : 'remove') + 'ClassName'; + + this.element[toggleClassName]('resizable').select('div:[class*=_sizer]').invoke(resizable ? 'show' : 'hide'); + if (resizable) + this.createResizeHandles(); + + this.element.select('div.se').first()[toggleClassName]('se_resize_handle'); + return this; + }, + + fire: function(eventName, memo) { + memo = memo || {}; + memo.window = this; + return (this.savedElement || this.element).fire('edialog:' + eventName, memo); + }, + + observe: function(eventName, handler) { + this.element.observe('edialog:' + eventName, handler.bind(this)); + return this; + }, + + addElements: function() { + this.dialogManager.container.appendChild(this.element); + }, + + effect: function(name, element, options) { + var effect = this.options[name] || Prototype.emptyFunction; + effect(element || this.element, options || {}); + }, + + // Set z-index to all window elements + setZIndex: function(zIndex) { + if (this.zIndex != zIndex) { + this.zIndex = zIndex; + [ this.element ].concat(this.element.childElements()).each(function(element) { + element.style.zIndex = zIndex++; + }); + this.lastZIndex = zIndex; + } + return this; + }, + + // re-compute window border size + computeBorderSize: function() { + if (this.element) { + if (e107API.Browser.IE >= 7) + this.content.style.width = "100%"; + + var dim = this.element.getDimensions(), + pos = this.content.positionedOffset(); + + this.borderSize = { + top: pos[1], + bottom: dim.height - pos[1] - this.content.getHeight(), + left: pos[0], + right: dim.width - pos[0] - this.content.getWidth() + }; + this.borderSize.width = this.borderSize.left + + this.borderSize.right; + this.borderSize.height = this.borderSize.top + + this.borderSize.bottom; + if (e107API.Browser.IE >= 7) + this.content.style.width = "auto"; + } + }, + + computeSize: function(width, height, innerSize) { + var innerWidth, innerHeight, outerWidth, outerHeight; + if (innerSize) { + outerWidth = width + this.borderSize.width; + outerHeight = height + this.borderSize.height; + } else { + outerWidth = width; + outerHeight = height; + } + // Check grid value + if (!this.animating) { + outerWidth = outerWidth.snap(this.options.gridX); + outerHeight = outerHeight.snap(this.options.gridY); + + // Check min size + if (outerWidth < this.options.minWidth) + outerWidth = this.options.minWidth; + if (outerHeight < this.options.minHeight) + outerHeight = this.options.minHeight; + + // Check max size + if (this.options.maxWidth && outerWidth > this.options.maxWidth) + outerWidth = this.options.maxWidth; + + if (this.options.maxHeight && outerHeight > this.options.maxHeight) + outerHeight = this.options.maxHeight; + } + + if (this.centerOptions && this.centerOptions.auto) + this.recenter(); + + innerWidth = outerWidth - this.borderSize.width; + innerHeight = outerHeight - this.borderSize.height; + return { + innerWidth: innerWidth, + innerHeight: innerHeight, + outerWidth: outerWidth, + outerHeight: outerHeight + }; + }, + + computePosition: function(top, left) { + if (this.modal && this.centerOptions && this.centerOptions.auto) + return this.computeRecenter(this.getSize()); + + return { + top: this.animating ? top : top.snap(this.options.gridY), + left: this.animating ? left : left.snap(this.options.gridX) + }; + }, + + computeRecenter: function(size) { + var viewport = this.dialogManager.viewport, area = viewport.getDimensions(), offset = viewport.getScrollOffset(), center = { + top: Object.isUndefined(this.centerOptions.top) ? (area.height - size.height) / 2 : this.centerOptions.top, + left: Object.isUndefined(this.centerOptions.left) ? (area.width - size.width) / 2 : this.centerOptions.left + }; + + return { + top: parseInt(center.top + offset.top), + left: parseInt(center.left + offset.left) + }; + }, + + recenter: function(event) { + var pos = this.computeRecenter(this.getSize()); + this.setPosition(pos.top, pos.left); + } +}); + +e107Widgets.Dialog.addMethods( { + + startDrag: function(handle) { + this.initBounds = this.getBounds(); + this.activate(); + + if (this.options.wired) { + this.createWiredElement(); + this.wiredElement.style.cssText = this.element.style.cssText; + this.element.hide(); + this.saveElement = this.element; + this.dialogManager.container.appendChild(this.wiredElement); + this.element = this.wiredElement; + } + + handle.hasClassName('resize_handle') ? this.startResize(handle) : this.startMove(); + }, + + endDrag: function() { + this.element.hasClassName('resized') ? this.endResize() : this.endMove(); + + if (this.options.wired) { + this.saveElement.style.cssText = this.wiredElement.style.cssText; + this.wiredElement.remove(); + this.element = this.saveElement; + this.saveElement = false; + } + }, + + startMove: function() { + // method used to drag + this.drag = this.moveDrag; + this.element.addClassName('moved'); + this.fire('move:started'); + }, + + endMove: function() { + this.element.removeClassName('moved'); + this.fire('move:ended'); + }, + + startResize: function(handle) { + this.drag = this[handle.readAttribute('drag_prefix') + 'Drag']; + this.element.addClassName('resized'); + this.fire('resize:started'); + }, + + endResize: function() { + this.element.removeClassName('resized'); + this.fire('resize:ended'); + }, + + moveDrag: function(dx, dy) { + var top = this.initBounds.top + dy, left = this.initBounds.left + dx; + if(this.options.noPositionConstrain) + this.setPosition(top, left); + else + this.setPosition(top < 0 ? 0 : top, left < 0 ? 0 : left); + }, + + swDrag: function(dx, dy) { + var initBounds = this.initBounds; + this.setSize(initBounds.width - dx, initBounds.height + dy).setPosition(initBounds.top, initBounds.left + (initBounds.width - this.getSize().width)); + }, + + seDrag: function(dx, dy) { + this.setSize(this.initBounds.width + dx, this.initBounds.height + dy); + }, + + nwDrag: function(dx, dy) { + var initBounds = this.initBounds; + this.setSize(initBounds.width - dx, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left + (initBounds.width - this.getSize().width)); + }, + + neDrag: function(dx, dy) { + var initBounds = this.initBounds; + this.setSize(initBounds.width + dx, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left); + }, + + wDrag: function(dx, dy) { + var initBounds = this.initBounds; + this.setSize(initBounds.width - dx, initBounds.height).setPosition(initBounds.top, initBounds.left + (initBounds.width - this.getSize().width)); + }, + + eDrag: function(dx, dy) { + this.setSize(this.initBounds.width + dx, this.initBounds.height); + }, + + nDrag: function(dx, dy) { + var initBounds = this.initBounds; + this.setSize(initBounds.width, initBounds.height - dy).setPosition(initBounds.top + (initBounds.height - this.getSize().height), initBounds.left); + }, + + sDrag: function(dx, dy) { + this.setSize(this.initBounds.width, this.initBounds.height + dy); + } +}); + +e107Widgets.Dialog.addMethods( { + createButtons: function() { + this.buttons = new Element("div", { + className: "buttons" + }).observe('click', this.onButtonsClick.bind(this)) + .observe('mouseover', this.onButtonsHover.bind(this)) + .observe('mouseout', this.onButtonsOut.bind(this)); + + this.element.insert(this.buttons); + + this.defaultButtons.each(function(button) { + if (this.options[button] !== false) + this.addButton(button); + }, this); + }, + + destroyButtons: function() { + this.buttons.stopObserving(); + }, + + defaultButtons: $w('close'), + + getButtonElement: function(buttonName) { + return this.buttons.down("." + buttonName); + }, + + // Controls close, minimize, maximize, etc. + // action can be either a string or a function + // if action is a string, it is the method name that will be called + // else the function will take the window as first parameter. + // if not given action will be taken in window's options + addButton: function(buttonName, action) { + this.buttons.insert(new Element("a", { + className: buttonName, + href: "#" + })); + + if (action) + this.options[buttonName] = action; + + return this; + }, + + removeButton: function(buttonName) { + this.getButtonElement(buttonName).remove(); + return this; + }, + + disableButton: function(buttonName) { + this.getButtonElement(buttonName).addClassName("disabled"); + return this; + }, + + enableButton: function(buttonName) { + this.getButtonElement(buttonName).removeClassName("disabled"); + return this; + }, + + onButtonsClick: function(event) { + var element = event.findElement('a:not(.disabled)'); + + if (element) + this.action(element.className); + event.stop(); + }, + + onButtonsHover: function(event) { + this.buttons.addClassName("over"); + }, + + onButtonsOut: function(event) { + this.buttons.removeClassName("over"); + }, + + updateButtonsOrder: function() { + var buttons = this.buttons.childElements(); + + buttons.inject(new Array(buttons.length), function(array, button) { + array[parseInt(button.getStyle("padding-top"))] = button.setStyle("padding: 0"); + return array; + }).each(function(button) { + this.buttons.insert(button) + }, this); + } +}); + +e107Base.setPrefs('core-dialogmanager', { + className : 'e-dialog', + container: null, // will default to document.body + zIndex: 2000, + theme: "e107", + shadowTheme: "e107", + showOverlay: Element.show, + hideOverlay: Element.hide, + positionningStrategyOffset: null, + positionningStrategy: function(win, area, winoffset) { + e107Widgets.DialogManager.DefPositionningStrategy(win, area, winoffset); + } +}); + +e107Widgets.DialogManager = Class.create(e107WidgetAbstract, { + + initialize: function(options) { + + this.initMod('core-dialogmanager', options); + + this.container = $(this.options.container || document.body); + + if (this.container === $(document.body)) { + this.viewport = document.viewport; + this.scrollContainer = window; + } else { + this.viewport = this.scrollContainer = this.container; + } + + this.container.observe('drag:started', this.onStartDrag.bind(this)) + .observe('drag:updated', this.onDrag.bind(this)) + .observe('drag:ended', this.onEndDrag.bind(this)); + + this.stack = new e107Widgets.DialogManager.Stack(); + this.modalSessions = 0; + + this.createOverlays(); + this.resizeEvent = this.resize.bind(this); + + Event.observe(window, "resize", this.resizeEvent); + }, + + destroy: function() { + this.windows().invoke('destroy'); + this.stack.destroy(); + Event.stopObserving(window, "resize", this.resizeEvent); + }, + + /* + * Method: setTheme Changes window manager's theme, all windows that + * don't have a own theme will have this new theme. + * + * Parameters: theme - theme name + * + * Example: e107Widgets.DialogManagerDefault.setTheme('bluelighting'); + */ + setTheme: function(theme) { + this.stack.windows.select(function(w) { + return !w.options.theme; + }).invoke('setTheme', theme, true); + this.options.theme = theme; + return this; + }, + + getTheme: function() { + return this.options.theme; + }, + + //shadow under construction + getShadowTheme: function() { + return this.options.shadowTheme; + }, + + register: function(win) { + if (this.getWindow(win.id)) + return; + + this.handlePosition(win); + this.stack.add(win); + this.restartZIndexes(); + }, + + unregister: function(win) { + this.stack.remove(win); + + if (win == this.focusedWindow) + this.focusedWindow = null; + }, + + /* + * Method: getWindow Find the window containing a given element. + * + * Example: $$('.e-dialog a.close').invoke('observe', 'click', + * function() { e107Widgets.DialogManagerDefault.getWindow(this).close(); }); + * + * Parameters: element - element or element identifier + * + * Returns: containing window or null + */ + getWindow: function(element) { + element = $(element); + + if (!element) + return; + + if (!element.hasClassName(this.options.className)) + element = element.up(this.options.className); + + var id = element.id; + return this.stack.windows.find(function(win) { + return win.id == id + }); + }, + + /* + * Method: windows Returns an array of all windows handled by this + * window manager. First one is the back window, last one is the front + * window. + * + * Example: UI.defaultWM.windows().invoke('destroy'); + */ + windows: function() { + return this.stack.windows.clone(); + }, + + /* + * Method: getFocusedWindow Returns the focused window + */ + getFocusedWindow: function() { + return this.focusedWindow; + }, + + // INTERNAL + + // Modal mode + startModalSession: function(win) { + if (!this.modalSessions) { + this.removeOverflow(); + this.modalOverlay.className = win.getTheme() + "_overlay"; + this.container.appendChild(this.modalOverlay); + + if (!this.modalOverlay.opacity) + this.modalOverlay.opacity = this.modalOverlay.getOpacity(); + this.modalOverlay.setStyle("height: " + this.viewport.getHeight() + "px"); + + this.options.showOverlay(this.modalOverlay, { + from: 0, + to: this.modalOverlay.opacity + }); + } + this.modalOverlay.setStyle( { + zIndex: win.zIndex - 1 + }); + this.modalSessions++; + }, + + endModalSession: function(win) { + this.modalSessions--; + if (this.modalSessions) { + this.modalOverlay.setStyle( { + zIndex: this.stack.getPreviousWindow(win).zIndex - 1 + }); + } else { + this.resetOverflow(); + this.options.hideOverlay(this.modalOverlay, { + from: this.modalOverlay.opacity, + to: 0 + }); + } + }, + + // not sure this should stay so, too slow? + moveHandleSelector: '.#{className}.draggable .move_handle', + resizeHandleSelector: '.#{className}.resizable .resize_handle', + + onStartDrag: function(event) { + var handle = event.element(), + isMoveHandle = handle.match(this.moveHandleSelector.interpolate(this.options)), + isResizeHandle = handle.match(this.resizeHandleSelector.interpolate(this.options)); + + // ensure dragged element is a window handle ! + if (isResizeHandle || isMoveHandle) { + event.stop(); + + // find the corresponding window + var win = this.getWindow(event.findElement('.' + this.options.className)); + + // render drag overlay + this.container.insert(this.dragOverlay.setStyle( { + zIndex: this.getLastZIndex() + })); + + win.startDrag(handle); + this.draggedWindow = win; + } + }, + + onDrag: function(event) { + if (this.draggedWindow) { + event.stop(); + this.draggedWindow.drag(event.memo.dx, event.memo.dy); + } + }, + + onEndDrag: function(event) { + if (this.draggedWindow) { + event.stop(); + this.dragOverlay.remove(); + this.draggedWindow.endDrag(); + this.draggedWindow = null; + } + }, + + maximize: function(win) { + this.removeOverflow(); + this.maximizedWindow = win; + return true; + }, + + restore: function(win) { + if (this.maximizedWindow) { + this.resetOverflow(); + this.maximizedWindow = false; + } + return true; + }, + + removeOverflow: function() { + var container = this.container; + // Remove overflow, save overflow and scrolloffset values to restore + // them when restore window + container.savedOverflow = container.style.overflow || "auto"; + container.savedOffset = this.viewport.getScrollOffset(); + container.style.overflow = "hidden"; + + this.viewport.setScrollOffset( { + top: 0, + left: 0 + }); + + if (this.container == document.body && Prototype.Browser.IE) + this.cssRule = CSS.addRule("html { overflow: hidden }"); + }, + + resetOverflow: function() { + var container = this.container; + // Restore overflow ans scrolloffset + if (container.savedOverflow) { + if (this.container == document.body && Prototype.Browser.IE) + this.cssRule.remove(); + + container.style.overflow = container.savedOverflow; + this.viewport.setScrollOffset(container.savedOffset); + + container.savedOffset = container.savedOverflow = null; + } + }, + + hide: function(win) { + var previous = this.stack.getPreviousWindow(win); + if (previous) + previous.focus(); + }, + + getZIndex: function() { + return this.options.zIndex; + }, + + restartZIndexes: function() { + // Reset zIndex + var zIndex = this.getZIndex() + 1; // keep a zIndex free for + // overlay divs + this.stack.windows.each(function(w) { + w.setZIndex(zIndex); + zIndex = w.lastZIndex + 1; + }); + }, + + getLastZIndex: function() { + return this.stack.getFrontWindow().lastZIndex + 1; + }, + + overlayStyle: "position: absolute; top: 0; left: 0; display: none; width: 100%;", + + createOverlays: function() { + this.modalOverlay = new Element("div", { + style: this.overlayStyle + }); + this.dragOverlay = new Element("div", { + style: this.overlayStyle + "height: 100%" + }); + }, + + focus: function(win) { + // Blur the previous focused window + if (this.focusedWindow) + this.focusedWindow.blur(); + this.focusedWindow = win; + }, + + blur: function(win) { + if (win == this.focusedWindow) + this.focusedWindow = null; + }, + + setAltitude: function(win, altitude) { + var stack = this.stack; + + if (altitude === "front") { + if (stack.getFrontWindow() === win) + return; + stack.bringToFront(win); + } else if (altitude === "back") { + if (stack.getBackWindow() === win) + return; + stack.sendToBack(win); + } else { + if (stack.getPosition(win) == altitude) + return; + stack.setPosition(win, altitude); + } + + this.restartZIndexes(); + return true; + }, + + getAltitude: function(win) { + return this.stack.getPosition(win); + }, + + resize: function(event) { + var area = this.viewport.getDimensions(); + + if (this.maximizedWindow) + this.maximizedWindow.setSize(area.width, area.height); + + if (this.modalOverlay.visible()) + this.modalOverlay.setStyle("height:" + area.height + "px"); + }, + + handlePosition: function(win) { + // window has its own position, nothing needs to be done + if (Object.isNumber(win.options.top) && Object.isNumber(win.options.left)) + return; + + // default values + //win.options.top = win.options.left = 0; + var strategy = this.options.positionningStrategy, + area = this.viewport.getDimensions(), + winoffset = win.options.positionningStrategyOffset || win.options.positionningStrategyOffset === false ? win.options.positionningStrategyOffset : this.options.positionningStrategyOffset; + + Object.isFunction(strategy) ? strategy(win, area) : strategy.position(win, area, winoffset); + } +}); + +e107Widgets.DialogManager.DefPositionningStrategy = function(win, area, winoffset) { + + var manager = win.dialogManager, + last = manager.stack.getFrontWindow(), + size = win.getSize(), + offset = manager.viewport.getScrollOffset(), + maxtop = area.height - size.height, + maxleft = area.width - size.width, + poffset = winoffset === false ? 0 : (winoffset || 20), + start = { left: offset[0] + poffset, top: offset[1] + poffset }; + + if(last) { + start = last.getPosition(); + start.left = (start.left < offset[0] ? offset[0] : start.left) + poffset; + start.top = (start.top < offset[1] ? offset[1] : start.top) + poffset; + } + + left = start.left < maxleft ? start.left : start.left - (poffset * 2); + top = start.top < maxtop ? start.top : start.top - (poffset * 2); + + win.setPosition(top, left); +}; + +e107Widgets.DialogManager.Stack = Class.create(Enumerable, { + initialize: function() { + this.windows = []; + }, + + each: function(iterator) { + this.windows.each(iterator); + }, + + add: function(win, position) { + this.windows.splice(position || this.windows.length, 0, win); + }, + + remove: function(win) { + this.windows = this.windows.without(win); + }, + + sendToBack: function(win) { + this.remove(win); + this.windows.unshift(win); + }, + + bringToFront: function(win) { + this.remove(win); + this.windows.push(win); + }, + + getPosition: function(win) { + return this.windows.indexOf(win); + }, + + setPosition: function(win, position) { + this.remove(win); + this.windows.splice(position, 0, win); + }, + + getFrontWindow: function() { + return this.windows.last(); + }, + + getBackWindow: function() { + return this.windows.first(); + }, + + getPreviousWindow: function(win) { + return (win == this.windows.first()) ? null : this.windows[this.windows.indexOf(win) - 1]; + } +}); + +/** + * String extension + */ +Object.extend(Number.prototype, { + // Snap a number to a grid + snap: function(round) { + return parseInt(round == 1 ? this : (this / round).floor() * round); + } +}); + +document.observe('dom:loaded', function() { + e107Widgets.DialogManagerDefault = new e107Widgets.DialogManager(); +}); + + \ No newline at end of file diff --git a/e107_files/jslib/core/dialog/dialog.css b/e107_files/jslib/core/dialog/dialog.css new file mode 100644 index 000000000..4129e05ed --- /dev/null +++ b/e107_files/jslib/core/dialog/dialog.css @@ -0,0 +1,86 @@ +.e-dialog { + position: absolute; +} + +.e-dialog .move_handle { + cursor:move !important; +} + +/*.e-dialog .e { + position: relative; +}*/ + +.e-dialog .resize_handle { + line-height: 1px; + font-size:1px; + width: 8px; + height: 8px; + z-index: 90; + position: absolute; +} + +.e-dialog .se_sizer { + cursor: se-resize; + z-index: 100; + bottom:0; + right:0; +} + +.e-dialog .sw_sizer { + cursor: sw-resize; + z-index: 100; + bottom:0; + left:0; +} + +.e-dialog .nw_sizer { + cursor: nw-resize; + z-index: 100; + top:0; + left:0; +} + +.e-dialog .ne_sizer { + cursor: ne-resize; + z-index: 100; + top:0; + right:0; +} + +.e-dialog .n_sizer { + cursor: n-resize; + width:100%; + top:0; + left:0; +} + +.e-dialog .s_sizer { + cursor: s-resize; + width: 100%; + bottom:0; + left:0; +} + +.e-dialog .e_sizer { + cursor: e-resize; + height:100%; + top:0; + right:0; +} + +.e-dialog .w_sizer { + cursor: w-resize; + height:100%; + top:0; + left:0; +} + +.e-dialog .content { + overflow: auto; + position: relative; +} + +html>body .e-dialog .content { + /* fixes an unbelievable rendering bug in Safari 2 */ + border-top: 1px solid transparent; +} diff --git a/e107_files/jslib/core/dialog/e107/e107.css b/e107_files/jslib/core/dialog/e107/e107.css new file mode 100644 index 000000000..44d1700e7 --- /dev/null +++ b/e107_files/jslib/core/dialog/e107/e107.css @@ -0,0 +1,194 @@ +.e107 .nw { + background:transparent url(icons/top_unactive.gif) no-repeat scroll 0px 0px; + height:22px; + padding-left:10px; +} +.e107_focused .nw { + background:transparent url(icons/top.gif) no-repeat scroll 0px 0px !important; +} + +.e107 .ne { + background:transparent url(icons/top_unactive.gif) no-repeat scroll right -44px; + height:22px; + padding-right:10px; +} + +.e107_focused .ne { + background:transparent url(icons/top.gif) no-repeat scroll right -44px !important; +} + +.e107 .n { + background:transparent url(icons/top_unactive.gif) repeat-x scroll right -22px; + color:#17385B; + font:normal 14px/26px Arial, sans-serif; + height:22px; + margin:0; + padding:0; + text-align:center; + overflow: hidden; + padding-left:60px; + line-height: 20px; + color: #000; +} + +.e107_focused .n { + background:transparent url(icons/top.gif) repeat-x scroll right -22px !important; +} + +.e107 .content { + background:#FFF; + color:#000; + font:normal 12px/1em Verdana, Arial, sans-serif; + overflow:auto; +} + +.e107 .w { + border-left:1px solid #DDD; + border-right:1px solid #DDD; +} + +.e107 .e { + border-left:1px solid #EEE; + border-right:1px solid #EEE; +} + + +.e107 .sw { + background:transparent url(icons/bottom_unactive.gif) no-repeat scroll 0 0px; + font-size:2px; + height:15px; + padding-left:12px; +} + +.e107_focused .sw { + background:transparent url(icons/bottom.gif) no-repeat scroll 0 0px !important; +} + +.e107 .se { + background:transparent url(icons/bottom_unactive.gif) no-repeat scroll right -30px; + font-size:2px; + height:15px; + padding-right:12px; +} + +.e107_focused .se { + background:transparent url(icons/bottom.gif) no-repeat scroll right -30px !important; +} + +.e107_focused .se_sizer { + width:12px; + height:12px; +} + +.e107 .se_resize_handle { + background:transparent url(icons/bottom_unactive.gif) no-repeat scroll right -45px; + font-size:2px; + height:15px; + padding-right:12px; +} + +.e107_focused .se_resize_handle { + background:transparent url(icons/bottom.gif) no-repeat scroll right -45px !important; +} + +.e107 .s { + background:transparent url(icons/bottom_unactive.gif) repeat-x scroll 0 -15px; + font-size:12px; + line-height:15px; + height:15px; + overflow:hidden; + border: 0 none; +} + +.e107_focused .s { + background:transparent url(icons/bottom.gif) repeat-x scroll 0 -15px !important; +} + +.e107 .buttons { + position: absolute; + top:1px; + right:7px; + height: 20px; +} + +.e107 .buttons a.close { + float:left; + background:transparent url(icons/button_unactive.gif) no-repeat 0 0; + background-repeat: no-repeat; + height:15px; + width:14px; + margin: 3px 7px 0 0px; + padding-top:0px; /* padding-top = buttons order, it will be reset to 0, do not use padding, use margin*/ + overflow:hidden; +} + +.e107_focused .buttons a.close { + background:transparent url(icons/buttons.gif) no-repeat 0 0 !important; + background-repeat: no-repeat; +} + +.e107 .buttons.over a.close { + background:transparent url(icons/buttons_over.gif) no-repeat 0 0px !important; +} + +.e107 .buttons a.maximize { + float:left; + background:transparent url(icons/button_unactive.gif) no-repeat 0 0; + height:15px; + width:14px; + margin: 3px 7px 0 0; + padding-top:2px; /* padding-top = buttons order, it will be reset to 0, do not use padding, use margin*/ + overflow:hidden; +} + +.e107_focused .buttons a.maximize { + background:transparent url(icons/buttons.gif) no-repeat 0 -30px !important; +} + +.e107 .buttons.over a.maximize { + background:transparent url(icons/buttons_over.gif) no-repeat 0 -30px !important; +} + +.e107 .buttons a.minimize { + float:left; + background:transparent url(icons/button_unactive.gif) no-repeat 0 0; + height:15px; + width:14px; + margin: 3px 7px 0 0; + padding-top:1px; /* padding-top = buttons order, it will be reset to 0, do not use padding, use margin*/ + overflow:hidden; +} + +.e107_focused .buttons a.minimize { + background:transparent url(icons/buttons.gif) no-repeat 0 -15px !important; +} + +.e107 .buttons.over a.minimize { + background:transparent url(icons/buttons_over.gif) no-repeat 0 -15px !important; +} + +.e107 .buttons a.minimize.disabled, .e107 .buttons.over a.minimize.disabled { + background:transparent url(icons/button_unactive.gif) no-repeat 0 0 !important; +} + +.e107_overlay { + position:absolute; + top:0; + left:0; + width:100%; + background-color:#DDD; + filter:alpha(opacity=60); + opacity: 0.6; + -moz-opacity: 0.6; +} + +.e107_wired { + position:absolute; + border: 3px dashed #f0f0f0; + background-color: #fff; + filter:alpha(opacity=60); + opacity: 0.6; + -moz-opacity: 0.6; +} + + diff --git a/e107_files/jslib/core/dialog/e107/icons/bottom.gif b/e107_files/jslib/core/dialog/e107/icons/bottom.gif new file mode 100644 index 000000000..b0f0bc00d Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/bottom.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/bottom_unactive.gif b/e107_files/jslib/core/dialog/e107/icons/bottom_unactive.gif new file mode 100644 index 000000000..ff54e8fb7 Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/bottom_unactive.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/button_unactive.gif b/e107_files/jslib/core/dialog/e107/icons/button_unactive.gif new file mode 100644 index 000000000..d70b703a5 Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/button_unactive.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/buttons.gif b/e107_files/jslib/core/dialog/e107/icons/buttons.gif new file mode 100644 index 000000000..d166bdd71 Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/buttons.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/buttons_over.gif b/e107_files/jslib/core/dialog/e107/icons/buttons_over.gif new file mode 100644 index 000000000..e126353a6 Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/buttons_over.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/top.gif b/e107_files/jslib/core/dialog/e107/icons/top.gif new file mode 100644 index 000000000..95d8d1c04 Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/top.gif differ diff --git a/e107_files/jslib/core/dialog/e107/icons/top_unactive.gif b/e107_files/jslib/core/dialog/e107/icons/top_unactive.gif new file mode 100644 index 000000000..bc510789c Binary files /dev/null and b/e107_files/jslib/core/dialog/e107/icons/top_unactive.gif differ diff --git a/e107_files/jslib/core/draggable.js b/e107_files/jslib/core/draggable.js new file mode 100644 index 000000000..579cbb4c1 --- /dev/null +++ b/e107_files/jslib/core/draggable.js @@ -0,0 +1,99 @@ +/* + * Prototype UI http://prototype-ui.com/ + * + * Group: Drag UI provides Element#enableDrag method that allow elements to fire + * drag-related events. + * + * Events fired: - drag:started : fired when a drag is started (mousedown then + * mousemove) - drag:updated : fired when a drag is updated (mousemove) - + * drag:ended : fired when a drag is ended (mouseup) + * + * Notice it doesn't actually move anything, drag behavior has to be implemented + * by attaching handlers to drag events. + * + * Drag-related informations: event.memo contains useful information about the + * drag occuring: - dx : difference between pointer x position when drag started + * and actual x position - dy : difference between pointer y position when drag + * started and actual y position - mouseEvent : the original mouse event, useful + * to know pointer absolute position, or if key were pressed. + * + * Example, with event handling for a specific element: > // Now "resizable" + * will fire drag-related events > $('resizable').enableDrag(); > > // Let's + * observe them > $('resizable').observe('drag:started', function(event) { > + * this._dimensions = this.getDimensions(); > }).observe('drag:updated', + * function(event) { > var drag = event.memo; > > this.setStyle({ > width: + * this._dimensions.width + drag.dx + 'px', > height: this._dimensions.height + + * drag.dy + 'px' > }); > }); + * + * Example, with event delegating on the whole document: > // All elements in + * the having the "draggable" class name will fire drag events. > + * $$('.draggable').invoke('enableDrag'); > > document.observe('drag:started', + * function(event) { > UI.logger.info('trying to drag ' + event.element().id); > + * }): + */ +Element.addMethods( { + enableDrag : function(element) { + return $(element).writeAttribute('draggable'); + }, + + disableDrag : function(element) { + return $(element).writeAttribute('draggable', null); + }, + + isDraggable : function(element) { + return $(element).hasAttribute('draggable'); + } +}); + +(function() { + var initPointer, draggedElement; + + document.observe('mousedown', function(event) { + if (draggedElement = findDraggable(event.element())) { + // prevent default browser action to avoid selecting text for + // instance + event.preventDefault(); + initPointer = event.pointer(); + + document.observe('mousemove', startDrag); + document.observe('mouseup', cancelDrag); + } + }); + + function findDraggable(element) { + while (element && element !== document) { + if (element.hasAttribute('draggable')) + return element; + element = $(element.parentNode); + } + } + + function startDrag(event) { + document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag).observe('mousemove', drag).observe('mouseup', endDrag); + fire('drag:started', event); + } + + function cancelDrag(event) { + document.stopObserving('mousemove', startDrag).stopObserving('mouseup', cancelDrag); + } + + function drag(event) { + fire('drag:updated', event); + } + + function endDrag(event) { + document.stopObserving('mousemove', drag).stopObserving('mouseup', endDrag); + + fire('drag:ended', event); + } + + function fire(eventName, event) { + var pointer = event.pointer(); + + draggedElement.fire(eventName, { + dx : pointer.x - initPointer.x, + dy : pointer.y - initPointer.y, + mouseEvent : event + }); + } +})(); \ No newline at end of file diff --git a/e107_files/jslib/e107.js.php b/e107_files/jslib/e107.js.php index 7e987696e..5afa4631e 100644 --- a/e107_files/jslib/e107.js.php +++ b/e107_files/jslib/e107.js.php @@ -8,9 +8,9 @@ * e107 Javascript API * * $Source: /cvs_backup/e107_0.8/e107_files/jslib/e107.js.php,v $ - * $Revision: 1.38 $ - * $Date: 2009-11-18 01:04:43 $ - * $Author: e107coders $ + * $Revision: 1.39 $ + * $Date: 2009-12-16 18:34:01 $ + * $Author: secretr $ * */ @@ -60,10 +60,10 @@ function SyncWithServerTime(serverTime) * @desc Retrieve the browser version */ (function() { - var nav = navigator; - var userAgent = ua = navigator.userAgent; - var v = nav.appVersion; - var version = parseFloat(v); + var nav = navigator, + userAgent = ua = navigator.userAgent, + v = nav.appVersion, + version = parseFloat(v); e107API.Browser = { IE : (Prototype.Browser.IE) ? parseFloat(v.split("MSIE ")[1]) || 0 : 0, @@ -525,7 +525,7 @@ var e107Base = { }, getParseData: function (data) { - Object.extend(data || {}, + data = Object.extend(data || {}, Object.extend(this.getLanVars(), this.getPathVars()) ); @@ -1145,7 +1145,57 @@ Element.addMethods( { downHide: e107Helper.downHide, downShow: e107Helper.downShow, downToggle: e107Helper.downToggle, - downExternalLinks: e107Helper.downExternalLinks + downExternalLinks: e107Helper.downExternalLinks, + + // -- more useful extensions - taken from Prototype UI -- + getScrollDimensions: function(element) { + element = $(element); + return { + width: element.scrollWidth, + height: element.scrollHeight + } + }, + + getScrollOffset: function(element) { + element = $(element); + return Element._returnOffset(element.scrollLeft, element.scrollTop); + }, + + setScrollOffset: function(element, offset) { + element = $(element); + if (arguments.length == 3) + offset = { left: offset, top: arguments[2] }; + element.scrollLeft = offset.left; + element.scrollTop = offset.top; + return element; + }, + + // returns "clean" numerical style (without "px") or null if style can not be resolved + // or is not numeric + getNumStyle: function(element, style) { + var value = parseFloat($(element).getStyle(style)); + return isNaN(value) ? null : value; + }, + + // (http://tobielangel.com/2007/5/22/prototype-quick-tip) + appendText: function(element, text) { + element = $(element); + element.appendChild(document.createTextNode(String.interpret(text))); + return element; + } +}); + +Object.extend(document.viewport, { + // Alias this method for consistency + getScrollOffset: document.viewport.getScrollOffsets, + + setScrollOffset: function(offset) { + Element.setScrollOffset(Prototype.Browser.WebKit ? document.body : document.documentElement, offset); + }, + + getScrollDimensions: function() { + return Element.getScrollDimensions(Prototype.Browser.WebKit ? document.body : document.documentElement); + } }); Element.addMethods('INPUT', {