var igDropdown = new Class({ Implements: [Options, Events], options: { 'openDelay': 0, 'closeDelay': 500, 'menu': null, 'align': 'left', 'direction': null, 'offset_x': 0, 'offset_y': 0, 'onShow': Function.from(true), 'onHide': Function.from(true), 'triggerEvent': 'click', 'target': null, 'repositiontime': 0, 'childpreventclose': [], 'closeexceptionclasses': [], 'secondaryScope': null, 'closebutton': null, 'highlight': null, 'preventAutoHide': false, 'scrollWindow': false, 'parent': null, 'screenAdjust': false, // Move the dropdown if a part of it will display off screen 'focusInput': null // input element within the menu that you want to automatically give focus to. }, initialize: function(trigger, menu, options) { // set the options this.setOptions(options || {}); this.elements = {}; // Create a list of open menus : there should only ever be 1 but we do this for timing issue Igloo.igDropDownOpenList = []; // set the parameters this.elements.trigger = $(trigger); this.elements.menu = this.options.menu || menu; this.elements.menu = $(this.elements.menu); this.elements.secondaryScope = null; if (this.options.secondaryScope) { this.elements.secondaryScope = $(this.options.secondaryScope); } var self = this; if (this.options.closebutton && $(this.options.closebutton)) {//we have the closebutton $(this.options.closebutton).addEvent('click', function(event) { event.preventDefault(); self.hide(); }); } this.isVisible = false; if (this.elements.trigger && this.elements.menu) { this.hasmenutip = true; if (this.options.menutip) { this.elements.menutip = $(this.options.menutip); } else { this.elements.menutip = this.elements.menu.getChildren("div.ig-floatmenu-tip")[0]; if (!this.elements.menutip) { this.hasmenutip = false; } } this.elements.menu.inject(document.body); if (this.options.floatmenu) { this.elements.floatmenu = $(this.options.floatmenu); } else { this.elements.floatmenu = this.elements.menu.getChildren("div.ig-floatmenu")[0]; this.hasigfloatmenu = true; if (!this.elements.floatmenu) { this.hasigfloatmenu = false; } } // Check menu container for JS specific class if (!self.elements.menu.hasClass('js-igMenu')) { self.elements.menu.addClass('js-igMenu'); /* Is this class truely needed? */ } // Check menu container for CSS specific classes if (!self.elements.menu.hasClass('ig-igMenu')) { self.elements.menu.addClass('ig-igMenu'); } if (!self.elements.menu.hasClass('ig-menu-closed')) { self.elements.menu.addClass('ig-menu-closed'); } // Remove hide after slow page loads when CSS has repositioned menus off screen (.ig-menu-closed) this.elements.menu.removeClass('hide'); // menu transition this.menuEffect = new Fx.Morph(this.elements.menu, { duration: this.options.openDelay, transition: Fx.Transitions.Sine.easeOut }); if (this.options.triggerEvent == 'hover') { // add the events on the trigger and nullify target this.elements.trigger.addEvent('mouseenter', function() { self.show(); }); this.elements.trigger.addEvent('mouseleave', function() { self.hideTimeout(); }); // add the events to the menu this.elements.menu.addEvent('mouseenter', function() { self.cancelTimeout(); }); this.elements.menu.addEvent('mouseleave', function() { self.hideTimeout(); }); this.elements.menu.addEvent('click', function(event) { event.stopPropagation(); }); } else if (this.options.triggerEvent == 'click') { // add the events on the trigger and nullify target this.elements.trigger.addEvent('click', function(event) { event.preventDefault(); self.toggle(); }); // ACCESS SECTION - included here to hide specific dropdowns used by access // if elements inside the menu have a 'js-clickClose' class address the hiding of the menu this.elements.menu.getElements('.js-clickClose').addEvent('click', function(event) { if (!event.target.href) { event.preventDefault(); } self.hide(); }); if (this.elements.secondaryScope) { this.elements.secondaryScope.getElements('.js-clickCloseRel').addEvent('click', function(event) { event.preventDefault(); self.hide(); }); } // if the user clicks on the menu prevent this from propagating to the document this.elements.menu.addEvent('click', function(event) { event.stopPropagation(); }); // however if they "click" on the document we want to hide the menu // unless the preventAutoHide option has been specified document.addEvent('click', function(event) { // deal with excepted elements var excepted = false; var clickedElem = event.target; Array.each(self.options.closeexceptionclasses, function(classId) { var parentMatches = $(clickedElem).getParents('.' + classId); if ($(clickedElem).hasClass(classId)) { parentMatches.push(new Element('span')); } if (parentMatches.length > 0) { excepted = true; } // compare the id of the clicked elem against the list, if we haven't found a class match. // This is for IE9 with modals, as IE9 loses all track of classes once a modal is closed, // and we can't do this stuff before the modal actually closes. if (!excepted) { if ($(clickedElem).id && $(clickedElem).id.toString().ciContains(classId)) { excepted = true } } }); if (!excepted) { self.doHide(event); } }); if (this.options.parent) { var parent = $(this.options.parent); if (parent) { parent.addEvent('click', function(event) { self.doHide(event); }); } } } else if (this.options.triggerEvent == 'rightClick') { // if elements inside the menu have a 'js-clickClose' class address the hiding of the menu this.elements.menu.getElements('.js-clickClose').addEvent('click', function() { self.hide(); }); // if the user clicks on the menu prevent this from propagating to the document this.elements.menu.addEvent('click', function(event) { event.stopPropagation(); }); // however if they click on the document we want to hide the menu // we use 'mousedown' instead of 'click' so that it covers the case in IE and Safari // when a menu is open and a user right-clicks on another item the open menus should hide // also since the contextmenu event is fired on mouseup in Safari and FF we should use mousedown // because the contextmenu gets confused as to what the actual target of the menu is. document.addEvent('mousedown', function(event) { if (!$(event.target).getParents().contains(self.elements.menu)) { self.hide(); } }); // add the events to the menu this.elements.menu.addEvent('mouseenter', function() { self.cancelTimeout(); }); this.elements.menu.addEvent('mouseleave', function() { self.hideTimeout(); }); } //the key handler for closing this dropdown using 'esc' document.addEvent('keyup', function(event) { if (event.key == 'esc') { var targetTag = ($(event.target).tagName) ? $(event.target).tagName.toLowerCase() : ""; if (targetTag != 'input' && targetTag != 'textarea') { //if it's not an input... self.hide(); //close the igdropdown if (self.options.highlight) { self.elements.trigger.removeClass(self.options.highlight); } return false; } else { var targetMatchedParents = (self.elements.menu.id) ? $(event.target).getParents('#' + self.elements.menu.id) : []; if (targetMatchedParents && targetMatchedParents.length > 0) { $(event.target).blur(); //lose focus from the input return false; } } } }); this.elements.trigger.addEvent('showDropdown', function() { self.show(); }); this.elements.trigger.addEvent('hideDropdown', function() { self.hide(); }); this.elements.trigger.addEvent('toggleDropdown', function() { self.toggle(); }); this.elements.trigger.addEvent('stopAutoHide', function() { self.options.preventAutoHide = true; }); this.elements.trigger.addEvent('resumeAutoHide', function() { self.options.preventAutoHide = false; }); } window.addEvent('resize', function() { if (self.isVisible) { self.reposition(); } }); }, doHide: function(event) { //helper function for hiding the menu on some event if (event.target != this.elements.trigger && !this.options.preventAutoHide) { if (this.options.childpreventclose && this.options.childpreventclose.length > 0) { var isInCPC = false; Array.each(this.options.childpreventclose, function(child) { if ($(child)) { if ($(child).getElements(event.target).length > 0) { isInCPC = true; } } }); if (!isInCPC) { this.hide(); } } else { // some browsers require that we double-click on the swiff object to invoke it. Let's not close the menu if we're clicking on one. if (event.target.id.toString().toLowerCase().indexOf("swiff_") == -1) { this.hide(); } } } if ($(event.target) != this.elements.trigger) { if ($(event.target).getParent('a') != this.elements.trigger) { if (this.options.highlight) this.elements.trigger.removeClass(this.options.highlight); } } }, reposition: function(mouse) { this.isVisible = true; // set up the menu position & hide it if (mouse) { $(this.elements.menu).setStyles({ // make sure to compensate for the Window scroll position so the menu stays on screen // we offset the menu a little because it has a shadow and border which can get in the way of our event handlers 'left': mouse.x + Window.getScrollLeft() + 2 + 'px', 'top': mouse.y + Window.getScrollTop() + 2 + 'px' }); } else { // move the menu to the bottom of the document so that it will show over top of all items this.elements.menu.inject(document.body); if (this.options.target) { var triggerCoordinates = $(this.options.target).getCoordinates(); } else { var triggerCoordinates = this.elements.trigger.getCoordinates(); } var menuCoordinates = this.elements.menu.getCoordinates(); var page = $('page'); if (!page) { page = $('userbar'); } if (page) { var pageDim = page.getCoordinates(); } // screenAdjust - Always adjust to keep menu on screen if screenAdjust = true if (this.options.screenAdjust && this.options.align == 'bottom-left') { var windowWidth = Window.getCoordinates().width; // Calculate how much space is on each side of menu depending on screen size var menuRightSpace = windowWidth - (triggerCoordinates.left + menuCoordinates.width); var menuLeftSpace = (triggerCoordinates.left + triggerCoordinates.width) - menuCoordinates.width; // When menu displays off screen right then switch menu float if ((triggerCoordinates.left + menuCoordinates.width) > windowWidth) { this.options.align = 'bottom-right'; // Switch back when menu will display more info bask as default align if (menuLeftSpace < 0 && menuRightSpace >= menuLeftSpace) { this.options.align = 'bottom-left'; } } } switch (this.options.align) { case 'bottom-right': case 'right': { var left = triggerCoordinates.right - menuCoordinates.width; $(this.elements.menu).setStyle('left', left + 'px'); $(this.elements.menu).setStyle('top', triggerCoordinates.top + triggerCoordinates.height + 'px'); break; } case 'top-right': { this.elements.menu.setStyles({ 'left': triggerCoordinates.right - menuCoordinates.width + 'px', 'top': triggerCoordinates.top + 'px' }); break; } case 'inner': { this.elements.menu.setStyles({ 'left': triggerCoordinates.right - menuCoordinates.width + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 2 + 'px' }); break; } case 'middle': { if (this.elements.menutip) { var tipwidth = this.elements.floatmenu.getCoordinates().width; this.elements.menutip.setStyles({ 'width': tipwidth + 'px' }); } this.elements.menu.setStyles({ 'left': (triggerCoordinates.left - ((this.elements.floatmenu.getCoordinates().width - triggerCoordinates.width) / 2)) + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 'px' }); break; } case 'bottom-middle': { // Center menu tip in middle of dropdown if (this.elements.menutip) { var menuTipPosition = Math.round((this.elements.menu.getCoordinates().width / 2) - (this.elements.menutip.getCoordinates().width / 2)); this.elements.menutip.setStyles({ 'right': menuTipPosition }); } // Center dropdown under trigger this.elements.menu.setStyles({ 'left': (triggerCoordinates.left - ((this.elements.menu.getCoordinates().width - triggerCoordinates.width) / 2)) + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 'px' }); break; } case 'column': { var column = this.elements.trigger.getParent("div.ig-column"); if (!column) { column = this.elements.trigger.getParent("div.column-secondary-inner"); } if (column) { var columnDimensions = column.getCoordinates(); this.elements.menu.setStyles({ 'left': columnDimensions.left + (columnDimensions.width - menuCoordinates.width) / 2 + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 'px' }); } break; } case 'page': { if (page) { this.elements.menu.setStyles({ 'left': pageDim.left + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 'px' }); if (this.hasigfloatmenu) { var borderoffset = 8; } else { var borderoffset = 4; } this.elements.floatmenu.setStyles({ 'width': pageDim.width - borderoffset + 'px' }); //we set it to be the total width-8 because the borders are 4px on each side. if (this.elements.menutip) { var tipwidth = Math.round(((triggerCoordinates.left - pageDim.left) * 2 + triggerCoordinates.width)); //calculate just how wide the tip needs to point at the middle of the trigger this.elements.menutip.setStyles({ 'width': tipwidth + 'px' }); } } break; } case 'top-left': { this.elements.menu.setStyles({ 'left': triggerCoordinates.left + 'px', 'top': triggerCoordinates.top + 'px' }); break; } case 'bottom-left': case 'left': default: { this.elements.menu.setStyles({ 'left': triggerCoordinates.left + 'px', 'top': triggerCoordinates.top + triggerCoordinates.height + 'px' }); break; } } // Must update the variable with the new coordinates menuCoordinates = this.elements.menu.getCoordinates(); switch (this.options.direction) { case 'n': { this.elements.menu.setStyles({ 'top': (menuCoordinates.top - menuCoordinates.height) + 'px' }); break; } case 's': // Default... Nothing to be done default: { // Keep things as is break; } } // Must update the variable with the new coordinates menuCoordinates = this.elements.menu.getCoordinates(); this.elements.menu.setStyles({ 'left': (menuCoordinates.left + this.options.offset_x) + 'px', 'top': (menuCoordinates.top + this.options.offset_y) + 'px' }); // Must update the variable with the new coordinates menuCoordinates = this.elements.menu.getCoordinates(); //make sure it's all within bounds if (this.elements.floatmenu) { var menuDim = this.elements.floatmenu.getCoordinates(); } else { var menuDim = menuCoordinates; } if (this.elements.menutip) { var menutipDim = this.elements.menutip.getCoordinates(); } if (page && this.options.align == 'middle') { //if there's a page to contain in and alignment is middle - since this is the iffy one if (menuDim.width <= pageDim.width) { //if the menu is meant to be larger than the page, we won't bother if (menuDim.right > pageDim.right) { var difference = menuDim.right - pageDim.right; this.elements.menu.setStyles({ 'left': menuDim.left - difference + 'px' }); if (this.elements.menutip) { this.elements.menutip.setStyles({ 'width': menutipDim.width + difference * 2 + 'px' }); } } if (menuDim.left < pageDim.left) { var difference = (pageDim.left - menuDim.left); this.elements.menu.setStyles({ 'left': menuDim.left + difference + 'px' }); if (this.elements.menutip) { this.elements.menutip.setStyles({ 'width': menutipDim.width - difference * 2 + 'px' }); } } } } } }, hideVisibleMenus: function() { var self = this; this.isVisible = false; // hide any visible menus $$('.js-igMenu').each(function(menu) { if (self && self.menuIsVisible) { self.hide(); } }); }, show: function(mouse) { var self = this; this.isVisible = true; // if position of trigger changed, menu should reflect this // we don't wanna hide all visible menus on house event because it gets tripped up with hiding, then showing, so it's messed.. if (!mouse) { // hide all visible menus this.hideVisibleMenus(); } // show the menu this.menuEffect.start({ 'opacity': [0, 1], 'margin-top': [-3, 0], 'onComplete': function() { self.isVisible = true; // Change default menu classes $(self.elements.menu).removeClass('ig-menu-closed'); $(self.elements.menu).addClass('ig-menu-open'); // Userbar only classes if ($(self.elements.menu).hasClass('ig-usermenu-closed')) { $(self.elements.menu).addClass('ig-usermenu-open'); $(self.elements.menu).removeClass('ig-usermenu-closed'); } self.reposition(mouse); if (!Igloo.igDropDownOpenList.contains(self.elements.trigger.id)) { Igloo.igDropDownOpenList.include(self.elements.trigger.id); } // Responsive Mobile if (Igloo.isResponsiveMobile) { self.mobileMenu(); } } }); if (this.options.scrollWindow) { var windowCoordinates = Window.getCoordinates(); var paneCoordinates = this.elements.menu.getCoordinates(); var myFx = new Fx.Scroll(window).start(0, this.elements.menu.getPosition().y); } if (this.elements.menu) { this.elements.menu.addEvent('hideDropdown', function() { self.hide(); }); } this.fireEvent('show'); }, toggle: function() { var test = this.options.target if (this.isVisible) { this.hide(); if (this.options.highlight) { this.elements.trigger.removeClass(this.options.highlight); } } else { this.show(); if (this.options.highlight) { this.elements.trigger.addClass(this.options.highlight); } if (this.options.focusInput) { $(this.options.focusInput).focus(); } } this.fireEvent('toggle'); }, hide: function() { var self = this; if (this.isVisible) { // hide the menu this.menuEffect.start({ 'opacity': [1, 0], 'onComplete': function() { self.isVisible = false; // Change default menu classes $(self.elements.menu).removeClass('ig-menu-open'); $(self.elements.menu).addClass('ig-menu-closed'); // Userbar only classes if ($(self.elements.menu).hasClass('ig-usermenu-open')) { $(self.elements.menu).removeClass('ig-usermenu-open'); $(self.elements.menu).addClass('ig-usermenu-closed'); } // Set styles and position back to default on hide self.elements.menu.erase('style'); if (Igloo.igDropDownOpenList.contains(self.elements.trigger.id)) { Igloo.igDropDownOpenList.erase(self.elements.trigger.id); } // Responsive Mobile if (Igloo.isResponsiveMobile) { self.mobileMenu(); } } }); this.fireEvent('hide'); } }, cancelTimeout: function() { // we don't wanna close the menu, cancel the action if (this.hideTimer) { window.clearTimeout(this.hideTimer); this.hideTimer = null; } }, hideTimeout: function() { // have a bit of a delay then call the hide function this.hideTimer = window.setTimeout(this.hide.bind(this), this.options.closeDelay); }, getMenu: function() { return this.elements.menu; }, mobileMenu: function() { var self = this; var mobileBlanket = $('js-mobileBlanket'); if (mobileBlanket && Igloo.igDropDownOpenList.length >= 1) { if (this.elements.trigger && !mobileBlanket.hasClass('ig-mobileblanket-open')) { // Cover content mobileBlanket.addClass('ig-mobileblanket-open'); mobileBlanket.addEvent('click', function(event) { this.removeClass('ig-mobileblanket-open'); }); } } else if (mobileBlanket) { mobileBlanket.removeClass('ig-mobileblanket-open'); } } }); window.addEvent('domready', function() { if (Igloo && Igloo.asset_igDropdown) { if (!window.igPaneObjects) { window.igPaneObjects = new Hash(); } Object.each(Igloo.asset_igDropdown, function(object, id) { window.igPaneObjects.set(id, new igDropdown(id, null, object)); }); } }); window.addEvent('bootstrap4finished', function() { if (window.dependents['igDropdown']) { // the old bootstrap4 attaches dataobj as a string, not a Hash // bootstrap4velo attaches the dataobj as a Hash, not a string. wheeee! if (!window.igPaneObjects) { window.igPaneObjects = new Hash(); } window.dependents['igDropdown'].each(function(elem) { window.igPaneObjects.set(elem.id, new igDropdown(elem.id, null, elem.dataobj.igDropdown)); }); } }); // ElementSpy is a plugin for mootools that will watch an element and // can be used to fire an event when a specified property has changed. // This is used in the igDockable class to allow us to reset the dockable // when the page resizes. // This should be used as a last resort only (where there is absolutely no // other way to attach an event to an element, as this is extremely // un-performant. var ElementSpy = new Class({ Implements: [Options,Events], options: { interval: 100 }, initialize: function(element,attribute,options) { this.setOptions(options); this.element = document.id(element); this.val = this.getter(attribute); this.interval; this.worker = function() { var value = this.getter(attribute); this.fireEvent('check',[this.val,value]); if(value != this.val) { var curval = this.val; this.val = value; this.fireEvent('change',[curval,value]); } else { this.fireEvent('stagnate',[value]); //current }}.bind(this); }, start: function() { this.interval = this.worker.periodical(this.options.interval); this.fireEvent('start'); return this; }, stop: function() { clearInterval (this.interval); this.fireEvent('stop'); return this; }, getter: function(attribute) { return typeOf(attribute) == 'function' ? (attribute.bind(this.element))() : this.element.get(attribute); } }); var igDockable = new Class({ Implements: [Options, Events], options: { offsets: { x:0, y:0 }, dockLocation: 'top' }, initialize: function(ship, options) { /* initial options */ this.setOptions(options || {}); this.ship = $(ship); if( !this.ship ){ // if the element doesn't exist on the page we can't do anything, stop the script before it breaks. return null; } /* Dimenstions and position of element before repositioning */ this.coordinates = this.ship.getCoordinates(); /* Create clone to hold spot. This just keeps a "blank space" where the ship will dock */ this.placeholder = this.ship.clone(); this.placeholder.setStyle('visibility', 'hidden'); this.placeholder.inject(this.ship, 'before'); /* Setting CSS and Class to dockable item */ this.ship.setStyles({ 'width': this.coordinates.width, 'height': this.coordinates.height }); this.ship.addClass('ig-dockable'); /* specific style for default dockable behaviour */ if (this.options.dockLocation == 'top') { this.ship.set('styles', {'position': 'absolute', 'top': this.coordinates.top}) } /* specific style for bottom dockable behaviour */ else { this.ship.set('styles', {'position': 'fixed', 'bottom': 0}) } /* Watch the body height for changes */ var placeholder = this.placeholder; var options = this.options; var element = document.id('content-inner'); var self = this; var watchforchanges = new ElementSpy(element, function() { var bodyheight = element.getHeight(); return bodyheight; }, { duration: 500, onChange: function(old,nu) { self.coordinates = self.placeholder.getCoordinates(); self.dock($(document.body).getScroll()['y'] + self.options.offsets['y']); } }); watchforchanges.start(); /* start listening */ this.startListeners(); }, startListeners: function() { this.ship.set('tween', { onComplete: function(){document.fireEvent('dockableTweenComplete')} }); var action = function() { // get the coordinates of the placeholder again in case position was changed (e.g. due to an attachment being added/removed) this.coordinates = this.placeholder.getCoordinates(); this.dock( $(document.body).getScroll()['y'] + this.options.offsets['y'] ); }.bind(this); window.addEvents({ 'scroll': action, 'load': action }); }, dock: function(move) { if (this.options.dockLocation == "bottom") { this.viewportheight = document.getSize().y; // if bellow native position, animate back to position if ( (move + this.viewportheight) > this.coordinates.bottom) { if (this.ship.getStyle('position')=='fixed') { this.ship.removeClass('ig-dockable'); this.ship.setStyles({ 'position':'absolute', 'top': move + this.viewportheight - this.coordinates.height }); this.ship.tween('top', this.coordinates.top); } else { this.ship.setStyles({ 'position':'absolute', 'top': this.coordinates.top }); } } else { if (this.ship.getStyle('position')=='absolute') { this.ship.setStyles({ 'top' : null, 'bottom': '0px', 'position': 'fixed' }); // transition smoothly if the element is triggered far away from it's original dock if ((move + this.viewportheight) > this.coordinates.top) { this.ship.addClass('ig-dockable'); if (!Browser.ie8) { this.ship.setStyle('opacity','0'); this.ship.fade('in'); } } } } } // else assume top else { // if above native position, animate back to position if (move < this.coordinates.top) { if (this.ship.getStyle('position')=='fixed') { this.ship.set('styles', {'position':'absolute','top': move}); this.ship.tween('top', this.coordinates.top); } } else { if (this.ship.getStyle('position')=='absolute') { this.ship.setStyles({ 'top': null, 'position': 'fixed' }); // transition smoothly if the element is triggered far away from it's original dock if (move > (this.coordinates.top + this.coordinates.height) && !Browser.ie8) { this.ship.setStyle('opacity','0'); this.ship.fade('in'); } } } } } }); window.addEvent('domready', function() { if (Igloo && Igloo.asset_igDockable) { var dockable; Object.each(Igloo.asset_igDockable, function(object, id) { dockable = new igDockable(id, object); }); } }); /****************************************** igPushpane by Dacheng Cheng Ideal use: For when you want a pane that comes out and pushes the other content out of the way. Kinda like trailerbar Visibility is always visible, which won't break things like TinyMCE Features: Animation for coming out and going back in Different directions of push (future [currently, down only]) Multiple buttons by ID or class for triggering close functionality ******************************************/ var igPushpane = new Class({ Implements: [Options, Events], options: { 'animation': -1, //time for animation of the sliding in/out - this is in miliseconds, and anything <= 0 == no animation 'menu': null, 'type': null, 'scrollWindow': false, //should we scroll the window to fit the panel? 'closebutton': null, 'hideOnShow': null, //a comma delimited list of element ids that will hide when the pushpane shows and show when it hides 'triggerAutoHide': true, //the trigger hides on menu show and shows on menu hide 'alttrigger': "" // alternate trigger }, initialize: function(trigger, menu, options) { this.id = trigger; // set the options this.setOptions(options || {}); this.elements = {}; // set the parameters this.elements.trigger = $(trigger); if (this.options.alttrigger != "" && $(this.options.alttrigger)) { this.elements.alttrigger = $(this.options.alttrigger); } this.elements.menu = this.options.menu || menu; this.elements.menu = $(this.elements.menu); this.elements.closebutton = $(this.options.closebutton); if (this.elements.trigger && this.elements.menu && this.elements.closebutton) { this.elements.menu.addClass('js-igPushpane'); this.elements.menu.removeClass('hide'); this.elements.menubody = this.elements.menu.getChildren('.ig-Pushpane-body')[0]; this.updateInnerHeight(); } var self = this; if (this.options.hideOnShow) { this.hideOnShow = []; var items = this.options.hideOnShow.split(","); items.each(function(elem) { if ($(elem)) { self.hideOnShow.push(elem); } }); } this.isOpen = false; this.firstOpen = true; this.elements.trigger.addEvent('click', function(event) { event.stop(); self.elements.closebutton.removeClass('hide'); self.show(); }); if (this.elements.alttrigger) { this.elements.alttrigger.addEvent('click', function(event) { event.preventDefault(); self.elements.trigger.fireEvent('click', event); }); } this.elements.closebutton.addEvent('click', function(event) { event.stop(); self.hide(); }); }, show: function() { var self = this; if (!this.isOpen) { this.isOpen = true; //hide the stuff if (this.options.hideOnShow) { this.hideOnShow.each(function(elem) { $(elem).addClass('hide'); }); } if (this.options.triggerAutoHide) { this.elements.trigger.addClass('hide'); } //update the height this.updateInnerHeight(); if (this.options.animation > 0) { var menuFx = new Fx.Tween(this.elements.menu, 'duration', this.options.animation); this.elements.menu.setStyle('height', 0); this.elements.menu.removeClass('js-igPushpane'); menuFx.addEvents({ 'complete': function() { if (self.firstOpen) { self.fireEvent('FirstOpenComplete'); self.firstOpen = false; } self.elements.menu.setStyle('height', null); self.fireEvent('paneOpen'); }, 'start': function() { if (self.firstOpen) { self.fireEvent('FirstOpen'); } // slides down inner content with pane if (self.options.type == 'slide') { var menuFxType = new Fx.Tween(self.elements.menubody, 'duration', self.options.animation); menuFxType.start('margin-top', -self.innerHeight, 0); } } }); menuFx.start('height', 0, self.innerHeight); } else { this.elements.menu.removeClass('js-igPushpane'); this.elements.menu.setStyle('height', null); if (this.firstOpen) { this.fireEvent('FirstOpen'); this.firstOpen = false; } } if (this.options.scrollWindow) { var windowCoordinates = Window.getCoordinates(); var paneCoordinates = this.elements.menu.getCoordinates(); if (windowCoordinates.top + Window.getScroll().y > paneCoordinates.top || windowCoordinates.bottom + Window.getScroll().y < paneCoordinates.bottom) { var myFx = new Fx.Scroll(window).start(0, this.elements.menu.getPosition().y); //var myFx = new Fx.Scroll(window).toElement(popupthing); } } } }, hide: function() { var self = this; if (this.isOpen && !this.isHiding) { this.isOpen = false; if (this.options.animation > 0) { this.updateInnerHeight(); var menuFx = new Fx.Tween(this.elements.menu, 'duration', this.options.animation); menuFx.addEvent('complete', function() { self.elements.menu.addClass('js-igPushpane'); self.elements.menu.setStyle('height', null); self.fireEvent('hide'); if (self.options.hideOnShow) { self.hideOnShow.each(function(elem) { $(elem).removeClass('hide'); }); } if (self.options.triggerAutoHide) { self.elements.trigger.removeClass('hide'); } self.elements.closebutton.addClass('hide'); }); menuFx.start('height', this.innerHeight, 0); // slides down inner content with pane if (this.options.type == 'slide') { var menuFxType = new Fx.Tween(this.elements.menubody, 'duration', this.options.animation); menuFxType.start('margin-top', 0, -this.innerHeight); } } else { this.elements.menu.addClass('js-igPushpane'); this.elements.closebutton.addClass('hide'); if (this.options.hideOnShow) { this.hideOnShow.each(function(elem) { elem.removeClass('hide'); }); } if (this.options.triggerAutoHide) { this.elements.trigger.removeClass('hide'); } } } }, updateInnerHeight: function() { this.innerHeight = this.elements.menubody.getSize().y; }, getMenu: function() { return this.elements.menu; } }); function fixBadTiming() { if (Igloo && Igloo.asset_igPushpane) { if (!window.igPaneObjects){//create array of igSlidepanes in the windows object window.igPaneObjects = new Hash(); } Object.each(Igloo.asset_igPushpane, function(object, id){ window.igPaneObjects.set(id,new igPushpane(id, null, object)); }); } window.pushpanesCreated = true; } window.addEvent('domready', function(){ fixBadTiming(); }); window.addEvent('bootstrap4finished', function() { if (window.dependents['igPushpane']) { // the old bootstrap4 attaches dataobj as a string, not a Hash // bootstrap4velo attaches the dataobj as a Hash, not a string. wheeee! if (!window.igPaneObjects){//create array of igSlidepanes in the windows object window.igPaneObjects = new Hash(); } window.dependents['igPushpane'].each(function(elem) { window.igPaneObjects.set(elem.id, new igPushpane(elem.id, null, elem.dataobj.igPushpane)); }); } }); /****************************************** igSlidepane by Dacheng Cheng Ideal use: For when you need an igDropdown that slides into and out of view, but never actually hides. Visibility is always visible, which won't break things like TinyMCE Features: Automatic positioning, to center the menu without going past the boundaries of the page (or userbar) Animation for open/closing the menu Mouseleave or Closebutton or click-away for hiding the menu a parentmenu option so that submenus will still close on click-away if the user clicks on parent ******************************************/ var igSlidepane = new Class({ Implements: [Options, Events], options:{ 'animation': -1, //time for animation of the sliding in/out - this is in miliseconds, and anything <= 0 == no animation 'menu': null, 'close': 'click', //can also be 'hover' or 'button' 'closebutton': null, 'childrenpreventclose':null, //array of elements 'parentmenu':null, //the parent menu, for clicking off - if this is not specified, then clicking on the parent element would not constitute a clicking off because the event for clicking on the parent (if it's an igSlidepane) has an event.stopPropagation 'highlight':null, 'closeexceptionclasses':[] }, initialize: function (trigger, menu, options) { // set the options this.setOptions(options || {}); this.elements = {}; // set the parameters this.elements.trigger = $(trigger); this.elements.menu = this.options.menu || menu; this.elements.menu = $(this.elements.menu); this.animating = false; if (this.elements.trigger && this.elements.menu ) { this.menucoords = this.elements.menu.getCoordinates(); this.elements.menu.addClass('js-igSlidepane'); this.elements.menu.removeClass('hide'); this.elements.menu.inject(document.body); //injecting it to the bottom of DOM this.elements.menubody = this.elements.menu.getChildren('.ig-slidepane-body')[0]; } this.isOpen = false; this.firstOpen = true; this.elements.trigger.addEvent('click', function (event) { event.preventDefault(); self.reposition(); self.toggle(); }); var self = this; switch (this.options.close) { case 'button':{ this.elements.closebutton = options.closebutton || $(options.closebutton); $(options.closebutton).addEvent('click', function () { self.hide(); }); break; } case 'hover': { this.elements.menu.addEvent('mouseleave', function () { if (!self.animating) { self.hide(); } }); break; } case 'click': default: { // however if they "click" on the document we want to hide the menu // if the user clicks on the menu prevent this from propagating to the document this.elements.menu.addEvent('click', function (event) { event.stopPropagation(); }); if (this.options.childrenpreventclose) { this.options.childrenpreventclose.each(function (ele) { var e = $(ele); e.addEvent('click', function (event) { event.stopPropagation();}); }); } if (this.options.parentmenu) { if ($(this.options.parentmenu)) { $(this.options.parentmenu).addEvent('click', function (event) { event.stopPropagation(); if (event.target != self.elements.trigger ) { self.hide(); } }); } } document.addEvent('click', function (event) { // deal with excepted elements var excepted = false; var clickedElem = event.target; Array.each(self.options.closeexceptionclasses, function(classId) { var parentMatches = $(clickedElem).getParents('.'+classId); if ($(clickedElem).hasClass(classId)) { parentMatches.push(new Element('span')); } if (parentMatches.length > 0) { excepted = true; } // compare the id of the clicked elem against the list, if we haven't found a class match. // This is for IE9 with modals, as IE9 loses all track of classes once a modal is closed, // and we can't do this stuff before the modal actually closes. if (!excepted) { if ($(clickedElem).id && $(clickedElem).id.toString().ciContains(classId)) { excepted = true } } }); if (event.target != self.elements.trigger && !excepted) {//&& !event.target.getParent().hasClass('swiff-uploader-box') if (self.isOpen && !self.animating) { self.hide(); } } if ($(event.target) != self.elements.trigger) { if ($(event.target).getParent('a') != self.elements.trigger) { if (self.options.highlight) self.elements.trigger.removeClass(self.options.highlight); } } }); //the key handler for closing this dropdown using 'esc' - this will only work if the close is by click (default) document.addEvent('keyup', function (event) { if (event.key == 'esc') { var targetTag = ($(event.target).tagName) ? $(event.target).tagName.toLowerCase() : ""; if (targetTag != 'input' && targetTag != 'textarea') { //if it's not an input... event.stopPropagation(); self.hide(); //close the igSlidepane return false; } else { var targetMatchedParents = (self.elements.menu.id) ? $(event.target).getParents('#'+self.elements.menu.id) : []; if (targetMatchedParents && targetMatchedParents.length > 0) { $(event.target).blur(); //lose focus from the input return false; } } } }); break; } } if (this.options.animation > 0) { //variables for animation this.innerHeight = this.elements.menubody.getSize().y; this.elements.menubody.setStyles({'position':'relative','top':-this.innerHeight}); this.reposition(); } }, reposition: function () { //repositions the thing to where the output is var origin = this.elements.trigger.getCoordinates(); var main = $('main'); //this is the main div, it is on most pages var menucoords = this.elements.menu.getCoordinates(); switch (this.options.align) { case 'middle': { var left = origin.left - (menucoords.width-origin.width)/2; break; } case 'right':{ var left = origin.left + origin.width - menucoords.width; break; } case 'left': default: { var left = origin.left; break; } } var top = origin.top+origin.height; if (main) { var borders = main.getCoordinates(); } else { var userbar = $('userbar'); //this is the userbar div var borders = userbar.getCoordinates(); } var far_right = borders.left+borders.width; var menu_right = origin.left + this.elements.menu.getSize().x; if (menu_right > far_right) { left += far_right - menu_right; } this.elements.menu.setStyles({ 'left':left, 'top':origin.top+origin.height }); }, toggle: function () { if (this.isOpen && !this.animating) { this.hide(); if (this.options.highlight) this.elements.trigger.removeClass(this.options.highlight); } else if (!this.isOpen && !this.animating) { this.show(); if (this.options.highlight) this.elements.trigger.addClass(this.options.highlight); } }, show: function () { var self = this; if (!this.isOpen) { this.isOpen = true; if (this.options.animation > 0 && !this.animating) { var menuFx = new Fx.Tween(this.elements.menubody,'duration',this.options.animation); this.elements.menubody.setStyle('top', -self.innerHeight); this.elements.menu.setStyle('height', null); //clear height this.elements.menu.removeClass('js-igSlidepane'); // Userbar only classes if (this.elements.menu.hasClass('ig-usermenu-closed')) { this.elements.menu.removeClass('ig-usermenu-closed'); this.elements.menu.addClass('ig-usermenu-open'); } menuFx.addEvents({ 'complete': function () { if (self.firstOpen) { self.fireEvent('FirstOpenComplete'); self.firstOpen = false; } self.elements.menubody.setStyle('top', null); self.animating = false; self.fireEvent('paneOpen'); }, 'start': function () { self.animating = true; if (self.firstOpen) { self.fireEvent('FirstOpen'); } document.fireEvent('slidepaneToggle'); } }); menuFx.start('top', -this.innerHeight,0 ); } else { this.elements.menu.removeClass('js-igSlidepane'); this.elements.menu.setStyle('height', null); this.fireEvent('paneOpen'); if (this.firstOpen) { this.fireEvent('FirstOpen'); this.firstOpen = false; } // Userbar only classes if (this.elements.menu.hasClass('ig-usermenu-closed')) { this.elements.menu.removeClass('ig-usermenu-closed'); this.elements.menu.addClass('ig-usermenu-open'); } } } else { // this is open. Let everyone know that. this.fireEvent('paneOpen'); } }, hide: function () { var self = this; if (this.isOpen && !this.isHiding) { this.isOpen = false; if (this.options.animation > 0 && !this.animating) { this.innerHeight = this.elements.menu.getSize().y; var menuFx = new Fx.Tween(this.elements.menubody,'duration',this.options.animation); this.elements.menubody.setStyle('position','relative'); this.animating = true; menuFx.addEvent('complete', function () { self.elements.menu.addClass('js-igSlidepane'); self.fireEvent('hide'); self.animating = false; // Userbar only classes if (self.elements.menu.hasClass('ig-usermenu-open')) { self.elements.menu.removeClass('ig-usermenu-open'); self.elements.menu.addClass('ig-usermenu-closed'); } }); menuFx.addEvent('start', function() { document.fireEvent('slidepaneToggle'); }); menuFx.start('top', 0, -this.innerHeight); } else { this.elements.menu.addClass('js-igSlidepane'); this.fireEvent('hide'); // Userbar only classes if (this.elements.menu.hasClass('ig-usermenu-cloesd')) { this.elements.menu.removeClass('ig-usermenu-closed'); this.elements.menu.addClass('ig-usermenu-open'); } } } }, updateInnerHeight: function () { this.innerHeight = this.elements.menubody.getSize().y; }, getMenu: function () { return this.elements.menu; } }); window.addEvent('domready', function () { if (Igloo && Igloo.asset_igSlidepane) { if (!window.igPaneObjects) {//create array of igSlidepanes in the windows object window.igPaneObjects = new Hash(); } var menu; Object.each(Igloo.asset_igSlidepane, function (object, id) { window.igPaneObjects.set(id, new igSlidepane(id, null, object)); }); } }); window.addEvent('bootstrap4finished', function () { if (window.dependents['igSlidepane']) { if (!window.igPaneObjects) { window.igPaneObjects = new Hash(); } // the old bootstrap4 attaches dataobj as a string, not a Hash // bootstrap4velo attaches the dataobj as a Hash, not a string. wheeee! window.dependents['igSlidepane'].each(function (elem) { window.igPaneObjects.set(elem.id, new igSlidepane(elem.id, null, elem.dataobj.igSlidepane)); }); } }); function applyMultiaccordion(elements){ Igloo.accordions = new Hash(); elements.each(function(elem){ // if the element doesn't have an id, give it one. if (!elem.id){ elem.id = 'multiaccordioncontainer' + Number.random(1, 999999999); } var panels = elem.getElements('.js-accordion-panels'); var accordion = new Accordion('#' + elem.id + ' .js-accordion-trigger','#' + elem.id + ' .js-accordion-panel',{ alwaysHide: true, opacity: false, start: 'all-closed', activePanel: null, onStart: function() { if (this.panelopen) { window.fireEvent('accordionOpening', activePanel); } else { window.fireEvent('accordionClosing', activePanel); } }, onActive: function(toggler, element){ toggler.getParent().getParent().addClass('active'); panels.setStyles({display:'block'}); // override display:none in CSS element.setStyles({display:'block'}); // override display:none in CSS activePanel = element; this.panelopen = true; }, onBackground: function(toggler, element) { if(toggler){ toggler.getParent().getParent().removeClass('active'); if (element.getElements('input.js-defaultfocus')[0]){ element.getElements('input.js-defaultfocus')[0].blur(); } } }, onComplete: function() { if(activePanel && !activePanel.hasClass('js-noscrollto')) { if (activePanel.getElements('input.js-defaultfocus')[0] && activePanel.getStyle('height').toInt() > 0){ activePanel.getElements('input.js-defaultfocus')[0].select(); } // if the panel goes below the fold, do some scrolling. var coo = activePanel.getCoordinates(); var winheight = Window.getCoordinates()['height']; var buffer = 60; if(coo['top'] + coo['height'] + buffer > winheight + Window.getScroll().y){ scrollto = coo['top']; if(coo['height'] < winheight){ scrollto = coo['top'] - (winheight - coo['height']) + buffer; } var scroll = new Fx.Scroll(window, { wait: false, duration: 500, offset: {'x': 0,'y':0}, transition: Fx.Transitions.Cubic.easeInOut }); scroll.start(0,scrollto); } } if (this.panelopen) { // this event will always fire when the panel has finished opening. // if you are just switching between panels, it will fire. window.fireEvent('accordianOpened', activePanel); this.panelopen = false; } else { // this event will always fire when the panel has finished closing. // if you are just switching between panels, it will NOT fire. window.fireEvent('accordianClosed', activePanel); } } }); Igloo.accordions.set(elem.id, accordion); // give elem a reference to the accordion elem.accordioncontroller = accordion; // find default open pos var triggers = elem.getElements('.js-accordion-trigger'); var defaultopenpos = null; var posi = 0; triggers.each(function(trigger){ // disable href on anchor trigger.addEvent('click', function(e) { if (!$(trigger).hasClass('js-accordion-noprevent')) { e = new Event(e).preventDefault(); } }); if(trigger.hasClass('js-accordion-defaultopen')){ defaultopenpos = posi; } posi++; }); if (defaultopenpos != null){ accordion.display(defaultopenpos); } var closers = elem.getElements('.js-accordion-closer'); closers.each(function(closer){ closer.onclick = function(){ accordion.display(-1); } }); // unhide the panels - CSS sets visibility to hidden to avoid ze flash elem.getElements('.js-accordion-panel').each(function(panel){ panel.style.visibility = 'visible'; panel.style.display = 'block'; }); }); } window.addEvent('domready', function(){ if( Igloo && Igloo.asset_multiaccordion ){ var elements = []; Object.each(Igloo.asset_multiaccordion, function(object, id){ var elem = $(id); if( elem ){ elements.push(elem); } }); applyMultiaccordion(elements); } }); window.addEvent('bootstrap4finished', function(){ var elements = window.dependents['multiaccordion'] || []; applyMultiaccordion(elements); }); var ShowHide = new Class({ Implements: [Options], options: { element: null, container: '', groupname: '', triggerhidingclass: 'triggerhidingclass', triggershowingclass: 'triggershowingclass', toggletext: null }, initialize: function (elem, options) { var self = this; this.setOptions(options); elem.addEvent('click', function(event) { event.stop(); if (self.options.container) { var groupItems = ($$('#' + self.options.container + ' .' + self.options.groupname)); } else { var groupItems = ($$('.' + self.options.groupname)); } var action = (groupItems[0].hasClass('ig-hidden') || groupItems[0].hasClass('hide')) ? 'hide' : 'show'; // Adjust Class if (action == 'hide') { // Show elem.removeClass(self.options.triggershowingclass); elem.addClass(self.options.triggerhidingclass); } else { // Hide elem.removeClass(self.options.triggerhidingclass); elem.addClass(self.options.triggershowingclass); } // Toggle Hide (default to 'hide' class) var hideClass = (groupItems[0].hasClass('ig-hidden')) ? 'ig-hidden' : 'hide'; groupItems.toggleClass(hideClass); // Toggle Text if (self.options.toggletext) { var oldtext = elem.innerHTML; var newtext = self.options.toggletext; elem.set('html', newtext); self.options.toggletext = oldtext; } return false; }); } }); window.addEvent('domready', function(){ if (Igloo.asset_showhide) { Object.each(Igloo.asset_showhide, function (options, id) { var elem = $(id); if (elem) { elem.ShowHide = new ShowHide(elem, options.showhide); } }); } });