}\r
\r
var k;\r
- //setting static properties, if any:\r
+\r
+ //setting static properties, if any.\r
+ //The idea is that static PopupDiv properties SPP (eg, PopupDiv.shadowOffset = 5) should be added to the current PopupDiv\r
+ //instance prototype ONCE (properties in the prototype are shared between all PopupDiv instances)\r
+ //and then deleted from the PopupDiv function.\r
+ //The problem is how to access the prototype: nor __proto__ neither Object.getPrototypeOf(this) are cross browser\r
+ //(see http://ejohn.org/blog/objectgetprototypeof/, which suggests to rewrite a global Object.getPrototypeOf(arg), which\r
+ //however does not work if arg constructor has been manipulated). Eventually, we do the following:\r
+ //Find a prototype variable P: P= Object.getPrototypeOf: is it NOT a function? then P = this.__proto__. Is it NOT an object?\r
+ //then P= this.\r
+ //Populate P, if P = this, we are assigning SPP to each new instance and NOT ONCE to the prototype object, which of course\r
+ //means that SPP's cannot be deleted after their first assignment. This requires more work and more memory consumption\r
+ //but it assures cross browser compatibility\r
+\r
+ var staticProps = undefined;\r
for(k in PopupDiv){\r
- this.__proto__[k] = PopupDiv[k];\r
- // consolelog(k+' '+PopupDiv[k]);\r
- delete PopupDiv[k];\r
+ if(!staticProps){\r
+ staticProps = {};\r
+ }\r
+ var f = PopupDiv[k];\r
+ if(typeof f !== 'function'){ //do not assign functions (PopupDiv.function... might be used in future as\r
+ //static functions accessible from outside\r
+ staticProps[k] = f;\r
+ }\r
}\r
+ if(staticProps){\r
+ var remove = true;\r
+ var proto = undefined;\r
+ if ( typeof Object.getPrototypeOf !== "function" ) {\r
+ if ( typeof this.__proto__ === "object" ) {\r
+ proto = this.__proto__;\r
+ } else {\r
+ // May break if the constructor has been tampered with:\r
+ // proto = this.constructor.prototype;\r
+ //so we assign tis class BUT we DO NOT remove static properties\r
+ proto = this;\r
+ remove = false;\r
+ }\r
+ }else{\r
+ proto = Object.getPrototypeOf(this);\r
+ }\r
+ for(k in staticProps){\r
+ proto[k] = staticProps[k];\r
+ if(remove){\r
+ delete PopupDiv[k];\r
+ }\r
+ }\r
+ }\r
+\r
+ \r
\r
//setting instance-specific properties:\r
for(k in data){\r
p.fadeOutTime = 0,\r
p.shadowOpacity = 0.25;\r
p.zIndex = 10000;\r
- // p.listItemClass = '';\r
+ p.listItemClass = '';\r
+ p.listItemCss = '';\r
\r
+ //returns the data associated to this popup. Basically, it searches for all input, select or textarea with attribute\r
+ //this.getFormDataAttrName(). The use of a custom attribute is cross browser, note that some attributes, eg name, are\r
+ //not (name is not safe in IE for instance)\r
p.getFormData = function(){\r
var elms = this.find('input,select,textarea');\r
var ret = {};\r
+ var att = this.getFormDataAttrName();\r
elms.each(function(i,e){\r
var ee = $(e);\r
- var key = ee.attr('name');\r
+ var key = ee.attr(att);\r
if(key){\r
ret[key] = ee.val();\r
}\r
var container = $($(div).children()[1]);\r
//div.appendTo('body'); //necessary to properly display the div size\r
container.empty();\r
-\r
+ var att = this.getFormDataAttrName();\r
if(content instanceof $){\r
container.append(content);\r
}else if(content instanceof Array){\r
- var jQ = $;\r
+ \r
var me = this;\r
//var name = this.getListItemName();\r
- var input = $('<input/>').attr('type','hidden').attr('name','selIndex');\r
+ var input = $('<input/>').attr('type','hidden').attr(att,'selIndex');\r
var setEvents = function(idx,anchor,input){\r
anchor.click(function(){\r
input.val(idx);\r
input.val(idx);\r
})\r
};\r
+ var listItems = $([]);\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
+ var a = $('<a/>').attr('href','#').html(""+item);\r
+ listItems = listItems.add(a);\r
setEvents(h,a,input);\r
container.append(a);\r
}\r
+ //set css and class on all listitem anchor:\r
+ //set margin to properly display the outline (border focus)\r
+ //this css can be overridden (see lines below) as it is not strictly necessary\r
+ listItems.css({\r
+ 'margin':'2px'\r
+ });\r
+ if(this.listItemClass){\r
+ listItems.addClass(this.listItemClass);\r
+ }\r
+ if(this.listItemCss){\r
+ listItems.css(this.listItemCss);\r
+ }\r
+ //override css which are necessary to properly display the listItem:\r
+ listItems.css({\r
+ 'position' : '',\r
+ 'display':'block'\r
+ });\r
container.append(input);\r
}else if(content && content.constructor == Object){\r
var leftElements = $([]);\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
+ component = $('<input/>').attr('type','text').val(val).attr(att,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
+ title = $('<input/>').attr('type','checkbox').attr(att,k).attr('id',id);\r
if(val){\r
title.attr('checked','checked');\r
}else{\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
+ component = $('<select/>').attr('size',1).attr(att,k);\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
'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
+ 'width':Math.round((3/5)*max(maxw[0], maxw[1]))+'em'\r
}); //might be zero if default values are all ""\r
}else{\r
container.append(""+content);\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(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
//otherwise execute callback\r
setTimeout(function(){\r
var v = doc_.activeElement;\r
- console.log(v);\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
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
+ //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
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
+ topDiv.hide();\r
+ }else{\r
+ topDiv.css({\r
+ 'paddingBottom':'1em',\r
+ 'whiteSpace': 'nowrap'\r
+ }).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',\r
+ 'visibility':'visible',\r
+ '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({\r
+ 'margin':'0px'\r
+ }).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',\r
+ 'margin':'0px',\r
+ '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
+ bottomDiv.css({\r
+ 'paddingTop':'1em',\r
+ 'textAlign':this.okButtonAlign\r
+ }).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({\r
+ 'display':'inline-block',\r
+ 'visibility':'visible'\r
+ }); //in order to set width and height on the element\r
}else{\r
bottomDiv.hide();\r
}\r
var centralDiv = $(subdiv[1]);\r
//reset properties of the central div\r
centralDiv.css({\r
- 'overflow':'auto',\r
+ 'overflow':'auto',\r
'maxHeight':'',\r
'maxWidth':'',\r
'minHeight':'',\r
'minWidth':'',\r
'height':'',\r
'width':'',\r
- 'visibility':'visible'\r
+ 'visibility':'visible'\r
}).show();\r
\r
- this.setSizable();//this means the popupdiv is display: !none and visibility:hidden, so every element\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
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
+ //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
+ titleInput.css({\r
+ 'maxWidth':'',\r
+ 'width':(titleW)+'px'\r
+ });\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
+ //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
+ if(maxHeight<height){\r
centralDiv.css('maxHeight',maxHeight+'px');\r
}\r
- //same for width:\r
- var maxWidth = div.width();\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
// var height = centralDiv.height();\r
// if(sizeAsPopup && maxHeight<height){\r
- // centralDiv.css('maxHeight',maxHeight+'px');\r
+ // centralDiv.css('maxHeight',maxHeight+'px');\r
// }else{\r
- // centralDiv.css({\r
- // 'maxHeight': maxHeight+'px',\r
- // 'minHeight': maxHeight+'px'\r
- // });\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
+ // centralDiv.css('maxWidth',maxWidth+'px');\r
// }else{\r
- // centralDiv.css({\r
- // 'maxWidth': maxWidth+'px',\r
- // 'minWidth':maxWidth+'px'\r
- // });\r
+ // centralDiv.css({\r
+ // 'maxWidth': maxWidth+'px',\r
+ // 'minWidth':maxWidth+'px'\r
+ // });\r
// }\r
\r
\r
var div = this.getDiv();\r
var oldCss= isSizable ? undefined : this.setSizable();\r
\r
- var shadowOffset = this.shadowOffset;\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 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 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
+ 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
return this.getId()+"_focus";\r
}\r
\r
+ p.getFormDataAttrName = function(){\r
+ return this.getId()+"_data";\r
+ }\r
\r
})(PopupDiv.prototype);\r