c.log(text);\r
}\r
}\r
-}\r
-\r
-\r
-function PopupDiv(){\r
- var $J = jQuery;\r
- var me = this;\r
- var data = arguments.length && arguments[0] || {};\r
- \r
- //var wdw = $J(window);\r
- var div = $J('<div/>');\r
- var header = $J('<div/>').append($J('<input/>').attr('type','text')).append($J('<a/>').attr('href','#').click(function(){\r
- me.close();\r
- return false;\r
- })); //.css('float','right');\r
- var container = $J('<div/>').css('overflow','auto');\r
- var footer = $J('<div/>').css({\r
- 'textAlign':'right'\r
- }).append($J('<a/>').attr('href','#').click(function(){\r
- me.trigger('ok',true);\r
- return false;\r
- }));\r
- header.find('*').add(footer.find('*')).css('display','none');\r
- div.append(header).append(container).append(footer);\r
- //defining immediately the method getDiv (because it is used below)\r
- this.getDiv = function(){\r
- return div;\r
- }\r
- //setting functions:\r
- \r
- var listeners = {};\r
- this.getListeners = function(){\r
- return listeners;\r
- }\r
-\r
- var k;\r
- //setting static properties, if any:\r
- for(k in PopupDiv){\r
- this.__proto__[k] = PopupDiv[k];\r
- // consolelog(k+' '+PopupDiv[k]);\r
- delete PopupDiv[k];\r
- }\r
-\r
- //setting instance-specific properties:\r
- for(k in data){\r
- if(k == 'onOk' || k == 'onShow' || k == 'onClose'){\r
- this.bind(k.substring(2).toLowerCase(),data[k]);\r
- }else if(k == 'content'){\r
- this.setContent(data[k]);\r
- }else {\r
- this[k] = data[k];\r
- }\r
- }\r
-\r
- if(!this.popupCss){\r
- this.popupCss = {}; //workaround to update css the first time we call show\r
- //note that {} evaluates to true, but jQueryElement.css({}) is harmless\r
- }\r
- \r
-\r
-}\r
-\r
-\r
-(function(p){\r
- //private static variables\r
- var $ = jQuery;\r
- var w_ = window;\r
- var d_ = document;\r
- var wdw = $(w_);\r
- var popupStaticId = 'popup_'+(new Date().getTime());\r
- //var doc = $(d_);\r
-\r
-\r
- //in the functions below, this refers to the new Popup instance, not to the prototype\r
-\r
-\r
-\r
- p.isClickElement = function(element){\r
- return element && element.length==1 && element instanceof $ && element[0] !== w_ && element[0] !== d_ &&\r
- (element.is('a') || element.is('input[type=button]') || element.is('button') ||\r
- element.is('input[type=submit]'));\r
- };\r
-\r
- p.getId = function(){\r
- var div = this.getDiv();\r
- if(!(div.attr('id'))){\r
- div.attr('id',popupStaticId+'_'+(new Date().getTime()));\r
- }\r
- return div.attr('id');\r
- };\r
-\r
-\r
- //default properties which can be overridden\r
- p.shadowOffset = 4;\r
- p.invoker = wdw;\r
- p.bounds = {\r
- 'top':0.25,\r
- 'left':0.25,\r
- 'right':0.25,\r
- 'bottom':0.25\r
- }; //note that sepcifying top+bottom>=1 there is undefined behaviour (in chrome, offset is set but height takes all available space)\r
- p.boundsexact = false;\r
- p.popupClass = '';\r
- p.popupCss = {};\r
- p.showok = false;\r
- p.showclose=false;\r
- p.title = "";\r
- p.okButtonClass = '';\r
- p.closeButtonClass = '';\r
- p.okButtonTitle = 'Ok';\r
- p.closeButtonTitle = 'x';\r
- p.defaultCloseOperation = 'hide';\r
- p.focusable = false;\r
- p.fadInTime = 'fast',\r
- p.fadeOutTime = 0,\r
- p.shadowOpacity = 0.25;\r
- p.zIndex = 10000;\r
- // p.listItemClass = '';\r
-\r
- p.getFormData = function(){\r
- var elms = this.find('input,select,textarea');\r
- var ret = {};\r
- elms.each(function(i,e){\r
- var ee = $(e);\r
- var key = ee.attr('name');\r
- if(key){\r
- ret[key] = ee.val();\r
- }\r
- });\r
- return ret;\r
- };\r
-\r
- p.closeLater = function(millseconds){\r
- var me = this;\r
- setTimeout(function(){\r
- me.close();\r
- },millseconds);\r
- },\r
-\r
- //methods:\r
- p.find = function(argumentAsInJQueryFind){\r
- return $(this.getDiv().children()[1]).find(argumentAsInJQueryFind);\r
- };\r
-\r
- p.bind = function(eventName, callback){ //eventname: show, close or ok\r
- var listeners = this.getListeners();\r
- if(eventName in listeners){\r
- listeners[eventName].push(callback);\r
- }else{\r
- listeners[eventName] = [callback];\r
- }\r
- };\r
-\r
-\r
- p.unbind = function(eventName){\r
- var listeners = this.getListeners();\r
- if(eventName && eventName in listeners){\r
- delete listeners[eventName];\r
- }else if(!eventName){\r
- for(var k in listeners){\r
- delete listeners[k];\r
- }\r
- }\r
- };\r
-\r
- p.trigger = function(eventName){\r
- var listeners = this.getListeners();\r
- var me = this;\r
- if(eventName in listeners){\r
- var callbacks = listeners[eventName];\r
- var i = 0;\r
- if(eventName == 'ok'){\r
- var data = this.getFormData();\r
- for(i=0; i<callbacks.length; i++){\r
- callbacks[i].apply(me,[data]);\r
- }\r
- if(arguments.length>1 && arguments[1]){\r
- //workaround to remove listeners on close:\r
- if('close' in listeners){\r
- var v = listeners['close'];\r
- delete listeners['close'];\r
- this.close();\r
- listeners['close'] = v;\r
- }else{\r
- this.close();\r
- }\r
-\r
- }\r
- }else{\r
- for(i=0; i<callbacks.length; i++){\r
- callbacks[i].apply(me);\r
- }\r
- }\r
- }\r
- };\r
-\r
- p.setContent = function(content){\r
- var div = this.getDiv();\r
- var container = $($(div).children()[1]);\r
- //div.appendTo('body'); //necessary to properly display the div size\r
- container.empty();\r
-\r
- if(content instanceof $){\r
- container.append(content);\r
- }else if(content instanceof Array){\r
- var jQ = $;\r
- var me = this;\r
- //var name = this.getListItemName();\r
- var input = $('<input/>').attr('type','hidden').attr('name','selIndex');\r
- var setEvents = function(idx,anchor,input){\r
- anchor.click(function(){\r
- input.val(idx);\r
- me.trigger('ok',true);\r
- return false;\r
- }).focus(function(){ //focus because we need to get the value if ok is present\r
- input.val(idx);\r
- })\r
- };\r
- for(var h=0; h<content.length; h++){\r
- var item = content[h];\r
- var a = $('<a/>').attr('href','#');\r
- if('class' in item){\r
- a.addClass(item['class']);\r
- }\r
- if('html' in item){\r
- a.html(item['html']);\r
- }\r
- if('name' in item){\r
- a.attr('name', item['name']);\r
- }\r
- if('id' in item){\r
- a.attr('id', item['id']);\r
- }\r
- if('css' in item){\r
- a.css(item['css']);\r
- }\r
- a.css({\r
- 'display':'block',\r
- 'margin':'2px'\r
- }); //margin is used to display the outline (focus)\r
- setEvents(h,a,input);\r
- container.append(a);\r
- }\r
- container.append(input);\r
- }else if(content && content.constructor == Object){\r
- var leftElements = $([]);\r
- var rightElements = $([]);\r
- var maxw = [0,0];\r
- var insert = function(e1,e2){\r
- var lineDiv = $('<div/>');\r
- if(!e2){\r
- e2=e1;\r
- e1 = $('<span/>');\r
- }\r
- rightElements = rightElements.add(e2);\r
- leftElements = leftElements.add(e1);\r
- container.append(lineDiv.append(e1).append(e2));\r
- return lineDiv;\r
- }\r
- var title, component;\r
-\r
- var max = Math.max; //instantiate once\r
- var lineDiv = undefined;\r
- var lineDivs = $([]);\r
- for(var k in content){\r
- var val = content[k];\r
- if(typeof val == 'string' || typeof val == 'number'){\r
- title = $('<span/>').html(k);\r
- maxw[0] = max(maxw[0],k.length);\r
- maxw[1] = max(maxw[1],val.length);\r
- component = $('<input/>').attr('type','text').val(val).attr('name',k);\r
- lineDivs = lineDivs.add(insert(title,component));\r
- }else if(val === true || val === false){\r
- var id = this.getId()+"_checkbox";\r
- title = $('<input/>').attr('type','checkbox').attr('name',k).attr('id',id);\r
- if(val){\r
- title.attr('checked','checked');\r
- }else{\r
- title.removeAttr('checked');\r
- }\r
- component = $('<label/>').attr('for',id).html(k);\r
- maxw[1] = max(maxw[1],k.length);\r
- lineDivs = lineDivs.add(insert($('<span/>').append(title),component));\r
- }else if(val instanceof Array){\r
- title = $('<span/>').html(k);\r
- maxw[0] = max(maxw[0],k.length);\r
- component = $('<select/>').attr('size',1);\r
- for(var i=0; i< val.length; i++){\r
- component.append($('<option/>').val(val[i]).html(val[i]));\r
- maxw[1] = max(maxw[1],val[i].length);\r
- }\r
- lineDivs = lineDivs.add(insert(title,component));\r
- }\r
- if(lineDiv){\r
- lineDiv.css('marginBottom','1ex');\r
- }\r
- }\r
- lineDivs.css({\r
- 'white-space': 'nowrap',\r
- 'marginBottom':'0.5ex'\r
- });\r
- //last div erase marginBottom\r
- $(lineDivs[lineDivs.length-1]).css('marginBottom','');\r
-\r
-\r
- //display: inline-block below assures that width are properly set\r
- //IE 6/7 accepts the value only on elements with a natural display: inline.\r
- //(see http://www.quirksmode.org/css/display.html#t03)\r
- //span and anchors are among them\r
- //(see http://www.maxdesign.com.au/articles/inline/)\r
- leftElements.add(rightElements).css({\r
- 'display':'inline-block',\r
- 'margin':'0px',\r
- 'padding':'0px'\r
- });\r
- leftElements.css({\r
- 'textAlign':'right',\r
- 'marginRight':'0.5ex',\r
- 'width':Math.round((3/5)*maxw[0])+'em'\r
- });\r
- rightElements.css({\r
- 'width':Math.round((3/5)*Math.max(maxw[0], maxw[1]))+'em'\r
- }); //might be zero if default values are all ""\r
- }else{\r
- container.append(""+content);\r
- }\r
-\r
- };\r
-\r
- p.setFocusCycleRoot = function(value){\r
- //var value = this.focusable;\r
- var popup = this.getDiv();\r
- var focusAttr = this.getFocusAttr();\r
- var invokerIsClickable = this.isClickElement(this.invoker);\r
- var children_ = popup.children();\r
- var topDiv = $(children_[0]);\r
- var centralDiv = $(children_[1]);\r
- var bottomDiv = $(children_[2]);\r
- var elementsWithFocus = centralDiv.find('input[type!=hidden],select,textarea,a'); //input:not(:hidden),select,textarea,a,\r
-\r
- var ret = elementsWithFocus.length ? $(elementsWithFocus[0]) : popup;\r
- if(this.showclose){\r
- elementsWithFocus =elementsWithFocus.add(topDiv.find('a'));\r
- }\r
- if(this.showok){\r
- elementsWithFocus = elementsWithFocus.add(bottomDiv.find('a'));\r
- }\r
- elementsWithFocus = elementsWithFocus.add(popup);\r
- var focusNameSpace = "blur."+this.getId();\r
- if(!value){\r
- elementsWithFocus.each(function(i,elm){\r
- $(elm).unbind(focusNameSpace).removeAttr('tabindex').removeAttr(focusAttr);\r
- });\r
- this.getFirstFocusableElement = function(){\r
- return undefined;\r
- }\r
- if(invokerIsClickable){\r
- this.invoker.removeAttr('tabindex').removeAttr(focusAttr);\r
- }\r
- return;\r
- }\r
- if(invokerIsClickable){\r
- this.invoker.attr('tabindex',0).attr(focusAttr,'true');\r
- }\r
- var doc_ = d_; //closure (see nested function below)\r
-\r
- //now all elements (including header and footer)\r
-\r
- var me = this;\r
- //bind the blur to each focusable element:\r
- elementsWithFocus.each(function(i,e){\r
- var ee = $(e);\r
- ee.attr(focusAttr,'true');\r
- ee.attr('tabindex',i+1);\r
- ee.unbind(focusNameSpace).bind(focusNameSpace,function(){\r
- //wait 250msec to see if the focus has been given to another popup focusable element: if yes, do nothing\r
- //otherwise execute callback\r
- setTimeout(function(){\r
- var v = doc_.activeElement;\r
- consolelog(v);\r
- if((v && $(v).attr(focusAttr)) || me.isClosing){\r
- //if we are closing, we will call back this method which removes the focus attributes, bt meanwhile the\r
- //timeout should execute\r
- return;\r
- }\r
-\r
- me.close();\r
- },200)\r
- }); //set here another time delay. 300 seems to be the good compromise between visual hide and safetiness that\r
- //meanwhile the focus has already been given to the next component\r
- });\r
- this.getFirstFocusableElement = function(){\r
- return ret;\r
- }\r
- };\r
- p.getFirstFocusableElement = function(){\r
- return undefined;\r
- };\r
- p.show = function(){\r
- var div = this.getDiv();\r
- var me = this;\r
-\r
-\r
- var cssModified = (this.popupClass || this.popupCss);\r
- if(this.popupClass){\r
- //which might be the prototype\r
- div.removeClass().addClass(this.popupClass);\r
- this.popupClass = ''; //override prototype property\r
- }\r
- if(this.popupCss){\r
- //which might be the prototype\r
- div.css(this.popupCss);\r
- this.popupCss = ''; //override prototype property\r
- }\r
- //css modified, restore properties we need to be restored:\r
- if(cssModified){\r
- div.css({\r
- 'position':'absolute',\r
- 'zIndex':this.zIndex,\r
- 'margin':'0px',\r
- 'overflow':'hidden'\r
- });\r
- }\r
-\r
- //if we have elements of type listitem, add the specified class\r
- // var name = this.getListItemName();\r
- // var elms = this.find('a[name='+name+']');\r
- // if(this.listItemClass){\r
- // elms.removeClass().addClass(this.listItemClass);\r
- // this.listItemClass = "";\r
- // }\r
- // elms.css('display','block');\r
-\r
-\r
- this.setFocusCycleRoot(this.focusable);\r
- this.setSizable();//this means the popupdiv is display: !none and visibility:hidden, so every element\r
- //inside it should be visible and therefore sizable. Being visible means that jQuery.is(':visible') returns true\r
- //start with showing top and bottom if some elements are visible\r
-\r
- var subdiv = div.children();\r
-\r
- //configure buttons. Text and classes are added here cause might have been changed\r
- var topDiv = $(subdiv[0]);\r
- var titleInput = topDiv.find(':text'); //$(':text') is equivalent to $('[type=text]') (selects all <input type="text"> elements)\r
- var closeBtn = topDiv.find('a');\r
- //if title doesn't exist or is hidden, set them to undefined (same for closeBtn).\r
- //to check visibility, we dont use is(:visible) cause apparently\r
- //width and height as zero mean hidden (I think that is the cause):\r
- titleInput = titleInput.length ? $(titleInput.get(0)) : undefined;\r
- closeBtn = closeBtn.length ? $(closeBtn.get(0)) : undefined;\r
- if(this.showclose || this.title){\r
- topDiv.css('paddingBottom','0.5ex').show(); //add padding to bottom\r
- //is(.. return true if at least one of these elements matches the given arguments.\r
- if(closeBtn){\r
- if(this.closeButtonClass){\r
- closeBtn.addClass(this.closeButtonClass);\r
- }\r
- if(this.closeButtonTitle){\r
- closeBtn.html(this.closeButtonTitle);\r
- }\r
- closeBtn.css({\r
- 'display':'inline-block'\r
- }); //in order to set width and height on the element\r
- }\r
- if(titleInput){\r
- titleInput.val(this.title).css({\r
- 'display':'inline-block',\r
- 'padding':'0',\r
- 'margin':'0',\r
- 'border':'0',\r
- 'backgroundColor':'transparent',\r
- 'marginRight' : '1ex'\r
- }).attr('readonly','readonly');\r
- }\r
- }else{\r
- topDiv.css('padding','0px');\r
- }\r
-\r
- var bottomDiv = $(subdiv[2]);\r
- //do the same as above, ok must exist and be visible, otherwise is undefined\r
- var okButton = bottomDiv.find('a');\r
- okButton = okButton.length ? $(okButton.get(0)) : undefined;\r
-\r
- //see note above about why we dont use okButton.is(':visible')\r
- if(this.showok){\r
- bottomDiv.css('paddingTop','0.5ex').show(); //add padding to bottom\r
- //is(.. return true if at least one of these elements matches the given arguments.\r
- if(okButton){\r
- if(this.okButtonClass){\r
- okButton.addClass(this.okButtonClass);\r
- }\r
- if(this.okButtonTitle){\r
- okButton.html(this.okButtonTitle);\r
- }\r
- okButton.css('display','inline-block'); //in order to set width and height on the element\r
- }\r
- }else{\r
- bottomDiv.css('paddingTop','0px');\r
- }\r
-\r
- var centralDiv = $(subdiv[1]);\r
- //reset properties of the central div\r
- centralDiv.css({\r
- 'maxHeight':'',\r
- 'maxWidth':'',\r
- 'minHeight':'',\r
- 'minWidth':'',\r
- 'height':'',\r
- 'width':''\r
- });\r
-\r
-\r
- var invoker = this.invoker;\r
-\r
- var sizeAsPopup = false;\r
- if(this.isClickElement(invoker)){\r
- this.setBoundsAsPopup(invoker, true);\r
- sizeAsPopup = true;\r
- //storing click events, when showing clicking on an event must give the focus to the popup\r
- //old handlers will be restored in close()\r
- this['_tmpHandlers'+this.getId()] = undefined;\r
- var focusElm = this.getFirstFocusableElement();\r
- if(focusElm){\r
- var oldHandlers = [];\r
- var type = 'click';\r
- var clickEvents =invoker.data("events")[type];\r
- $.each(clickEvents, function(key, value) {\r
- oldHandlers.push(value);\r
- })\r
- invoker.unbind(type); //remove (temporarily) the binding to the event.\r
- //for instance, if we show the popup by clicking invoker, when the popup is shown do nothing\r
- //on clicking invoker until popup.hide is called\r
-\r
- this['_tmpHandlers'+this.getId()] = oldHandlers;\r
- invoker.unbind(type).bind(type,function(evt){\r
- //let the invoker have focus and let it be recognized as an element which does not blur the popup:\r
- //invoker.attr('tabindex',0).attr(focusAttr,'true');\r
- if(div.length && div.is(':visible')){\r
- focusElm.focus();\r
- return false;\r
- }\r
- //something wrong: close the popup and restore the hanlers\r
- me.close.apply(me);\r
- return false;\r
- });\r
- }\r
-\r
- }else{\r
- this.setBoundsInside(invoker, this.bounds, this.boundsexact, true);\r
- }\r
-\r
-\r
-\r
- //set central div max height ONLY IF NECESSARY:\r
- var maxHeight = (div.height()-topDiv.outerHeight(true)-bottomDiv.outerHeight(true)-\r
- (centralDiv.outerHeight(true)-centralDiv.height()));\r
- var height = centralDiv.height();\r
- if(sizeAsPopup && maxHeight<height){\r
- centralDiv.css('maxHeight',maxHeight+'px');\r
- }else{\r
- centralDiv.css({\r
- 'maxHeight': maxHeight+'px',\r
- 'minHeight': maxHeight+'px'\r
- });\r
- }\r
- //set central div max width ONLY IF NECESSARY:\r
- var maxWidth = div.width();\r
- var width = $(subdiv[1]).outerWidth(true);\r
- if(sizeAsPopup && maxWidth<width){\r
- centralDiv.css('maxWidth',maxWidth+'px');\r
- }else{\r
- centralDiv.css({\r
- 'maxWidth': maxWidth+'px',\r
- 'minWidth':maxWidth+'px'\r
- });\r
- }\r
-\r
- //set title and close button to span whole width, if necessary\r
- if(titleInput || closeBtn){\r
- var titleW = topDiv.width() - (closeBtn ? closeBtn.outerWidth(true) : 0) - (titleInput ? titleInput.outerWidth(true)-titleInput.width() : 0);\r
- if(titleInput){\r
- titleInput.css('width',titleW+'px');\r
- }\r
- }\r
-\r
- //creating shadow. REmove attributes tabindex (unnecessary) and especially focusAttr,\r
- //so that clicking tab key and setting the shadow focusable hides the popup. If one wants the shadow not to hide the popup. keep\r
- //focusAttr BUT insert shadow in the focus cycle root (see method)\r
- var shadow = div.clone(false,false).empty().css({\r
- 'backgroundColor':'#000',\r
- 'borderColor':'#000',\r
- 'visibility':'visible',\r
- 'zIndex':this.zIndex-1\r
- }).removeAttr('tabindex').removeAttr(this.getFocusAttr()).fadeTo(0,0).\r
- attr('id',this.getShadowDivId()) //for use in hide\r
- .insertAfter(div);\r
-\r
-\r
- var postShowFcn = function(){\r
- me.trigger('show');\r
- var rect = me.getBounds.apply(me);\r
- shadow.css({\r
- 'left':(rect.x + me.shadowOffset)+'px',\r
- 'top':(rect.y + me.shadowOffset)+'px',\r
- 'width':(rect.width)+'px',\r
- 'height':(rect.height)+'px'\r
- }).fadeTo(me.fadInTime,me.shadowOpacity, function(){\r
- var v = me.getFirstFocusableElement();\r
- if(v){\r
- v.focus();\r
- }\r
- });\r
- }\r
-\r
- div.hide().css('visibility','visible').show(this.fadInTime,function(){\r
-\r
- postShowFcn();\r
-\r
-\r
- });\r
- };\r
-\r
- p.setBoundsAsPopup = function(popupInvoker, isSizable){\r
- var invoker = popupInvoker;\r
- var div = this.getDiv();\r
- var oldCss= isSizable ? undefined : this.setSizable();\r
-\r
- var windowRectangle = this.getBoundsOf(wdw); //returns the window rectangle\r
-\r
- var invokerOffset = invoker.offset();\r
- var invokerOuterHeight = invoker.outerHeight();\r
-\r
- //first set the maxwidth,so that we can compute the height according to it:\r
- this.setMaxSize({\r
- width: wdw.scrollLeft()+wdw.width()-invokerOffset.left\r
- },isSizable);\r
-\r
-\r
- var spaceAbove = invokerOffset.top - windowRectangle.y;\r
- var spaceBelow = windowRectangle.height - invokerOuterHeight - spaceAbove;\r
-\r
- var placeAbove = spaceAbove > spaceBelow && div.outerHeight(true) > spaceBelow;\r
- //note that div.outerHeight() should be == div.outerHeight(true), as we set margins =0\r
-\r
- // alert(div.outerHeight(true)+' '+spaceAbove+' '+spaceBelow);\r
- // div.css('visibility','');\r
- // return;\r
-\r
- this.setMaxSize({\r
- height : (placeAbove ? spaceAbove : spaceBelow)\r
- },isSizable); //width will be ignored (for the moment)\r
- //decrement of one pixel cause when the popup has to be reduced and the shadows bounds "touch" the window right or bottom sides,\r
- //the window scrolls (and it shouldn't)\r
-\r
- //setting the minimum size to the invoker width, minheight the same as maxHeight (see above)\r
- this.setMinSize({\r
- width: invoker.outerWidth()+this.shadowOffset //workaround to NOT consider the shadow, as offset below substracts the shadow\r
- //height : spaceAbove>spaceBelow ? spaceAbove : spaceBelow //why this? because if we click the popup a\r
- //computed height CH seems to be set. At subsequent popup show, CH will be the same UNLESS a new maxHeight lower than CH is set\r
- //however, we want CH to change even if a new maxHeight greater than CH is set\r
- },isSizable);\r
-\r
- //setting the top and left. This must be done at last because popupDiv.outerHeight(true)\r
- //must have been computed according to the height set above...\r
- this.offset({\r
- 'left': invokerOffset.left,\r
- 'top': (placeAbove ? invokerOffset.top - div.outerHeight(true) :\r
- invokerOffset.top + invokerOuterHeight)\r
- },isSizable);\r
- if(oldCss){\r
- div.css({\r
- 'display':oldCss['display'],\r
- 'visibility':oldCss['visibility']\r
- });\r
- }\r
- };\r
- //places and resize the popupdiv inside parent\r
- //padding is a dict {top:,left:,bottom:..,right:,...} measuring the distance of the popupdiv from the corners, so that\r
- //padding={top:0.25,left:0.25,bottom:0.25,right:0.25} will place the popupdiv at the center of parent\r
- //padding={top:25,left:25,bottom:25,right:25} will place the popupdiv at distances 25 px from parent sides\r
- //in other words, padding keys lower or euqals to 1 will be conbsidered as percentage, otherwise as absolute measures in px\r
- p.setBoundsInside = function(parent, pd, boundsExact, isSizable){\r
-\r
- var div = this.getDiv();\r
- var oldCss = isSizable ? undefined : this.setSizable();\r
-\r
- var bounds = this.getBoundsOf(parent);\r
-\r
- var x=bounds.x;\r
- var y = bounds.y;\r
- var w = bounds.width\r
- var h = bounds.height;\r
- var pInt = parseInt;\r
- //rebuilding:\r
-\r
- var padding = {\r
- top: pd['top'],\r
- left:pd['left'],\r
- bottom:pd['bottom'],\r
- right:pd['right']\r
- };\r
-\r
- for(var k in padding){\r
- if(padding[k]<=0){\r
- padding[k]=0;\r
- }else if(padding[k]<=1){\r
- padding[k] = k=='top' || k =='bottom' ? h*padding[k] : w*padding[k];\r
- }else{\r
- padding[k] = pInt(padding[k]);\r
- }\r
- }\r
-\r
- var maxSize = {\r
- 'width':w-padding['left']-padding['right']+this.shadowOffset,\r
- 'height':h-padding['top']-padding['bottom']+this.shadowOffset\r
- };\r
-\r
- var vvvv ={\r
- width:maxSize.width,\r
- height:maxSize.height\r
- };\r
- this._convertSize(div, vvvv);\r
-\r
- if(boundsExact){\r
- this.setMinSize({\r
- width:maxSize.width,\r
- height:maxSize.height\r
- },isSizable); //a copy cause the argument will be modified\r
- this.setMaxSize({\r
- width:maxSize.width,\r
- height:maxSize.height\r
- }, isSizable); //a copy cause the argument will be modified\r
-\r
- this.offset({\r
- 'left':x + padding['left'],\r
- 'top': y + padding['top']\r
- },isSizable);\r
- }else{\r
- this.setMaxSize({\r
- width:maxSize.width,\r
- height:maxSize.height\r
- },isSizable); //a copy cause the argument will be modified\r
- var spanLeft = maxSize.width - div.outerWidth(true);\r
- var spanTop = maxSize.height - div.outerHeight(true);\r
- this.offset({\r
- 'left':x + padding['left'] + (spanLeft > 0 ? spanLeft/2 : 0),\r
- 'top': y + padding['top'] +(spanTop > 0 ? spanTop/2 : 0)\r
- },isSizable);\r
-\r
- }\r
- //convert to percentage in order to keep same dimensions when zooming\r
-\r
-\r
- if(oldCss){\r
- div.css({\r
- 'display':oldCss['display'],\r
- 'visibility':oldCss['visibility']\r
- });\r
- }\r
- };\r
- p.getBounds = function(){\r
- return this.getBoundsOf(this.getDiv());\r
- };\r
- //TODO: change argument\r
- p.getBoundsOf = function(jQueryElement){\r
- var ret = {\r
- x:0,\r
- y:0,\r
- width:0,\r
- height:0\r
- };\r
- if(!jQueryElement || !(jQueryElement instanceof $)){\r
- jQueryElement = wdw;\r
- }\r
- if(jQueryElement[0] === w_){\r
- ret.x = jQueryElement.scrollLeft();\r
- ret.y = jQueryElement.scrollTop();\r
- }else{\r
- var offs = jQueryElement.offset();\r
- ret.x = offs.left;\r
- ret.y = offs.top;\r
- }\r
- ret.width = jQueryElement.width();\r
- ret.height = jQueryElement.height();\r
- return ret;\r
- };\r
-\r
- p.setMaxSize = function(size, isSizable){\r
- var div = this.getDiv();\r
- var oldCss = isSizable ? undefined : this.setSizable();\r
-\r
- this._convertSize(div, size);\r
- var css = {};\r
- if('width' in size){\r
- css.maxWidth = size.width+'px';\r
- }\r
- if('height' in size){\r
- css.maxHeight = size.height+'px';\r
- }\r
- if(css){\r
- div.css(css);\r
- }\r
- if(oldCss){\r
- div.css({\r
- 'display':oldCss['display'],\r
- 'visibility':oldCss['visibility'],\r
- 'top':oldCss['top'],\r
- 'left':oldCss['left']\r
- });\r
- }\r
- return size;\r
- };\r
- p.setMinSize = function(size, isSizable){\r
- var div = this.getDiv();\r
- var oldCss= isSizable ? undefined : this.setSizable();\r
-\r
- this._convertSize(div, size);\r
- var css = {};\r
- if('width' in size){\r
- css.minWidth = size.width+'px';\r
- }\r
- if('height' in size){\r
- css.minHeight = size.height+'px';\r
- }\r
- if(css){\r
- div.css(css);\r
- }\r
- if(oldCss){\r
- div.css({\r
- 'display':oldCss['display'],\r
- 'visibility':oldCss['visibility'],\r
- 'top':oldCss['top'],\r
- 'left':oldCss['left']\r
- });\r
- }\r
- return size;\r
- };\r
- //div must be display!=hidden. size is a dict with at least one of the fields 'width' and 'height'\r
- //TODO: check if div is sizable????\r
- p._convertSize = function(div, size){\r
- var eD = {\r
- 'width': div.outerWidth(true)-div.width(),\r
- 'height':div.outerHeight(true)-div.height()\r
- };\r
-\r
- if('width' in size){\r
- size.width -= (eD.width + this.shadowOffset);\r
- }\r
- if('height' in size){\r
- size.height -= (eD.height + this.shadowOffset);\r
- }\r
- };\r
-\r
- p.offset = function(offs, isSizable){\r
- var div = this.getDiv();\r
- var oldCss= isSizable? undefined : this.setSizable();\r
- //offset does NOT consider margins. Do we have top and left?\r
-\r
- this.getDiv().offset(offs);\r
- if(oldCss){\r
- div.css({\r
- 'display':oldCss['display'],\r
- 'visibility':oldCss['visibility'],\r
- 'maxWidth':oldCss['maxWidth'],\r
- 'maxHeight':oldCss['maxHeight'],\r
- 'minWidth':oldCss['minWidth'],\r
- 'minHeight':oldCss['minHeight']\r
- });\r
- }\r
- };\r
-\r
- p.isClosing = false;\r
- p.close = function(){\r
- this.isClosing = true;\r
- this.setFocusCycleRoot(false);\r
- var div = this.getDiv();\r
- var shadow = $('#'+this.getShadowDivId());\r
- shadow.remove();\r
- var me = this;\r
- var remove = this.defaultCloseOperation == 'remove';\r
- div.hide(this.fadeOutTime, function(){\r
-\r
- if(remove){\r
- div.remove();\r
- //this is because all bindings will be removed, including blur events\r
- //we remove this.getFocusAttr() to reupdate focus cycle root when calling show again\r
- }\r
-\r
- //restore event data on invoker, if any\r
- var id = '_tmpHandlers'+me.getId();\r
- if(me[id]){\r
- var oldHandlers = me[id];\r
- delete me[id];\r
- me.invoker.unbind('click');\r
- for(var k =0; k< oldHandlers.length; k++){\r
- var h = oldHandlers[k];\r
- me.invoker.bind(h.type+(h.namespace ? "."+h.namespace : ""),h.handler);\r
- }\r
- }\r
-\r
- me.isClosing = false;\r
- me.trigger('close');\r
- });\r
-\r
- };\r
-\r
-\r
- p.setSizable = function(){\r
- //if false, just update the flag\r
- var div = this.getDiv();\r
-\r
- if(!div.parent().length){\r
- div.appendTo('body');\r
- }\r
- var keys = ['display','visibility','left','top','maxWidth','maxHeight','minWidth','minHeight'];\r
- var css = {};\r
- for(var i=0; i<keys.length; i++){\r
- css[keys[i]] = div.css(keys[i]);\r
- }\r
- div.offset({\r
- 'left':wdw.scrollLeft(),\r
- 'top':wdw.scrollTop()\r
- });\r
- //set the div invisible but displayable to calculate the size (callbackPreShow)\r
- div.css({\r
- 'visibility':'hidden',\r
- 'maxWidth':'',\r
- 'maxHeight':'',\r
- 'minWidth':'',\r
- 'minHeight':'',\r
- 'height':'',\r
- 'width':''\r
- }).show();\r
-\r
-\r
- return css;\r
- }\r
- p.getShadowDivId = function(){\r
- return this.getId()+"_shadow";\r
- }\r
-\r
- p.getFocusAttr = function(){\r
- return this.getId()+"_focus";\r
- }\r
-\r
-\r
-})(PopupDiv.prototype);
\ No newline at end of file
+}
\ No newline at end of file
--- /dev/null
+/**\r
+ * TimeSide - Web Audio Components\r
+ * Copyright (c) 2011 Parisson\r
+ * Author: Riccardo Zaccarelli\r
+ * License: GNU General Public License version 2.0\r
+ */\r
+\r
+/**\r
+ * class for showing non-modal dialogs such as popups or combo lists\r
+ */\r
+function PopupDiv(){\r
+ var $J = jQuery;\r
+ var me = this;\r
+ var data = arguments.length && arguments[0] || {};\r
+\r
+ //var wdw = $J(window);\r
+ var div = $J('<div/>');\r
+ //we use an input rather than a span for two reasons:\r
+ //1: span with overflow:hidden have problems in vertical align with the close button in FF and IE\r
+ //2: if text title overlaps, with a span it is not selectable, with an input it is\r
+ var header = $J('<div/>').append($J('<input/>')).append($J('<a/>').attr('href','#').click(function(){\r
+ me.close();\r
+ return false;\r
+ })); //.css('float','right');\r
+ var container = $J('<div/>').css('overflow','auto');\r
+ var footer = $J('<div/>').append($J('<a/>').attr('href','#').click(function(){\r
+ me.trigger('ok',true);\r
+ return false;\r
+ }));\r
+ header.find('*').add(footer.find('*')).css('display','none');\r
+ div.append(header).append(container).append(footer);\r
+ //defining immediately the method getDiv (because it is used below)\r
+ this.getDiv = function(){\r
+ return div;\r
+ }\r
+ //setting functions:\r
+\r
+ var listeners = {};\r
+ this.getListeners = function(){\r
+ return listeners;\r
+ }\r
+\r
+ var k;\r
+ //setting static properties, if any:\r
+ for(k in PopupDiv){\r
+ this.__proto__[k] = PopupDiv[k];\r
+ // consolelog(k+' '+PopupDiv[k]);\r
+ delete PopupDiv[k];\r
+ }\r
+\r
+ //setting instance-specific properties:\r
+ for(k in data){\r
+ if(k == 'onOk' || k == 'onShow' || k == 'onClose'){\r
+ this.bind(k.substring(2).toLowerCase(),data[k]);\r
+ }else if(k == 'content'){\r
+ this.setContent(data[k]);\r
+ }else {\r
+ this[k] = data[k];\r
+ }\r
+ }\r
+\r
+ if(!this.popupCss){\r
+ this.popupCss = {}; //workaround to update css the first time we call show\r
+ //note that {} evaluates to true, but jQueryElement.css({}) is harmless\r
+ }\r
+\r
+\r
+}\r
+\r
+\r
+(function(p){\r
+ //private static variables\r
+ var $ = jQuery;\r
+ var w_ = window;\r
+ var d_ = document;\r
+ var wdw = $(w_);\r
+ var popupStaticId = 'popup_'+(new Date().getTime());\r
+ //var doc = $(d_);\r
+\r
+\r
+ //in the functions below, this refers to the new Popup instance, not to the prototype\r
+\r
+\r
+\r
+ p.isClickElement = function(element){\r
+ return element && element.length==1 && element instanceof $ && element[0] !== w_ && element[0] !== d_ &&\r
+ (element.is('a') || element.is('input[type=button]') || element.is('button') ||\r
+ element.is('input[type=submit]'));\r
+ };\r
+\r
+ p.getId = function(){\r
+ var div = this.getDiv();\r
+ if(!(div.attr('id'))){\r
+ div.attr('id',popupStaticId+'_'+(new Date().getTime()));\r
+ }\r
+ return div.attr('id');\r
+ };\r
+\r
+\r
+ //default properties which can be overridden\r
+ p.shadowOffset = 4;\r
+ p.invoker = wdw;\r
+ p.bounds = {\r
+ 'top':0.25,\r
+ 'left':0.25,\r
+ 'right':0.25,\r
+ 'bottom':0.25\r
+ }; //note that sepcifying top+bottom>=1 there is undefined behaviour (in chrome, offset is set but height takes all available space)\r
+ p.boundsExact = false;\r
+ p.popupClass = '';\r
+ p.popupCss = {};\r
+ p.showOk = false;\r
+ p.showClose=false;\r
+ p.title = "";\r
+ p.okButtonClass = '';\r
+ p.okButtonAlign = 'right';\r
+ p.closeButtonClass = '';\r
+ p.titleClass = '';\r
+ p.okButtonTitle = 'Ok';\r
+ p.closeButtonTitle = 'x';\r
+ p.defaultCloseOperation = 'hide';\r
+ p.focusable = false;\r
+ p.fadInTime = 'fast',\r
+ p.fadeOutTime = 0,\r
+ p.shadowOpacity = 0.25;\r
+ p.zIndex = 10000;\r
+ // p.listItemClass = '';\r
+\r
+ p.getFormData = function(){\r
+ var elms = this.find('input,select,textarea');\r
+ var ret = {};\r
+ elms.each(function(i,e){\r
+ var ee = $(e);\r
+ var key = ee.attr('name');\r
+ if(key){\r
+ ret[key] = ee.val();\r
+ }\r
+ });\r
+ return ret;\r
+ };\r
+\r
+ p.closeLater = function(millseconds){\r
+ var me = this;\r
+ setTimeout(function(){\r
+ me.close();\r
+ },millseconds);\r
+ },\r
+\r
+ //methods:\r
+ p.find = function(argumentAsInJQueryFind){\r
+ return $(this.getDiv().children()[1]).find(argumentAsInJQueryFind);\r
+ };\r
+\r
+ p.bind = function(eventName, callback){ //eventname: show, close or ok\r
+ var listeners = this.getListeners();\r
+ if(eventName in listeners){\r
+ listeners[eventName].push(callback);\r
+ }else{\r
+ listeners[eventName] = [callback];\r
+ }\r
+ };\r
+\r
+\r
+ p.unbind = function(eventName){\r
+ var listeners = this.getListeners();\r
+ if(eventName && eventName in listeners){\r
+ delete listeners[eventName];\r
+ }else if(!eventName){\r
+ for(var k in listeners){\r
+ delete listeners[k];\r
+ }\r
+ }\r
+ };\r
+\r
+ p.trigger = function(eventName){\r
+ var listeners = this.getListeners();\r
+ var me = this;\r
+ if(eventName in listeners){\r
+ var callbacks = listeners[eventName];\r
+ var i = 0;\r
+ if(eventName == 'ok'){\r
+ var data = this.getFormData();\r
+ for(i=0; i<callbacks.length; i++){\r
+ callbacks[i].apply(me,[data]);\r
+ }\r
+ if(arguments.length>1 && arguments[1]){\r
+ //workaround to remove listeners on close:\r
+ if('close' in listeners){\r
+ var v = listeners['close'];\r
+ delete listeners['close'];\r
+ this.close();\r
+ listeners['close'] = v;\r
+ }else{\r
+ this.close();\r
+ }\r
+\r
+ }\r
+ }else{\r
+ for(i=0; i<callbacks.length; i++){\r
+ callbacks[i].apply(me);\r
+ }\r
+ }\r
+ }\r
+ };\r
+\r
+ p.setContent = function(content){\r
+ var div = this.getDiv();\r
+ var container = $($(div).children()[1]);\r
+ //div.appendTo('body'); //necessary to properly display the div size\r
+ container.empty();\r
+\r
+ if(content instanceof $){\r
+ container.append(content);\r
+ }else if(content instanceof Array){\r
+ var jQ = $;\r
+ var me = this;\r
+ //var name = this.getListItemName();\r
+ var input = $('<input/>').attr('type','hidden').attr('name','selIndex');\r
+ var setEvents = function(idx,anchor,input){\r
+ anchor.click(function(){\r
+ input.val(idx);\r
+ me.trigger('ok',true);\r
+ return false;\r
+ }).focus(function(){ //focus because we need to get the value if ok is present\r
+ input.val(idx);\r
+ })\r
+ };\r
+ for(var h=0; h<content.length; h++){\r
+ var item = content[h];\r
+ var a = $('<a/>').attr('href','#');\r
+ if('class' in item){\r
+ a.addClass(item['class']);\r
+ }\r
+ if('html' in item){\r
+ a.html(item['html']);\r
+ }\r
+ if('name' in item){\r
+ a.attr('name', item['name']);\r
+ }\r
+ if('id' in item){\r
+ a.attr('id', item['id']);\r
+ }\r
+ if('css' in item){\r
+ a.css(item['css']);\r
+ }\r
+ a.css({\r
+ 'display':'block',\r
+ 'margin':'2px'\r
+ }); //margin is used to display the outline (focus)\r
+ setEvents(h,a,input);\r
+ container.append(a);\r
+ }\r
+ container.append(input);\r
+ }else if(content && content.constructor == Object){\r
+ var leftElements = $([]);\r
+ var rightElements = $([]);\r
+ var maxw = [0,0];\r
+ var insert = function(e1,e2){\r
+ var lineDiv = $('<div/>');\r
+ if(!e2){\r
+ e2=e1;\r
+ e1 = $('<span/>');\r
+ }\r
+ rightElements = rightElements.add(e2);\r
+ leftElements = leftElements.add(e1);\r
+ container.append(lineDiv.append(e1).append(e2));\r
+ return lineDiv;\r
+ }\r
+ var title, component;\r
+\r
+ var max = Math.max; //instantiate once\r
+ var lineDiv = undefined;\r
+ var lineDivs = $([]);\r
+ for(var k in content){\r
+ var val = content[k];\r
+ if(typeof val == 'string' || typeof val == 'number'){\r
+ title = $('<span/>').html(k);\r
+ maxw[0] = max(maxw[0],k.length);\r
+ maxw[1] = max(maxw[1],val.length);\r
+ component = $('<input/>').attr('type','text').val(val).attr('name',k);\r
+ lineDivs = lineDivs.add(insert(title,component));\r
+ }else if(val === true || val === false){\r
+ var id = this.getId()+"_checkbox";\r
+ title = $('<input/>').attr('type','checkbox').attr('name',k).attr('id',id);\r
+ if(val){\r
+ title.attr('checked','checked');\r
+ }else{\r
+ title.removeAttr('checked');\r
+ }\r
+ component = $('<label/>').attr('for',id).html(k);\r
+ maxw[1] = max(maxw[1],k.length);\r
+ lineDivs = lineDivs.add(insert($('<span/>').append(title),component));\r
+ }else if(val instanceof Array){\r
+ title = $('<span/>').html(k);\r
+ maxw[0] = max(maxw[0],k.length);\r
+ component = $('<select/>').attr('size',1);\r
+ for(var i=0; i< val.length; i++){\r
+ component.append($('<option/>').val(val[i]).html(val[i]));\r
+ maxw[1] = max(maxw[1],val[i].length);\r
+ }\r
+ lineDivs = lineDivs.add(insert(title,component));\r
+ }\r
+ if(lineDiv){\r
+ lineDiv.css('marginBottom','1ex');\r
+ }\r
+ }\r
+ lineDivs.css({\r
+ 'white-space': 'nowrap',\r
+ 'marginBottom':'0.5ex'\r
+ });\r
+ //last div erase marginBottom\r
+ $(lineDivs[lineDivs.length-1]).css('marginBottom','');\r
+\r
+\r
+ //display: inline-block below assures that width are properly set\r
+ //IE 6/7 accepts the value only on elements with a natural display: inline.\r
+ //(see http://www.quirksmode.org/css/display.html#t03)\r
+ //span and anchors are among them\r
+ //(see http://www.maxdesign.com.au/articles/inline/)\r
+ leftElements.add(rightElements).css({\r
+ 'display':'inline-block',\r
+ 'margin':'0px',\r
+ 'padding':'0px'\r
+ });\r
+ leftElements.css({\r
+ 'textAlign':'right',\r
+ 'marginRight':'0.5ex',\r
+ 'width':Math.round((3/5)*maxw[0])+'em'\r
+ });\r
+ rightElements.css({\r
+ 'width':Math.round((3/5)*Math.max(maxw[0], maxw[1]))+'em'\r
+ }); //might be zero if default values are all ""\r
+ }else{\r
+ container.append(""+content);\r
+ }\r
+\r
+ };\r
+\r
+ p.setFocusCycleRoot = function(value){\r
+ //var value = this.focusable;\r
+ var popup = this.getDiv();\r
+ var focusAttr = this.getFocusAttr();\r
+ var invokerIsClickable = this.isClickElement(this.invoker);\r
+ var children_ = popup.children();\r
+ var topDiv = $(children_[0]);\r
+ var centralDiv = $(children_[1]);\r
+ var bottomDiv = $(children_[2]);\r
+ var elementsWithFocus = centralDiv.find('input[type!=hidden],select,textarea,a'); //input:not(:hidden),select,textarea,a,\r
+\r
+ var ret = elementsWithFocus.length ? $(elementsWithFocus[0]) : popup;\r
+ if(this.showClose){\r
+ elementsWithFocus =elementsWithFocus.add(topDiv.find('a'));\r
+ }\r
+ if(this.showOk || this.title){\r
+ elementsWithFocus = elementsWithFocus.add(topDiv.find(':text'));\r
+ if(this.showOk){\r
+ elementsWithFocus = elementsWithFocus.add(bottomDiv.find('a'));\r
+ }\r
+ }\r
+ elementsWithFocus = elementsWithFocus.add(popup);\r
+ var focusNameSpace = "blur."+this.getId();\r
+ if(!value){\r
+ elementsWithFocus.each(function(i,elm){\r
+ $(elm).unbind(focusNameSpace).removeAttr('tabindex').removeAttr(focusAttr);\r
+ });\r
+ this.getFirstFocusableElement = function(){\r
+ return undefined;\r
+ }\r
+ if(invokerIsClickable){\r
+ this.invoker.removeAttr('tabindex').removeAttr(focusAttr);\r
+ }\r
+ return;\r
+ }\r
+ if(invokerIsClickable){\r
+ this.invoker.attr('tabindex',0).attr(focusAttr,'true');\r
+ }\r
+ var doc_ = d_; //closure (see nested function below)\r
+\r
+ //now all elements (including header and footer)\r
+\r
+ var me = this;\r
+ //bind the blur to each focusable element:\r
+ elementsWithFocus.each(function(i,e){\r
+ var ee = $(e);\r
+ ee.attr(focusAttr,'true');\r
+ ee.attr('tabindex',i+1);\r
+ ee.unbind(focusNameSpace).bind(focusNameSpace,function(){\r
+ //wait 250msec to see if the focus has been given to another popup focusable element: if yes, do nothing\r
+ //otherwise execute callback\r
+ setTimeout(function(){\r
+ var v = doc_.activeElement;\r
+ console.log(v);\r
+ if((v && $(v).attr(focusAttr)) || me.isClosing){\r
+ //if we are closing, we will call back this method which removes the focus attributes, bt meanwhile the\r
+ //timeout should execute\r
+ return;\r
+ }\r
+\r
+ me.close();\r
+ },200)\r
+ }); //set here another time delay. 300 seems to be the good compromise between visual hide and safetiness that\r
+ //meanwhile the focus has already been given to the next component\r
+ });\r
+ this.getFirstFocusableElement = function(){\r
+ return ret;\r
+ }\r
+ };\r
+ p.getFirstFocusableElement = function(){\r
+ return undefined;\r
+ };\r
+\r
+ p.show = function(){\r
+ var div = this.getDiv();\r
+ var me = this;\r
+\r
+ var cssModified = (this.popupClass || this.popupCss);\r
+ if(this.popupClass){\r
+ //this.popupClass might be in the prototype (not set by user)\r
+ div.removeClass().addClass(this.popupClass);\r
+ this.popupClass = ''; //override prototype property\r
+ }\r
+ if(this.popupCss){\r
+ //this.popupCss might be in the prototype (not set by user)\r
+ div.css(this.popupCss);\r
+ this.popupCss = ''; //override prototype property\r
+ }\r
+ //css modified, restore properties we need not to change:\r
+ //cssModified should be true ALL first times we call show, as this.popupCss = {} )ie, it evaluates to TRUE)\r
+ if(cssModified){\r
+ div.css({\r
+ 'position':'absolute',\r
+ 'zIndex':this.zIndex,\r
+ 'margin':'0px',\r
+ 'overflow':'hidden'\r
+ });\r
+ }\r
+\r
+ this.setFocusCycleRoot(this.focusable);\r
+\r
+ var subdiv = div.children();\r
+ //configure buttons. Text and classes are added here cause might have been changed\r
+ var topDiv = $(subdiv[0]);\r
+ var titleInput = topDiv.find(':text').eq(0); //$(':text') is equivalent to $('[type=text]') (selects all <input type="text"> elements)\r
+ var closeBtn = topDiv.find('a').eq(0);\r
+ if(!this.showClose && !this.title){\r
+ topDiv.hide();\r
+ }else{\r
+ topDiv.css({'paddingBottom':'1em','whiteSpace': 'nowrap'}).show(); //add padding to bottom\r
+ //warning: do NOT use real numbers such as 0.5ex cause browsers round it in a different manner\r
+ //whiteSpace is FUNDAMENTAL in calculating the popup div in case the title is the longest (max width) element\r
+ //in the popup div. We will set the same whitespace css also on the title (see below)\r
+\r
+ if(this.showClose){\r
+ closeBtn.attr('class',this.closeButtonClass); //removes all existing classes, if any (see jQuery removeClass doc)\r
+ closeBtn.html(this.closeButtonTitle);\r
+ closeBtn.css({\r
+ 'display':'inline-block','visibility':'visible','marginLeft':'1em'\r
+ //warning: do NOT use real numbers such as 0.5ex cause browsers round it in a different manner\r
+ //inline-block in order to retrieve/set width and height on the element\r
+ });\r
+ }else{\r
+ closeBtn.css({'margin':'0px'}).hide(); //margin:0 is to be sure, as afterwards we must span the title the whole popup width\r
+ }\r
+ //in any case, show titleElement cause even if title="", titleElement is used to position close on the right\r
+ titleInput.val(this.title).attr('readonly','readonly').attr('class',this.titleClass).removeClass().css({\r
+ 'display':'inline-block',\r
+ 'backgroundColor':'transparent',\r
+ 'padding': '0px', 'margin':'0px','border':'0px',\r
+ 'visibility': this.title ? 'visible' : 'hidden',\r
+ 'width':'',\r
+ 'maxWidth':'1px' //it is too tricky to set the width of the input spanning the whole title (in case of long titles)\r
+ //we might use a span, but we experienced problems in vertical align with the close button, as stated somewhere above.\r
+ //Moreover, a long title messes up the calculations in popup mode:\r
+ //a long title most likely determines the popup size, the latter the popup position, and once\r
+ //positioned and sized the popup size determines the title width (in order to span the title or letting the close button be visible)\r
+ //This is not robust at all and in fact it does not render the same popup position in all browsers.\r
+ //So, finally, set the input to the minimum allowed width, This means that maxWidth and maxHeight\r
+ //will be calculated based on the centraldiv dimensions, which is anyway the core div we want to properly visualize.\r
+ //Moreover, this way title resizing does not interfeere with the position\r
+ });\r
+ }\r
+\r
+ var bottomDiv = $(subdiv[2]);\r
+ var okButton = bottomDiv.find('a').eq(0);\r
+ //see note above about why we dont use okButton.is(':visible')\r
+ if(this.showOk){\r
+ bottomDiv.css({'paddingTop':'1em','textAlign':this.okButtonAlign}).show(); //add padding to bottom\r
+ //warning: do NOT use real numbers such as 0.5ex cause browsers round it in a different manner\r
+ okButton.attr('class', this.okButtonClass); //removes all existing classes, if any\r
+ okButton.html(this.okButtonTitle);\r
+ okButton.css({'display':'inline-block','visibility':'visible'}); //in order to set width and height on the element\r
+ }else{\r
+ bottomDiv.hide();\r
+ }\r
+\r
+ var centralDiv = $(subdiv[1]);\r
+ //reset properties of the central div\r
+ centralDiv.css({\r
+ 'overflow':'auto',\r
+ 'maxHeight':'',\r
+ 'maxWidth':'',\r
+ 'minHeight':'',\r
+ 'minWidth':'',\r
+ 'height':'',\r
+ 'width':'',\r
+ 'visibility':'visible'\r
+ }).show();\r
+\r
+ this.setSizable();//this means the popupdiv is display: !none and visibility:hidden, so every element\r
+ //inside it should be visible and therefore sizable. Being visible means that jQuery.is(':visible') returns true\r
+ //start with showing top and bottom if some elements are visible\r
+\r
+ var invoker = this.invoker;\r
+\r
+ var sizeAsPopup = false;\r
+ if(this.isClickElement(invoker)){\r
+ this.setBoundsAsPopup(invoker, true);\r
+ sizeAsPopup = true;\r
+ //storing click events, when showing clicking on an event must give the focus to the popup\r
+ //old handlers will be restored in close()\r
+ this['_tmpHandlers'+this.getId()] = undefined;\r
+ var focusElm = this.getFirstFocusableElement();\r
+ if(focusElm){\r
+ var oldHandlers = [];\r
+ var type = 'click';\r
+ var clickEvents =invoker.data("events")[type];\r
+ $.each(clickEvents, function(key, value) {\r
+ oldHandlers.push(value);\r
+ })\r
+ invoker.unbind(type); //remove (temporarily) the binding to the event.\r
+ //for instance, if we show the popup by clicking invoker, when the popup is shown do nothing\r
+ //on clicking invoker until popup.hide is called\r
+\r
+ this['_tmpHandlers'+this.getId()] = oldHandlers;\r
+ invoker.unbind(type).bind(type,function(evt){\r
+ //let the invoker have focus and let it be recognized as an element which does not blur the popup:\r
+ //invoker.attr('tabindex',0).attr(focusAttr,'true');\r
+ if(div.length && div.is(':visible')){\r
+ focusElm.focus();\r
+ return false;\r
+ }\r
+ //something wrong: close the popup and restore the hanlers\r
+ me.close.apply(me);\r
+ return false;\r
+ });\r
+ }\r
+\r
+ }else{\r
+ this.setBoundsInside(invoker, this.bounds, this.boundsExact, true);\r
+ }\r
+\r
+ //set title and close button to span whole width, if necessary\r
+ //closeButton.outerWidth should be zero if this.showClose = false\r
+ //titleInput.outerWidth(true) should be equal to titleInput.width(), as margins borders and padding are zero, however we want to calculate it safely\r
+ if(this.showClose || this.title){\r
+ var titleW = topDiv.width() - closeBtn.outerWidth(true) - (titleInput.outerWidth(true)-titleInput.width());\r
+ titleInput.css({'maxWidth':'','width':(titleW)+'px'});\r
+ }\r
+\r
+ //set central div max height ONLY IF NECESSARY (overflow). Until here, the main popup is sized and placed\r
+ //but the central div might overflow\r
+ var height = centralDiv.height();\r
+ var maxHeight = (div.height()-topDiv.outerHeight(true)-bottomDiv.outerHeight(true)-\r
+ (centralDiv.outerHeight(true)-centralDiv.height()));\r
+ if(maxHeight<height){\r
+ centralDiv.css('maxHeight',maxHeight+'px');\r
+ }\r
+ //same for width:\r
+ var maxWidth = div.width();\r
+ var width = centralDiv.outerWidth(true);\r
+ if(maxWidth<width){\r
+ centralDiv.css('maxWidth',maxWidth+'px');\r
+ }\r
+\r
+ // var height = centralDiv.height();\r
+ // if(sizeAsPopup && maxHeight<height){\r
+ // centralDiv.css('maxHeight',maxHeight+'px');\r
+ // }else{\r
+ // centralDiv.css({\r
+ // 'maxHeight': maxHeight+'px',\r
+ // 'minHeight': maxHeight+'px'\r
+ // });\r
+ // }\r
+ // //set central div max width ONLY IF NECESSARY:\r
+ // var maxWidth = div.width();\r
+ // var width = $(subdiv[1]).outerWidth(true);\r
+ // if(sizeAsPopup && maxWidth<width){\r
+ // centralDiv.css('maxWidth',maxWidth+'px');\r
+ // }else{\r
+ // centralDiv.css({\r
+ // 'maxWidth': maxWidth+'px',\r
+ // 'minWidth':maxWidth+'px'\r
+ // });\r
+ // }\r
+\r
+\r
+\r
+ //creating shadow. REmove attributes tabindex (unnecessary) and especially focusAttr,\r
+ //so that clicking tab key and setting the shadow focusable hides the popup. If one wants the shadow not to hide the popup. keep\r
+ //focusAttr BUT insert shadow in the focus cycle root (see method)\r
+ var shadow = div.clone(false,false).empty().css({\r
+ 'backgroundColor':'#000',\r
+ 'borderColor':'#000',\r
+ 'visibility':'visible',\r
+ 'zIndex':this.zIndex-1\r
+ }).removeAttr('tabindex').removeAttr(this.getFocusAttr()).fadeTo(0,0).\r
+ attr('id',this.getShadowDivId()) //for use in hide\r
+ .insertAfter(div);\r
+\r
+\r
+ var postShowFcn = function(){\r
+ me.trigger('show');\r
+ var rect = me.getBounds.apply(me);\r
+ shadow.css({\r
+ 'left':(rect.x + me.shadowOffset)+'px',\r
+ 'top':(rect.y + me.shadowOffset)+'px',\r
+ 'width':(rect.width)+'px',\r
+ 'height':(rect.height)+'px'\r
+ }).fadeTo(me.fadInTime,me.shadowOpacity, function(){\r
+ var v = me.getFirstFocusableElement();\r
+ if(v){\r
+ v.focus();\r
+ }\r
+ });\r
+ }\r
+\r
+ div.hide().css('visibility','visible').show(this.fadInTime,function(){\r
+ postShowFcn();\r
+ });\r
+ };\r
+\r
+ p.setBoundsAsPopup = function(popupInvoker, isSizable){\r
+ var invoker = popupInvoker;\r
+ var div = this.getDiv();\r
+ var oldCss= isSizable ? undefined : this.setSizable();\r
+\r
+ var shadowOffset = this.shadowOffset;\r
+ var windowRectangle = this.getBoundsOf(wdw); //returns the window rectangle\r
+\r
+ var invokerOffset = invoker.offset();\r
+\r
+ var invokerOuterHeight = invoker.outerHeight();\r
+ var spaceAbove = invokerOffset.top - windowRectangle.y;\r
+ var spaceBelow = windowRectangle.height - invokerOuterHeight - spaceAbove;\r
+ var placeAbove = spaceAbove > spaceBelow && div.outerHeight(false) + shadowOffset > spaceBelow;\r
+\r
+ var invokerOuterWidth = invoker.outerWidth();\r
+ var spaceRight = windowRectangle.x + windowRectangle.width - invokerOffset.left ;\r
+ var spaceLeft = invokerOffset.left + invokerOuterWidth - windowRectangle.x;\r
+ var placeLeft = spaceLeft > spaceRight && div.outerWidth(false) + shadowOffset > spaceRight;\r
+\r
+ this.setMaxSize({\r
+ height : (placeAbove ? spaceAbove : spaceBelow),\r
+ width: (placeLeft ? spaceLeft : spaceRight)\r
+ },isSizable); //width will be ignored (for the moment)\r
+ //decrement of one pixel cause when the popup has to be reduced and the shadows bounds "touch" the window right or bottom sides,\r
+ //the window scrolls (and it shouldn't)\r
+\r
+ //setting the minimum size to the invoker width, minheight the same as maxHeight (see above)\r
+ this.setMinSize({\r
+ width: invoker.outerWidth()+this.shadowOffset //workaround to NOT consider the shadow, as offset below substracts the shadow\r
+ //height : spaceAbove>spaceBelow ? spaceAbove : spaceBelow //why this? because if we click the popup a\r
+ //computed height CH seems to be set. At subsequent popup show, CH will be the same UNLESS a new maxHeight lower than CH is set\r
+ //however, we want CH to change even if a new maxHeight greater than CH is set\r
+ },isSizable);\r
+\r
+ //setting the top and left. This must be done at last because popupDiv.outerHeight(true)\r
+ //must have been computed according to the height set above...\r
+ this.offset({\r
+ 'left': placeLeft ? invokerOffset.left+ invokerOuterWidth - div.outerWidth(true)-shadowOffset : invokerOffset.left,\r
+ 'top': (placeAbove ? invokerOffset.top - div.outerHeight(true) :\r
+ invokerOffset.top + invokerOuterHeight)\r
+ },isSizable);\r
+\r
+\r
+ if(oldCss){\r
+ div.css({\r
+ 'display':oldCss['display'],\r
+ 'visibility':oldCss['visibility']\r
+ });\r
+ }\r
+ };\r
+ //places and resize the popupdiv inside parent\r
+ //padding is a dict {top:,left:,bottom:..,right:,...} measuring the distance of the popupdiv from the corners, so that\r
+ //padding={top:0.25,left:0.25,bottom:0.25,right:0.25} will place the popupdiv at the center of parent\r
+ //padding={top:25,left:25,bottom:25,right:25} will place the popupdiv at distances 25 px from parent sides\r
+ //in other words, padding keys lower or euqals to 1 will be conbsidered as percentage, otherwise as absolute measures in px\r
+ p.setBoundsInside = function(parent, pd, boundsExact, isSizable){\r
+\r
+ var div = this.getDiv();\r
+ var oldCss = isSizable ? undefined : this.setSizable();\r
+\r
+ var bounds = this.getBoundsOf(parent);\r
+\r
+ var x=bounds.x;\r
+ var y = bounds.y;\r
+ var w = bounds.width\r
+ var h = bounds.height;\r
+ var pInt = parseInt;\r
+ //rebuilding:\r
+\r
+ var padding = {\r
+ top: pd['top'],\r
+ left:pd['left'],\r
+ bottom:pd['bottom'],\r
+ right:pd['right']\r
+ };\r
+\r
+ for(var k in padding){\r
+ if(padding[k]<=0){\r
+ padding[k]=0;\r
+ }else if(padding[k]<=1){\r
+ padding[k] = k=='top' || k =='bottom' ? h*padding[k] : w*padding[k];\r
+ }else{\r
+ padding[k] = pInt(padding[k]);\r
+ }\r
+ }\r
+\r
+ var maxSize = {\r
+ 'width':w-padding['left']-padding['right']+this.shadowOffset,\r
+ 'height':h-padding['top']-padding['bottom']+this.shadowOffset\r
+ };\r
+\r
+ var vvvv ={\r
+ width:maxSize.width,\r
+ height:maxSize.height\r
+ };\r
+ this._convertSize(div, vvvv);\r
+\r
+ if(boundsExact){\r
+ this.setMinSize({\r
+ width:maxSize.width,\r
+ height:maxSize.height\r
+ },isSizable); //a copy cause the argument will be modified\r
+ this.setMaxSize({\r
+ width:maxSize.width,\r
+ height:maxSize.height\r
+ }, isSizable); //a copy cause the argument will be modified\r
+\r
+ this.offset({\r
+ 'left':x + padding['left'],\r
+ 'top': y + padding['top']\r
+ },isSizable);\r
+ }else{\r
+ this.setMaxSize({\r
+ width:maxSize.width,\r
+ height:maxSize.height\r
+ },isSizable); //a copy cause the argument will be modified\r
+ var spanLeft = maxSize.width - div.outerWidth(true);\r
+ var spanTop = maxSize.height - div.outerHeight(true);\r
+ this.offset({\r
+ 'left':x + padding['left'] + (spanLeft > 0 ? spanLeft/2 : 0),\r
+ 'top': y + padding['top'] +(spanTop > 0 ? spanTop/2 : 0)\r
+ },isSizable);\r
+\r
+ }\r
+ //convert to percentage in order to keep same dimensions when zooming\r
+\r
+\r
+ if(oldCss){\r
+ div.css({\r
+ 'display':oldCss['display'],\r
+ 'visibility':oldCss['visibility']\r
+ });\r
+ }\r
+ };\r
+\r
+ p.getBounds = function(){\r
+ return this.getBoundsOf(this.getDiv());\r
+ };\r
+\r
+ p.getBoundsOf = function(jQueryElement){\r
+ var ret = {\r
+ x:0,\r
+ y:0,\r
+ width:0,\r
+ height:0\r
+ };\r
+ if(!jQueryElement || !(jQueryElement instanceof $)){\r
+ jQueryElement = wdw;\r
+ }\r
+ if(jQueryElement[0] === w_){\r
+ ret.x = jQueryElement.scrollLeft();\r
+ ret.y = jQueryElement.scrollTop();\r
+ }else{\r
+ var offs = jQueryElement.offset();\r
+ ret.x = offs.left;\r
+ ret.y = offs.top;\r
+ }\r
+ ret.width = jQueryElement.width();\r
+ ret.height = jQueryElement.height();\r
+ return ret;\r
+ };\r
+\r
+ p.setMaxSize = function(size, isSizable){\r
+ var div = this.getDiv();\r
+ var oldCss = isSizable ? undefined : this.setSizable();\r
+\r
+ this._convertSize(div, size);\r
+ var css = {};\r
+ if('width' in size){\r
+ css.maxWidth = size.width+'px';\r
+ }\r
+ if('height' in size){\r
+ css.maxHeight = size.height+'px';\r
+ }\r
+ if(css){\r
+ div.css(css);\r
+ }\r
+ if(oldCss){\r
+ div.css({\r
+ 'display':oldCss['display'],\r
+ 'visibility':oldCss['visibility'],\r
+ 'top':oldCss['top'],\r
+ 'left':oldCss['left']\r
+ });\r
+ }\r
+ return size;\r
+ };\r
+ p.setMinSize = function(size, isSizable){\r
+ var div = this.getDiv();\r
+ var oldCss= isSizable ? undefined : this.setSizable();\r
+\r
+ this._convertSize(div, size);\r
+ var css = {};\r
+ if('width' in size){\r
+ css.minWidth = size.width+'px';\r
+ }\r
+ if('height' in size){\r
+ css.minHeight = size.height+'px';\r
+ }\r
+ if(css){\r
+ div.css(css);\r
+ }\r
+ if(oldCss){\r
+ div.css({\r
+ 'display':oldCss['display'],\r
+ 'visibility':oldCss['visibility'],\r
+ 'top':oldCss['top'],\r
+ 'left':oldCss['left']\r
+ });\r
+ }\r
+ return size;\r
+ };\r
+ //div must be display!=hidden. size is a dict with at least one of the fields 'width' and 'height'\r
+ p._convertSize = function(div, size){\r
+ var eD = {\r
+ 'width': div.outerWidth(true)-div.width(),\r
+ 'height':div.outerHeight(true)-div.height()\r
+ };\r
+\r
+ if('width' in size){\r
+ size.width -= (eD.width + this.shadowOffset);\r
+ }\r
+ if('height' in size){\r
+ size.height -= (eD.height + this.shadowOffset);\r
+ }\r
+ };\r
+\r
+ p.offset = function(offs, isSizable){\r
+ var div = this.getDiv();\r
+ var oldCss= isSizable? undefined : this.setSizable();\r
+ //offset does NOT consider margins. Do we have top and left?\r
+\r
+ this.getDiv().offset(offs);\r
+ if(oldCss){\r
+ div.css({\r
+ 'display':oldCss['display'],\r
+ 'visibility':oldCss['visibility'],\r
+ 'maxWidth':oldCss['maxWidth'],\r
+ 'maxHeight':oldCss['maxHeight'],\r
+ 'minWidth':oldCss['minWidth'],\r
+ 'minHeight':oldCss['minHeight']\r
+ });\r
+ }\r
+ };\r
+\r
+ p.isClosing = false;\r
+ p.close = function(){\r
+ this.isClosing = true;\r
+ this.setFocusCycleRoot(false);\r
+ var div = this.getDiv();\r
+ var shadow = $('#'+this.getShadowDivId());\r
+ shadow.remove();\r
+ var me = this;\r
+ var remove = this.defaultCloseOperation == 'remove';\r
+ div.hide(this.fadeOutTime, function(){\r
+\r
+ if(remove){\r
+ div.remove();\r
+ //this is because all bindings will be removed, including blur events\r
+ //we remove this.getFocusAttr() to reupdate focus cycle root when calling show again\r
+ }\r
+\r
+ //restore event data on invoker, if any\r
+ var id = '_tmpHandlers'+me.getId();\r
+ if(me[id]){\r
+ var oldHandlers = me[id];\r
+ delete me[id];\r
+ me.invoker.unbind('click');\r
+ for(var k =0; k< oldHandlers.length; k++){\r
+ var h = oldHandlers[k];\r
+ me.invoker.bind(h.type+(h.namespace ? "."+h.namespace : ""),h.handler);\r
+ }\r
+ }\r
+\r
+ me.isClosing = false;\r
+ me.trigger('close');\r
+ });\r
+\r
+ };\r
+\r
+\r
+ p.setSizable = function(){\r
+ //if false, just update the flag\r
+ var div = this.getDiv();\r
+\r
+ if(!div.parent().length){\r
+ div.appendTo('body');\r
+ }\r
+ var keys = ['display','visibility','left','top','maxWidth','maxHeight','minWidth','minHeight'];\r
+ var css = {};\r
+ for(var i=0; i<keys.length; i++){\r
+ css[keys[i]] = div.css(keys[i]);\r
+ }\r
+ div.offset({\r
+ 'left':wdw.scrollLeft(),\r
+ 'top':wdw.scrollTop()\r
+ });\r
+ //set the div invisible but displayable to calculate the size (callbackPreShow)\r
+ div.css({\r
+ 'visibility':'hidden',\r
+ 'maxWidth':'',\r
+ 'maxHeight':'',\r
+ 'minWidth':'',\r
+ 'minHeight':'',\r
+ 'height':'',\r
+ 'width':''\r
+ }).show();\r
+\r
+\r
+ return css;\r
+ }\r
+ p.getShadowDivId = function(){\r
+ return this.getId()+"_shadow";\r
+ }\r
+\r
+ p.getFocusAttr = function(){\r
+ return this.getId()+"_focus";\r
+ }\r
+\r
+\r
+})(PopupDiv.prototype);\r