").attr("role","tooltip").addClass("ui-tooltip ui-widget ui-corner-all ui-widget-content "+(this.options.tooltipClass||"")),s=i.uniqueId().attr("id");return e("
").addClass("ui-tooltip-content").appendTo(i),i.appendTo(this.document[0].body),this.tooltips[s]={element:t,tooltip:i}},_find:function(e){var t=e.data("ui-tooltip-id");return t?this.tooltips[t]:null},_removeTooltip:function(e){e.remove(),delete this.tooltips[e.attr("id")]},_destroy:function(){var t=this;e.each(this.tooltips,function(i,s){var n=e.Event("blur"),a=s.element;n.target=n.currentTarget=a[0],t.close(n,!0),e("#"+i).remove(),a.data("ui-tooltip-title")&&(a.attr("title")||a.attr("title",a.data("ui-tooltip-title")),a.removeData("ui-tooltip-title"))}),this.liveRegion.remove()}});var b="ui-effects-",y=e;e.effects={effect:{}},function(e,t){function i(e,t,i){var s=d[t.type]||{};return null==e?i||!t.def?null:t.def:(e=s.floor?~~e:parseFloat(e),isNaN(e)?t.def:s.mod?(e+s.mod)%s.mod:0>e?0:e>s.max?s.max:e)}function s(i){var s=l(),n=s._rgba=[];return i=i.toLowerCase(),f(h,function(e,a){var o,r=a.re.exec(i),h=r&&a.parse(r),l=a.space||"rgba";return h?(o=s[l](h),s[u[l].cache]=o[u[l].cache],n=s._rgba=o._rgba,!1):t}),n.length?("0,0,0,0"===n.join()&&e.extend(n,a.transparent),s):a[i]}function n(e,t,i){return i=(i+1)%1,1>6*i?e+6*(t-e)*i:1>2*i?t:2>3*i?e+6*(t-e)*(2/3-i):e}var a,o="backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",r=/^([\-+])=\s*(\d+\.?\d*)/,h=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[e[1],e[2],e[3],e[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(e){return[2.55*e[1],2.55*e[2],2.55*e[3],e[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,parse:function(e){return[parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16)]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])/,parse:function(e){return[parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16)]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(e){return[e[1],e[2]/100,e[3]/100,e[4]]}}],l=e.Color=function(t,i,s,n){return new e.Color.fn.parse(t,i,s,n)},u={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},d={"byte":{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},c=l.support={},p=e("
")[0],f=e.each;p.style.cssText="background-color:rgba(1,1,1,.5)",c.rgba=p.style.backgroundColor.indexOf("rgba")>-1,f(u,function(e,t){t.cache="_"+e,t.props.alpha={idx:3,type:"percent",def:1}}),l.fn=e.extend(l.prototype,{parse:function(n,o,r,h){if(n===t)return this._rgba=[null,null,null,null],this;(n.jquery||n.nodeType)&&(n=e(n).css(o),o=t);var d=this,c=e.type(n),p=this._rgba=[];return o!==t&&(n=[n,o,r,h],c="array"),"string"===c?this.parse(s(n)||a._default):"array"===c?(f(u.rgba.props,function(e,t){p[t.idx]=i(n[t.idx],t)}),this):"object"===c?(n instanceof l?f(u,function(e,t){n[t.cache]&&(d[t.cache]=n[t.cache].slice())}):f(u,function(t,s){var a=s.cache;f(s.props,function(e,t){if(!d[a]&&s.to){if("alpha"===e||null==n[e])return;d[a]=s.to(d._rgba)}d[a][t.idx]=i(n[e],t,!0)}),d[a]&&0>e.inArray(null,d[a].slice(0,3))&&(d[a][3]=1,s.from&&(d._rgba=s.from(d[a])))}),this):t},is:function(e){var i=l(e),s=!0,n=this;return f(u,function(e,a){var o,r=i[a.cache];return r&&(o=n[a.cache]||a.to&&a.to(n._rgba)||[],f(a.props,function(e,i){return null!=r[i.idx]?s=r[i.idx]===o[i.idx]:t})),s}),s},_space:function(){var e=[],t=this;return f(u,function(i,s){t[s.cache]&&e.push(i)}),e.pop()},transition:function(e,t){var s=l(e),n=s._space(),a=u[n],o=0===this.alpha()?l("transparent"):this,r=o[a.cache]||a.to(o._rgba),h=r.slice();return s=s[a.cache],f(a.props,function(e,n){var a=n.idx,o=r[a],l=s[a],u=d[n.type]||{};null!==l&&(null===o?h[a]=l:(u.mod&&(l-o>u.mod/2?o+=u.mod:o-l>u.mod/2&&(o-=u.mod)),h[a]=i((l-o)*t+o,n)))}),this[n](h)},blend:function(t){if(1===this._rgba[3])return this;var i=this._rgba.slice(),s=i.pop(),n=l(t)._rgba;return l(e.map(i,function(e,t){return(1-s)*n[t]+s*e}))},toRgbaString:function(){var t="rgba(",i=e.map(this._rgba,function(e,t){return null==e?t>2?1:0:e});return 1===i[3]&&(i.pop(),t="rgb("),t+i.join()+")"},toHslaString:function(){var t="hsla(",i=e.map(this.hsla(),function(e,t){return null==e&&(e=t>2?1:0),t&&3>t&&(e=Math.round(100*e)+"%"),e});return 1===i[3]&&(i.pop(),t="hsl("),t+i.join()+")"},toHexString:function(t){var i=this._rgba.slice(),s=i.pop();return t&&i.push(~~(255*s)),"#"+e.map(i,function(e){return e=(e||0).toString(16),1===e.length?"0"+e:e}).join("")},toString:function(){return 0===this._rgba[3]?"transparent":this.toRgbaString()}}),l.fn.parse.prototype=l.fn,u.hsla.to=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t,i,s=e[0]/255,n=e[1]/255,a=e[2]/255,o=e[3],r=Math.max(s,n,a),h=Math.min(s,n,a),l=r-h,u=r+h,d=.5*u;return t=h===r?0:s===r?60*(n-a)/l+360:n===r?60*(a-s)/l+120:60*(s-n)/l+240,i=0===l?0:.5>=d?l/u:l/(2-u),[Math.round(t)%360,i,d,null==o?1:o]},u.hsla.from=function(e){if(null==e[0]||null==e[1]||null==e[2])return[null,null,null,e[3]];var t=e[0]/360,i=e[1],s=e[2],a=e[3],o=.5>=s?s*(1+i):s+i-s*i,r=2*s-o;return[Math.round(255*n(r,o,t+1/3)),Math.round(255*n(r,o,t)),Math.round(255*n(r,o,t-1/3)),a]},f(u,function(s,n){var a=n.props,o=n.cache,h=n.to,u=n.from;l.fn[s]=function(s){if(h&&!this[o]&&(this[o]=h(this._rgba)),s===t)return this[o].slice();var n,r=e.type(s),d="array"===r||"object"===r?s:arguments,c=this[o].slice();return f(a,function(e,t){var s=d["object"===r?e:t.idx];null==s&&(s=c[t.idx]),c[t.idx]=i(s,t)}),u?(n=l(u(c)),n[o]=c,n):l(c)},f(a,function(t,i){l.fn[t]||(l.fn[t]=function(n){var a,o=e.type(n),h="alpha"===t?this._hsla?"hsla":"rgba":s,l=this[h](),u=l[i.idx];return"undefined"===o?u:("function"===o&&(n=n.call(this,u),o=e.type(n)),null==n&&i.empty?this:("string"===o&&(a=r.exec(n),a&&(n=u+parseFloat(a[2])*("+"===a[1]?1:-1))),l[i.idx]=n,this[h](l)))})})}),l.hook=function(t){var i=t.split(" ");f(i,function(t,i){e.cssHooks[i]={set:function(t,n){var a,o,r="";if("transparent"!==n&&("string"!==e.type(n)||(a=s(n)))){if(n=l(a||n),!c.rgba&&1!==n._rgba[3]){for(o="backgroundColor"===i?t.parentNode:t;(""===r||"transparent"===r)&&o&&o.style;)try{r=e.css(o,"backgroundColor"),o=o.parentNode}catch(h){}n=n.blend(r&&"transparent"!==r?r:"_default")}n=n.toRgbaString()}try{t.style[i]=n}catch(h){}}},e.fx.step[i]=function(t){t.colorInit||(t.start=l(t.elem,i),t.end=l(t.end),t.colorInit=!0),e.cssHooks[i].set(t.elem,t.start.transition(t.end,t.pos))}})},l.hook(o),e.cssHooks.borderColor={expand:function(e){var t={};return f(["Top","Right","Bottom","Left"],function(i,s){t["border"+s+"Color"]=e}),t}},a=e.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}}(y),function(){function t(t){var i,s,n=t.ownerDocument.defaultView?t.ownerDocument.defaultView.getComputedStyle(t,null):t.currentStyle,a={};if(n&&n.length&&n[0]&&n[n[0]])for(s=n.length;s--;)i=n[s],"string"==typeof n[i]&&(a[e.camelCase(i)]=n[i]);else for(i in n)"string"==typeof n[i]&&(a[i]=n[i]);return a}function i(t,i){var s,a,o={};for(s in i)a=i[s],t[s]!==a&&(n[s]||(e.fx.step[s]||!isNaN(parseFloat(a)))&&(o[s]=a));return o}var s=["add","remove","toggle"],n={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};e.each(["borderLeftStyle","borderRightStyle","borderBottomStyle","borderTopStyle"],function(t,i){e.fx.step[i]=function(e){("none"!==e.end&&!e.setAttr||1===e.pos&&!e.setAttr)&&(y.style(e.elem,i,e.end),e.setAttr=!0)}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e.effects.animateClass=function(n,a,o,r){var h=e.speed(a,o,r);return this.queue(function(){var a,o=e(this),r=o.attr("class")||"",l=h.children?o.find("*").addBack():o;l=l.map(function(){var i=e(this);return{el:i,start:t(this)}}),a=function(){e.each(s,function(e,t){n[t]&&o[t+"Class"](n[t])})},a(),l=l.map(function(){return this.end=t(this.el[0]),this.diff=i(this.start,this.end),this}),o.attr("class",r),l=l.map(function(){var t=this,i=e.Deferred(),s=e.extend({},h,{queue:!1,complete:function(){i.resolve(t)}});return this.el.animate(this.diff,s),i.promise()}),e.when.apply(e,l.get()).done(function(){a(),e.each(arguments,function(){var t=this.el;e.each(this.diff,function(e){t.css(e,"")})}),h.complete.call(o[0])})})},e.fn.extend({addClass:function(t){return function(i,s,n,a){return s?e.effects.animateClass.call(this,{add:i},s,n,a):t.apply(this,arguments)}}(e.fn.addClass),removeClass:function(t){return function(i,s,n,a){return arguments.length>1?e.effects.animateClass.call(this,{remove:i},s,n,a):t.apply(this,arguments)}}(e.fn.removeClass),toggleClass:function(t){return function(i,s,n,a,o){return"boolean"==typeof s||void 0===s?n?e.effects.animateClass.call(this,s?{add:i}:{remove:i},n,a,o):t.apply(this,arguments):e.effects.animateClass.call(this,{toggle:i},s,n,a)}}(e.fn.toggleClass),switchClass:function(t,i,s,n,a){return e.effects.animateClass.call(this,{add:i,remove:t},s,n,a)}})}(),function(){function t(t,i,s,n){return e.isPlainObject(t)&&(i=t,t=t.effect),t={effect:t},null==i&&(i={}),e.isFunction(i)&&(n=i,s=null,i={}),("number"==typeof i||e.fx.speeds[i])&&(n=s,s=i,i={}),e.isFunction(s)&&(n=s,s=null),i&&e.extend(t,i),s=s||i.duration,t.duration=e.fx.off?0:"number"==typeof s?s:s in e.fx.speeds?e.fx.speeds[s]:e.fx.speeds._default,t.complete=n||i.complete,t}function i(t){return!t||"number"==typeof t||e.fx.speeds[t]?!0:"string"!=typeof t||e.effects.effect[t]?e.isFunction(t)?!0:"object"!=typeof t||t.effect?!1:!0:!0}e.extend(e.effects,{version:"1.11.3",save:function(e,t){for(var i=0;t.length>i;i++)null!==t[i]&&e.data(b+t[i],e[0].style[t[i]])},restore:function(e,t){var i,s;for(s=0;t.length>s;s++)null!==t[s]&&(i=e.data(b+t[s]),void 0===i&&(i=""),e.css(t[s],i))},setMode:function(e,t){return"toggle"===t&&(t=e.is(":hidden")?"show":"hide"),t},getBaseline:function(e,t){var i,s;switch(e[0]){case"top":i=0;break;case"middle":i=.5;break;case"bottom":i=1;break;default:i=e[0]/t.height}switch(e[1]){case"left":s=0;break;case"center":s=.5;break;case"right":s=1;break;default:s=e[1]/t.width}return{x:s,y:i}},createWrapper:function(t){if(t.parent().is(".ui-effects-wrapper"))return t.parent();var i={width:t.outerWidth(!0),height:t.outerHeight(!0),"float":t.css("float")},s=e("
").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),n={width:t.width(),height:t.height()},a=document.activeElement;try{a.id}catch(o){a=document.body}return t.wrap(s),(t[0]===a||e.contains(t[0],a))&&e(a).focus(),s=t.parent(),"static"===t.css("position")?(s.css({position:"relative"}),t.css({position:"relative"})):(e.extend(i,{position:t.css("position"),zIndex:t.css("z-index")}),e.each(["top","left","bottom","right"],function(e,s){i[s]=t.css(s),isNaN(parseInt(i[s],10))&&(i[s]="auto")}),t.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),t.css(n),s.css(i).show()},removeWrapper:function(t){var i=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),(t[0]===i||e.contains(t[0],i))&&e(i).focus()),t},setTransition:function(t,i,s,n){return n=n||{},e.each(i,function(e,i){var a=t.cssUnit(i);a[0]>0&&(n[i]=a[0]*s+a[1])}),n}}),e.fn.extend({effect:function(){function i(t){function i(){e.isFunction(a)&&a.call(n[0]),e.isFunction(t)&&t()}var n=e(this),a=s.complete,r=s.mode;(n.is(":hidden")?"hide"===r:"show"===r)?(n[r](),i()):o.call(n[0],s,i)}var s=t.apply(this,arguments),n=s.mode,a=s.queue,o=e.effects.effect[s.effect];return e.fx.off||!o?n?this[n](s.duration,s.complete):this.each(function(){s.complete&&s.complete.call(this)
+}):a===!1?this.each(i):this.queue(a||"fx",i)},show:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="show",this.effect.call(this,n)}}(e.fn.show),hide:function(e){return function(s){if(i(s))return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="hide",this.effect.call(this,n)}}(e.fn.hide),toggle:function(e){return function(s){if(i(s)||"boolean"==typeof s)return e.apply(this,arguments);var n=t.apply(this,arguments);return n.mode="toggle",this.effect.call(this,n)}}(e.fn.toggle),cssUnit:function(t){var i=this.css(t),s=[];return e.each(["em","px","%","pt"],function(e,t){i.indexOf(t)>0&&(s=[parseFloat(i),t])}),s}})}(),function(){var t={};e.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,i){t[i]=function(t){return Math.pow(t,e+2)}}),e.extend(t,{Sine:function(e){return 1-Math.cos(e*Math.PI/2)},Circ:function(e){return 1-Math.sqrt(1-e*e)},Elastic:function(e){return 0===e||1===e?e:-Math.pow(2,8*(e-1))*Math.sin((80*(e-1)-7.5)*Math.PI/15)},Back:function(e){return e*e*(3*e-2)},Bounce:function(e){for(var t,i=4;((t=Math.pow(2,--i))-1)/11>e;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*t-2)/22-e,2)}}),e.each(t,function(t,i){e.easing["easeIn"+t]=i,e.easing["easeOut"+t]=function(e){return 1-i(1-e)},e.easing["easeInOut"+t]=function(e){return.5>e?i(2*e)/2:1-i(-2*e+2)/2}})}(),e.effects,e.effects.effect.blind=function(t,i){var s,n,a,o=e(this),r=/up|down|vertical/,h=/up|left|vertical|horizontal/,l=["position","top","bottom","left","right","height","width"],u=e.effects.setMode(o,t.mode||"hide"),d=t.direction||"up",c=r.test(d),p=c?"height":"width",f=c?"top":"left",m=h.test(d),g={},v="show"===u;o.parent().is(".ui-effects-wrapper")?e.effects.save(o.parent(),l):e.effects.save(o,l),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n=s[p](),a=parseFloat(s.css(f))||0,g[p]=v?n:0,m||(o.css(c?"bottom":"right",0).css(c?"top":"left","auto").css({position:"absolute"}),g[f]=v?a:n+a),v&&(s.css(p,0),m||s.css(f,a+n)),s.animate(g,{duration:t.duration,easing:t.easing,queue:!1,complete:function(){"hide"===u&&o.hide(),e.effects.restore(o,l),e.effects.removeWrapper(o),i()}})},e.effects.effect.bounce=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"effect"),l="hide"===h,u="show"===h,d=t.direction||"up",c=t.distance,p=t.times||5,f=2*p+(u||l?1:0),m=t.duration/f,g=t.easing,v="up"===d||"down"===d?"top":"left",b="up"===d||"left"===d,y=o.queue(),_=y.length;for((u||l)&&r.push("opacity"),e.effects.save(o,r),o.show(),e.effects.createWrapper(o),c||(c=o["top"===v?"outerHeight":"outerWidth"]()/3),u&&(a={opacity:1},a[v]=0,o.css("opacity",0).css(v,b?2*-c:2*c).animate(a,m,g)),l&&(c/=Math.pow(2,p-1)),a={},a[v]=0,s=0;p>s;s++)n={},n[v]=(b?"-=":"+=")+c,o.animate(n,m,g).animate(a,m,g),c=l?2*c:c/2;l&&(n={opacity:0},n[v]=(b?"-=":"+=")+c,o.animate(n,m,g)),o.queue(function(){l&&o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}),_>1&&y.splice.apply(y,[1,0].concat(y.splice(_,f+1))),o.dequeue()},e.effects.effect.clip=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","height","width"],h=e.effects.setMode(o,t.mode||"hide"),l="show"===h,u=t.direction||"vertical",d="vertical"===u,c=d?"height":"width",p=d?"top":"left",f={};e.effects.save(o,r),o.show(),s=e.effects.createWrapper(o).css({overflow:"hidden"}),n="IMG"===o[0].tagName?s:o,a=n[c](),l&&(n.css(c,0),n.css(p,a/2)),f[c]=l?a:0,f[p]=l?0:a/2,n.animate(f,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){l||o.hide(),e.effects.restore(o,r),e.effects.removeWrapper(o),i()}})},e.effects.effect.drop=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","opacity","height","width"],o=e.effects.setMode(n,t.mode||"hide"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h?"pos":"neg",d={opacity:r?1:0};e.effects.save(n,a),n.show(),e.effects.createWrapper(n),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0)/2,r&&n.css("opacity",0).css(l,"pos"===u?-s:s),d[l]=(r?"pos"===u?"+=":"-=":"pos"===u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.explode=function(t,i){function s(){y.push(this),y.length===d*c&&n()}function n(){p.css({visibility:"visible"}),e(y).remove(),m||p.hide(),i()}var a,o,r,h,l,u,d=t.pieces?Math.round(Math.sqrt(t.pieces)):3,c=d,p=e(this),f=e.effects.setMode(p,t.mode||"hide"),m="show"===f,g=p.show().css("visibility","hidden").offset(),v=Math.ceil(p.outerWidth()/c),b=Math.ceil(p.outerHeight()/d),y=[];for(a=0;d>a;a++)for(h=g.top+a*b,u=a-(d-1)/2,o=0;c>o;o++)r=g.left+o*v,l=o-(c-1)/2,p.clone().appendTo("body").wrap("
").css({position:"absolute",visibility:"visible",left:-o*v,top:-a*b}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:v,height:b,left:r+(m?l*v:0),top:h+(m?u*b:0),opacity:m?0:1}).animate({left:r+(m?0:l*v),top:h+(m?0:u*b),opacity:m?1:0},t.duration||500,t.easing,s)},e.effects.effect.fade=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"toggle");s.animate({opacity:n},{queue:!1,duration:t.duration,easing:t.easing,complete:i})},e.effects.effect.fold=function(t,i){var s,n,a=e(this),o=["position","top","bottom","left","right","height","width"],r=e.effects.setMode(a,t.mode||"hide"),h="show"===r,l="hide"===r,u=t.size||15,d=/([0-9]+)%/.exec(u),c=!!t.horizFirst,p=h!==c,f=p?["width","height"]:["height","width"],m=t.duration/2,g={},v={};e.effects.save(a,o),a.show(),s=e.effects.createWrapper(a).css({overflow:"hidden"}),n=p?[s.width(),s.height()]:[s.height(),s.width()],d&&(u=parseInt(d[1],10)/100*n[l?0:1]),h&&s.css(c?{height:0,width:u}:{height:u,width:0}),g[f[0]]=h?n[0]:u,v[f[1]]=h?n[1]:0,s.animate(g,m,t.easing).animate(v,m,t.easing,function(){l&&a.hide(),e.effects.restore(a,o),e.effects.removeWrapper(a),i()})},e.effects.effect.highlight=function(t,i){var s=e(this),n=["backgroundImage","backgroundColor","opacity"],a=e.effects.setMode(s,t.mode||"show"),o={backgroundColor:s.css("backgroundColor")};"hide"===a&&(o.opacity=0),e.effects.save(s,n),s.show().css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(o,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===a&&s.hide(),e.effects.restore(s,n),i()}})},e.effects.effect.size=function(t,i){var s,n,a,o=e(this),r=["position","top","bottom","left","right","width","height","overflow","opacity"],h=["position","top","bottom","left","right","overflow","opacity"],l=["width","height","overflow"],u=["fontSize"],d=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],c=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],p=e.effects.setMode(o,t.mode||"effect"),f=t.restore||"effect"!==p,m=t.scale||"both",g=t.origin||["middle","center"],v=o.css("position"),b=f?r:h,y={height:0,width:0,outerHeight:0,outerWidth:0};"show"===p&&o.show(),s={height:o.height(),width:o.width(),outerHeight:o.outerHeight(),outerWidth:o.outerWidth()},"toggle"===t.mode&&"show"===p?(o.from=t.to||y,o.to=t.from||s):(o.from=t.from||("show"===p?y:s),o.to=t.to||("hide"===p?y:s)),a={from:{y:o.from.height/s.height,x:o.from.width/s.width},to:{y:o.to.height/s.height,x:o.to.width/s.width}},("box"===m||"both"===m)&&(a.from.y!==a.to.y&&(b=b.concat(d),o.from=e.effects.setTransition(o,d,a.from.y,o.from),o.to=e.effects.setTransition(o,d,a.to.y,o.to)),a.from.x!==a.to.x&&(b=b.concat(c),o.from=e.effects.setTransition(o,c,a.from.x,o.from),o.to=e.effects.setTransition(o,c,a.to.x,o.to))),("content"===m||"both"===m)&&a.from.y!==a.to.y&&(b=b.concat(u).concat(l),o.from=e.effects.setTransition(o,u,a.from.y,o.from),o.to=e.effects.setTransition(o,u,a.to.y,o.to)),e.effects.save(o,b),o.show(),e.effects.createWrapper(o),o.css("overflow","hidden").css(o.from),g&&(n=e.effects.getBaseline(g,s),o.from.top=(s.outerHeight-o.outerHeight())*n.y,o.from.left=(s.outerWidth-o.outerWidth())*n.x,o.to.top=(s.outerHeight-o.to.outerHeight)*n.y,o.to.left=(s.outerWidth-o.to.outerWidth)*n.x),o.css(o.from),("content"===m||"both"===m)&&(d=d.concat(["marginTop","marginBottom"]).concat(u),c=c.concat(["marginLeft","marginRight"]),l=r.concat(d).concat(c),o.find("*[width]").each(function(){var i=e(this),s={height:i.height(),width:i.width(),outerHeight:i.outerHeight(),outerWidth:i.outerWidth()};f&&e.effects.save(i,l),i.from={height:s.height*a.from.y,width:s.width*a.from.x,outerHeight:s.outerHeight*a.from.y,outerWidth:s.outerWidth*a.from.x},i.to={height:s.height*a.to.y,width:s.width*a.to.x,outerHeight:s.height*a.to.y,outerWidth:s.width*a.to.x},a.from.y!==a.to.y&&(i.from=e.effects.setTransition(i,d,a.from.y,i.from),i.to=e.effects.setTransition(i,d,a.to.y,i.to)),a.from.x!==a.to.x&&(i.from=e.effects.setTransition(i,c,a.from.x,i.from),i.to=e.effects.setTransition(i,c,a.to.x,i.to)),i.css(i.from),i.animate(i.to,t.duration,t.easing,function(){f&&e.effects.restore(i,l)})})),o.animate(o.to,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){0===o.to.opacity&&o.css("opacity",o.from.opacity),"hide"===p&&o.hide(),e.effects.restore(o,b),f||("static"===v?o.css({position:"relative",top:o.to.top,left:o.to.left}):e.each(["top","left"],function(e,t){o.css(t,function(t,i){var s=parseInt(i,10),n=e?o.to.left:o.to.top;return"auto"===i?n+"px":s+n+"px"})})),e.effects.removeWrapper(o),i()}})},e.effects.effect.scale=function(t,i){var s=e(this),n=e.extend(!0,{},t),a=e.effects.setMode(s,t.mode||"effect"),o=parseInt(t.percent,10)||(0===parseInt(t.percent,10)?0:"hide"===a?0:100),r=t.direction||"both",h=t.origin,l={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()},u={y:"horizontal"!==r?o/100:1,x:"vertical"!==r?o/100:1};n.effect="size",n.queue=!1,n.complete=i,"effect"!==a&&(n.origin=h||["middle","center"],n.restore=!0),n.from=t.from||("show"===a?{height:0,width:0,outerHeight:0,outerWidth:0}:l),n.to={height:l.height*u.y,width:l.width*u.x,outerHeight:l.outerHeight*u.y,outerWidth:l.outerWidth*u.x},n.fade&&("show"===a&&(n.from.opacity=0,n.to.opacity=1),"hide"===a&&(n.from.opacity=1,n.to.opacity=0)),s.effect(n)},e.effects.effect.puff=function(t,i){var s=e(this),n=e.effects.setMode(s,t.mode||"hide"),a="hide"===n,o=parseInt(t.percent,10)||150,r=o/100,h={height:s.height(),width:s.width(),outerHeight:s.outerHeight(),outerWidth:s.outerWidth()};e.extend(t,{effect:"scale",queue:!1,fade:!0,mode:n,complete:i,percent:a?o:100,from:a?h:{height:h.height*r,width:h.width*r,outerHeight:h.outerHeight*r,outerWidth:h.outerWidth*r}}),s.effect(t)},e.effects.effect.pulsate=function(t,i){var s,n=e(this),a=e.effects.setMode(n,t.mode||"show"),o="show"===a,r="hide"===a,h=o||"hide"===a,l=2*(t.times||5)+(h?1:0),u=t.duration/l,d=0,c=n.queue(),p=c.length;for((o||!n.is(":visible"))&&(n.css("opacity",0).show(),d=1),s=1;l>s;s++)n.animate({opacity:d},u,t.easing),d=1-d;n.animate({opacity:d},u,t.easing),n.queue(function(){r&&n.hide(),i()}),p>1&&c.splice.apply(c,[1,0].concat(c.splice(p,l+1))),n.dequeue()},e.effects.effect.shake=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","height","width"],o=e.effects.setMode(n,t.mode||"effect"),r=t.direction||"left",h=t.distance||20,l=t.times||3,u=2*l+1,d=Math.round(t.duration/u),c="up"===r||"down"===r?"top":"left",p="up"===r||"left"===r,f={},m={},g={},v=n.queue(),b=v.length;for(e.effects.save(n,a),n.show(),e.effects.createWrapper(n),f[c]=(p?"-=":"+=")+h,m[c]=(p?"+=":"-=")+2*h,g[c]=(p?"-=":"+=")+2*h,n.animate(f,d,t.easing),s=1;l>s;s++)n.animate(m,d,t.easing).animate(g,d,t.easing);n.animate(m,d,t.easing).animate(f,d/2,t.easing).queue(function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}),b>1&&v.splice.apply(v,[1,0].concat(v.splice(b,u+1))),n.dequeue()},e.effects.effect.slide=function(t,i){var s,n=e(this),a=["position","top","bottom","left","right","width","height"],o=e.effects.setMode(n,t.mode||"show"),r="show"===o,h=t.direction||"left",l="up"===h||"down"===h?"top":"left",u="up"===h||"left"===h,d={};e.effects.save(n,a),n.show(),s=t.distance||n["top"===l?"outerHeight":"outerWidth"](!0),e.effects.createWrapper(n).css({overflow:"hidden"}),r&&n.css(l,u?isNaN(s)?"-"+s:-s:s),d[l]=(r?u?"+=":"-=":u?"-=":"+=")+s,n.animate(d,{queue:!1,duration:t.duration,easing:t.easing,complete:function(){"hide"===o&&n.hide(),e.effects.restore(n,a),e.effects.removeWrapper(n),i()}})},e.effects.effect.transfer=function(t,i){var s=e(this),n=e(t.to),a="fixed"===n.css("position"),o=e("body"),r=a?o.scrollTop():0,h=a?o.scrollLeft():0,l=n.offset(),u={top:l.top-r,left:l.left-h,height:n.innerHeight(),width:n.innerWidth()},d=s.offset(),c=e("
").appendTo(document.body).addClass(t.className).css({top:d.top-r,left:d.left-h,height:s.innerHeight(),width:s.innerWidth(),position:a?"fixed":"absolute"}).animate(u,t.duration,t.easing,function(){c.remove(),i()})}});
\ No newline at end of file
diff --git a/web/pgadmin/static/js/jquery-ui/jquery.event.drag-2.2.js b/web/pgadmin/static/js/jquery-ui/jquery.event.drag-2.2.js
new file mode 100644
index 000000000..e459407bc
--- /dev/null
+++ b/web/pgadmin/static/js/jquery-ui/jquery.event.drag-2.2.js
@@ -0,0 +1,402 @@
+/*!
+ * jquery.event.drag - v 2.2
+ * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
+ * Open Source MIT License - http://threedubmedia.com/code/license
+ */
+// Created: 2008-06-04
+// Updated: 2012-05-21
+// REQUIRES: jquery 1.7.x
+
+;(function( $ ){
+
+// add the jquery instance method
+$.fn.drag = function( str, arg, opts ){
+ // figure out the event type
+ var type = typeof str == "string" ? str : "",
+ // figure out the event handler...
+ fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
+ // fix the event type
+ if ( type.indexOf("drag") !== 0 )
+ type = "drag"+ type;
+ // were options passed
+ opts = ( str == fn ? arg : opts ) || {};
+ // trigger or bind event handler
+ return fn ? this.bind( type, opts, fn ) : this.trigger( type );
+};
+
+// local refs (increase compression)
+var $event = $.event,
+$special = $event.special,
+// configure the drag special event
+drag = $special.drag = {
+
+ // these are the default settings
+ defaults: {
+ which: 1, // mouse button pressed to start drag sequence
+ distance: 0, // distance dragged before dragstart
+ not: ':input', // selector to suppress dragging on target elements
+ handle: null, // selector to match handle target elements
+ relative: false, // true to use "position", false to use "offset"
+ drop: true, // false to suppress drop events, true or selector to allow
+ click: false // false to suppress click events after dragend (no proxy)
+ },
+
+ // the key name for stored drag data
+ datakey: "dragdata",
+
+ // prevent bubbling for better performance
+ noBubble: true,
+
+ // count bound related events
+ add: function( obj ){
+ // read the interaction data
+ var data = $.data( this, drag.datakey ),
+ // read any passed options
+ opts = obj.data || {};
+ // count another realted event
+ data.related += 1;
+ // extend data options bound with this event
+ // don't iterate "opts" in case it is a node
+ $.each( drag.defaults, function( key, def ){
+ if ( opts[ key ] !== undefined )
+ data[ key ] = opts[ key ];
+ });
+ },
+
+ // forget unbound related events
+ remove: function(){
+ $.data( this, drag.datakey ).related -= 1;
+ },
+
+ // configure interaction, capture settings
+ setup: function(){
+ // check for related events
+ if ( $.data( this, drag.datakey ) )
+ return;
+ // initialize the drag data with copied defaults
+ var data = $.extend({ related:0 }, drag.defaults );
+ // store the interaction data
+ $.data( this, drag.datakey, data );
+ // bind the mousedown event, which starts drag interactions
+ $event.add( this, "touchstart mousedown", drag.init, data );
+ // prevent image dragging in IE...
+ if ( this.attachEvent )
+ this.attachEvent("ondragstart", drag.dontstart );
+ },
+
+ // destroy configured interaction
+ teardown: function(){
+ var data = $.data( this, drag.datakey ) || {};
+ // check for related events
+ if ( data.related )
+ return;
+ // remove the stored data
+ $.removeData( this, drag.datakey );
+ // remove the mousedown event
+ $event.remove( this, "touchstart mousedown", drag.init );
+ // enable text selection
+ drag.textselect( true );
+ // un-prevent image dragging in IE...
+ if ( this.detachEvent )
+ this.detachEvent("ondragstart", drag.dontstart );
+ },
+
+ // initialize the interaction
+ init: function( event ){
+ // sorry, only one touch at a time
+ if ( drag.touched )
+ return;
+ // the drag/drop interaction data
+ var dd = event.data, results;
+ // check the which directive
+ if ( event.which != 0 && dd.which > 0 && event.which != dd.which )
+ return;
+ // check for suppressed selector
+ if ( $( event.target ).is( dd.not ) )
+ return;
+ // check for handle selector
+ if ( dd.handle && !$( event.target ).closest( dd.handle, event.currentTarget ).length )
+ return;
+
+ drag.touched = event.type == 'touchstart' ? this : null;
+ dd.propagates = 1;
+ dd.mousedown = this;
+ dd.interactions = [ drag.interaction( this, dd ) ];
+ dd.target = event.target;
+ dd.pageX = event.pageX;
+ dd.pageY = event.pageY;
+ dd.dragging = null;
+ // handle draginit event...
+ results = drag.hijack( event, "draginit", dd );
+ // early cancel
+ if ( !dd.propagates )
+ return;
+ // flatten the result set
+ results = drag.flatten( results );
+ // insert new interaction elements
+ if ( results && results.length ){
+ dd.interactions = [];
+ $.each( results, function(){
+ dd.interactions.push( drag.interaction( this, dd ) );
+ });
+ }
+ // remember how many interactions are propagating
+ dd.propagates = dd.interactions.length;
+ // locate and init the drop targets
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd );
+ // disable text selection
+ drag.textselect( false );
+ // bind additional events...
+ if ( drag.touched )
+ $event.add( drag.touched, "touchmove touchend", drag.handler, dd );
+ else
+ $event.add( document, "mousemove mouseup", drag.handler, dd );
+ // helps prevent text selection or scrolling
+ if ( !drag.touched || dd.live )
+ return false;
+ },
+
+ // returns an interaction object
+ interaction: function( elem, dd ){
+ var offset = $( elem )[ dd.relative ? "position" : "offset" ]() || { top:0, left:0 };
+ return {
+ drag: elem,
+ callback: new drag.callback(),
+ droppable: [],
+ offset: offset
+ };
+ },
+
+ // handle drag-releatd DOM events
+ handler: function( event ){
+ // read the data before hijacking anything
+ var dd = event.data;
+ // handle various events
+ switch ( event.type ){
+ // mousemove, check distance, start dragging
+ case !dd.dragging && 'touchmove':
+ event.preventDefault();
+ case !dd.dragging && 'mousemove':
+ // drag tolerance, x≤ + y≤ = distance≤
+ if ( Math.pow( event.pageX-dd.pageX, 2 ) + Math.pow( event.pageY-dd.pageY, 2 ) < Math.pow( dd.distance, 2 ) )
+ break; // distance tolerance not reached
+ event.target = dd.target; // force target from "mousedown" event (fix distance issue)
+ drag.hijack( event, "dragstart", dd ); // trigger "dragstart"
+ if ( dd.propagates ) // "dragstart" not rejected
+ dd.dragging = true; // activate interaction
+ // mousemove, dragging
+ case 'touchmove':
+ event.preventDefault();
+ case 'mousemove':
+ if ( dd.dragging ){
+ // trigger "drag"
+ drag.hijack( event, "drag", dd );
+ if ( dd.propagates ){
+ // manage drop events
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "dropstart", "dropend"
+ break; // "drag" not rejected, stop
+ }
+ event.type = "mouseup"; // helps "drop" handler behave
+ }
+ // mouseup, stop dragging
+ case 'touchend':
+ case 'mouseup':
+ default:
+ if ( drag.touched )
+ $event.remove( drag.touched, "touchmove touchend", drag.handler ); // remove touch events
+ else
+ $event.remove( document, "mousemove mouseup", drag.handler ); // remove page events
+ if ( dd.dragging ){
+ if ( dd.drop !== false && $special.drop )
+ $special.drop.handler( event, dd ); // "drop"
+ drag.hijack( event, "dragend", dd ); // trigger "dragend"
+ }
+ drag.textselect( true ); // enable text selection
+ // if suppressing click events...
+ if ( dd.click === false && dd.dragging )
+ $.data( dd.mousedown, "suppress.click", new Date().getTime() + 5 );
+ dd.dragging = drag.touched = false; // deactivate element
+ break;
+ }
+ },
+
+ // re-use event object for custom events
+ hijack: function( event, type, dd, x, elem ){
+ // not configured
+ if ( !dd )
+ return;
+ // remember the original event and type
+ var orig = { event:event.originalEvent, type:event.type },
+ // is the event drag related or drog related?
+ mode = type.indexOf("drop") ? "drag" : "drop",
+ // iteration vars
+ result, i = x || 0, ia, $elems, callback,
+ len = !isNaN( x ) ? x : dd.interactions.length;
+ // modify the event type
+ event.type = type;
+ // remove the original event
+ event.originalEvent = null;
+ // initialize the results
+ dd.results = [];
+ // handle each interacted element
+ do if ( ia = dd.interactions[ i ] ){
+ // validate the interaction
+ if ( type !== "dragend" && ia.cancelled )
+ continue;
+ // set the dragdrop properties on the event object
+ callback = drag.properties( event, dd, ia );
+ // prepare for more results
+ ia.results = [];
+ // handle each element
+ $( elem || ia[ mode ] || dd.droppable ).each(function( p, subject ){
+ // identify drag or drop targets individually
+ callback.target = subject;
+ // force propagtion of the custom event
+ event.isPropagationStopped = function(){ return false; };
+ // handle the event
+ result = subject ? $event.dispatch.call( subject, event, callback ) : null;
+ // stop the drag interaction for this element
+ if ( result === false ){
+ if ( mode == "drag" ){
+ ia.cancelled = true;
+ dd.propagates -= 1;
+ }
+ if ( type == "drop" ){
+ ia[ mode ][p] = null;
+ }
+ }
+ // assign any dropinit elements
+ else if ( type == "dropinit" )
+ ia.droppable.push( drag.element( result ) || subject );
+ // accept a returned proxy element
+ if ( type == "dragstart" )
+ ia.proxy = $( drag.element( result ) || ia.drag )[0];
+ // remember this result
+ ia.results.push( result );
+ // forget the event result, for recycling
+ delete event.result;
+ // break on cancelled handler
+ if ( type !== "dropinit" )
+ return result;
+ });
+ // flatten the results
+ dd.results[ i ] = drag.flatten( ia.results );
+ // accept a set of valid drop targets
+ if ( type == "dropinit" )
+ ia.droppable = drag.flatten( ia.droppable );
+ // locate drop targets
+ if ( type == "dragstart" && !ia.cancelled )
+ callback.update();
+ }
+ while ( ++i < len )
+ // restore the original event & type
+ event.type = orig.type;
+ event.originalEvent = orig.event;
+ // return all handler results
+ return drag.flatten( dd.results );
+ },
+
+ // extend the callback object with drag/drop properties...
+ properties: function( event, dd, ia ){
+ var obj = ia.callback;
+ // elements
+ obj.drag = ia.drag;
+ obj.proxy = ia.proxy || ia.drag;
+ // starting mouse position
+ obj.startX = dd.pageX;
+ obj.startY = dd.pageY;
+ // current distance dragged
+ obj.deltaX = event.pageX - dd.pageX;
+ obj.deltaY = event.pageY - dd.pageY;
+ // original element position
+ obj.originalX = ia.offset.left;
+ obj.originalY = ia.offset.top;
+ // adjusted element position
+ obj.offsetX = obj.originalX + obj.deltaX;
+ obj.offsetY = obj.originalY + obj.deltaY;
+ // assign the drop targets information
+ obj.drop = drag.flatten( ( ia.drop || [] ).slice() );
+ obj.available = drag.flatten( ( ia.droppable || [] ).slice() );
+ return obj;
+ },
+
+ // determine is the argument is an element or jquery instance
+ element: function( arg ){
+ if ( arg && ( arg.jquery || arg.nodeType == 1 ) )
+ return arg;
+ },
+
+ // flatten nested jquery objects and arrays into a single dimension array
+ flatten: function( arr ){
+ return $.map( arr, function( member ){
+ return member && member.jquery ? $.makeArray( member ) :
+ member && member.length ? drag.flatten( member ) : member;
+ });
+ },
+
+ // toggles text selection attributes ON (true) or OFF (false)
+ textselect: function( bool ){
+ $( document )[ bool ? "unbind" : "bind" ]("selectstart", drag.dontstart )
+ .css("MozUserSelect", bool ? "" : "none" );
+ // .attr("unselectable", bool ? "off" : "on" )
+ document.unselectable = bool ? "off" : "on";
+ },
+
+ // suppress "selectstart" and "ondragstart" events
+ dontstart: function(){
+ return false;
+ },
+
+ // a callback instance contructor
+ callback: function(){}
+
+};
+
+// callback methods
+drag.callback.prototype = {
+ update: function(){
+ if ( $special.drop && this.available.length )
+ $.each( this.available, function( i ){
+ $special.drop.locate( this, i );
+ });
+ }
+};
+
+// patch $.event.$dispatch to allow suppressing clicks
+var $dispatch = $event.dispatch;
+$event.dispatch = function( event ){
+ if ( $.data( this, "suppress."+ event.type ) - new Date().getTime() > 0 ){
+ $.removeData( this, "suppress."+ event.type );
+ return;
+ }
+ return $dispatch.apply( this, arguments );
+};
+
+// event fix hooks for touch events...
+var touchHooks =
+$event.fixHooks.touchstart =
+$event.fixHooks.touchmove =
+$event.fixHooks.touchend =
+$event.fixHooks.touchcancel = {
+ props: "clientX clientY pageX pageY screenX screenY".split( " " ),
+ filter: function( event, orig ) {
+ if ( orig ){
+ var touched = ( orig.touches && orig.touches[0] )
+ || ( orig.changedTouches && orig.changedTouches[0] )
+ || null;
+ // iOS webkit: touchstart, touchmove, touchend
+ if ( touched )
+ $.each( touchHooks.props, function( i, prop ){
+ event[ prop ] = touched[ prop ];
+ });
+ }
+ return event;
+ }
+};
+
+// share the same special event configuration with related events...
+$special.draginit = $special.dragstart = $special.dragend = drag;
+
+})( jQuery );
\ No newline at end of file
diff --git a/web/pgadmin/static/js/jquery-ui/jquery.event.drop-2.2.js b/web/pgadmin/static/js/jquery-ui/jquery.event.drop-2.2.js
new file mode 100644
index 000000000..38d484344
--- /dev/null
+++ b/web/pgadmin/static/js/jquery-ui/jquery.event.drop-2.2.js
@@ -0,0 +1,302 @@
+/*!
+ * jquery.event.drop - v 2.2
+ * Copyright (c) 2010 Three Dub Media - http://threedubmedia.com
+ * Open Source MIT License - http://threedubmedia.com/code/license
+ */
+// Created: 2008-06-04
+// Updated: 2012-05-21
+// REQUIRES: jquery 1.7.x, event.drag 2.2
+
+;(function($){ // secure $ jQuery alias
+
+// Events: drop, dropstart, dropend
+
+// add the jquery instance method
+$.fn.drop = function( str, arg, opts ){
+ // figure out the event type
+ var type = typeof str == "string" ? str : "",
+ // figure out the event handler...
+ fn = $.isFunction( str ) ? str : $.isFunction( arg ) ? arg : null;
+ // fix the event type
+ if ( type.indexOf("drop") !== 0 )
+ type = "drop"+ type;
+ // were options passed
+ opts = ( str == fn ? arg : opts ) || {};
+ // trigger or bind event handler
+ return fn ? this.bind( type, opts, fn ) : this.trigger( type );
+};
+
+// DROP MANAGEMENT UTILITY
+// returns filtered drop target elements, caches their positions
+$.drop = function( opts ){
+ opts = opts || {};
+ // safely set new options...
+ drop.multi = opts.multi === true ? Infinity :
+ opts.multi === false ? 1 : !isNaN( opts.multi ) ? opts.multi : drop.multi;
+ drop.delay = opts.delay || drop.delay;
+ drop.tolerance = $.isFunction( opts.tolerance ) ? opts.tolerance :
+ opts.tolerance === null ? null : drop.tolerance;
+ drop.mode = opts.mode || drop.mode || 'intersect';
+};
+
+// local refs (increase compression)
+var $event = $.event,
+$special = $event.special,
+// configure the drop special event
+drop = $.event.special.drop = {
+
+ // these are the default settings
+ multi: 1, // allow multiple drop winners per dragged element
+ delay: 20, // async timeout delay
+ mode: 'overlap', // drop tolerance mode
+
+ // internal cache
+ targets: [],
+
+ // the key name for stored drop data
+ datakey: "dropdata",
+
+ // prevent bubbling for better performance
+ noBubble: true,
+
+ // count bound related events
+ add: function( obj ){
+ // read the interaction data
+ var data = $.data( this, drop.datakey );
+ // count another realted event
+ data.related += 1;
+ },
+
+ // forget unbound related events
+ remove: function(){
+ $.data( this, drop.datakey ).related -= 1;
+ },
+
+ // configure the interactions
+ setup: function(){
+ // check for related events
+ if ( $.data( this, drop.datakey ) )
+ return;
+ // initialize the drop element data
+ var data = {
+ related: 0,
+ active: [],
+ anyactive: 0,
+ winner: 0,
+ location: {}
+ };
+ // store the drop data on the element
+ $.data( this, drop.datakey, data );
+ // store the drop target in internal cache
+ drop.targets.push( this );
+ },
+
+ // destroy the configure interaction
+ teardown: function(){
+ var data = $.data( this, drop.datakey ) || {};
+ // check for related events
+ if ( data.related )
+ return;
+ // remove the stored data
+ $.removeData( this, drop.datakey );
+ // reference the targeted element
+ var element = this;
+ // remove from the internal cache
+ drop.targets = $.grep( drop.targets, function( target ){
+ return ( target !== element );
+ });
+ },
+
+ // shared event handler
+ handler: function( event, dd ){
+ // local vars
+ var results, $targets;
+ // make sure the right data is available
+ if ( !dd )
+ return;
+ // handle various events
+ switch ( event.type ){
+ // draginit, from $.event.special.drag
+ case 'mousedown': // DROPINIT >>
+ case 'touchstart': // DROPINIT >>
+ // collect and assign the drop targets
+ $targets = $( drop.targets );
+ if ( typeof dd.drop == "string" )
+ $targets = $targets.filter( dd.drop );
+ // reset drop data winner properties
+ $targets.each(function(){
+ var data = $.data( this, drop.datakey );
+ data.active = [];
+ data.anyactive = 0;
+ data.winner = 0;
+ });
+ // set available target elements
+ dd.droppable = $targets;
+ // activate drop targets for the initial element being dragged
+ $special.drag.hijack( event, "dropinit", dd );
+ break;
+ // drag, from $.event.special.drag
+ case 'mousemove': // TOLERATE >>
+ case 'touchmove': // TOLERATE >>
+ drop.event = event; // store the mousemove event
+ if ( !drop.timer )
+ // monitor drop targets
+ drop.tolerate( dd );
+ break;
+ // dragend, from $.event.special.drag
+ case 'mouseup': // DROP >> DROPEND >>
+ case 'touchend': // DROP >> DROPEND >>
+ drop.timer = clearTimeout( drop.timer ); // delete timer
+ if ( dd.propagates ){
+ $special.drag.hijack( event, "drop", dd );
+ $special.drag.hijack( event, "dropend", dd );
+ }
+ break;
+
+ }
+ },
+
+ // returns the location positions of an element
+ locate: function( elem, index ){
+ var data = $.data( elem, drop.datakey ),
+ $elem = $( elem ),
+ posi = $elem.offset() || {},
+ height = $elem.outerHeight(),
+ width = $elem.outerWidth(),
+ location = {
+ elem: elem,
+ width: width,
+ height: height,
+ top: posi.top,
+ left: posi.left,
+ right: posi.left + width,
+ bottom: posi.top + height
+ };
+ // drag elements might not have dropdata
+ if ( data ){
+ data.location = location;
+ data.index = index;
+ data.elem = elem;
+ }
+ return location;
+ },
+
+ // test the location positions of an element against another OR an X,Y coord
+ contains: function( target, test ){ // target { location } contains test [x,y] or { location }
+ return ( ( test[0] || test.left ) >= target.left && ( test[0] || test.right ) <= target.right
+ && ( test[1] || test.top ) >= target.top && ( test[1] || test.bottom ) <= target.bottom );
+ },
+
+ // stored tolerance modes
+ modes: { // fn scope: "$.event.special.drop" object
+ // target with mouse wins, else target with most overlap wins
+ 'intersect': function( event, proxy, target ){
+ return this.contains( target, [ event.pageX, event.pageY ] ) ? // check cursor
+ 1e9 : this.modes.overlap.apply( this, arguments ); // check overlap
+ },
+ // target with most overlap wins
+ 'overlap': function( event, proxy, target ){
+ // calculate the area of overlap...
+ return Math.max( 0, Math.min( target.bottom, proxy.bottom ) - Math.max( target.top, proxy.top ) )
+ * Math.max( 0, Math.min( target.right, proxy.right ) - Math.max( target.left, proxy.left ) );
+ },
+ // proxy is completely contained within target bounds
+ 'fit': function( event, proxy, target ){
+ return this.contains( target, proxy ) ? 1 : 0;
+ },
+ // center of the proxy is contained within target bounds
+ 'middle': function( event, proxy, target ){
+ return this.contains( target, [ proxy.left + proxy.width * .5, proxy.top + proxy.height * .5 ] ) ? 1 : 0;
+ }
+ },
+
+ // sort drop target cache by by winner (dsc), then index (asc)
+ sort: function( a, b ){
+ return ( b.winner - a.winner ) || ( a.index - b.index );
+ },
+
+ // async, recursive tolerance execution
+ tolerate: function( dd ){
+ // declare local refs
+ var i, drp, drg, data, arr, len, elem,
+ // interaction iteration variables
+ x = 0, ia, end = dd.interactions.length,
+ // determine the mouse coords
+ xy = [ drop.event.pageX, drop.event.pageY ],
+ // custom or stored tolerance fn
+ tolerance = drop.tolerance || drop.modes[ drop.mode ];
+ // go through each passed interaction...
+ do if ( ia = dd.interactions[x] ){
+ // check valid interaction
+ if ( !ia )
+ return;
+ // initialize or clear the drop data
+ ia.drop = [];
+ // holds the drop elements
+ arr = [];
+ len = ia.droppable.length;
+ // determine the proxy location, if needed
+ if ( tolerance )
+ drg = drop.locate( ia.proxy );
+ // reset the loop
+ i = 0;
+ // loop each stored drop target
+ do if ( elem = ia.droppable[i] ){
+ data = $.data( elem, drop.datakey );
+ drp = data.location;
+ if ( !drp ) continue;
+ // find a winner: tolerance function is defined, call it
+ data.winner = tolerance ? tolerance.call( drop, drop.event, drg, drp )
+ // mouse position is always the fallback
+ : drop.contains( drp, xy ) ? 1 : 0;
+ arr.push( data );
+ } while ( ++i < len ); // loop
+ // sort the drop targets
+ arr.sort( drop.sort );
+ // reset the loop
+ i = 0;
+ // loop through all of the targets again
+ do if ( data = arr[ i ] ){
+ // winners...
+ if ( data.winner && ia.drop.length < drop.multi ){
+ // new winner... dropstart
+ if ( !data.active[x] && !data.anyactive ){
+ // check to make sure that this is not prevented
+ if ( $special.drag.hijack( drop.event, "dropstart", dd, x, data.elem )[0] !== false ){
+ data.active[x] = 1;
+ data.anyactive += 1;
+ }
+ // if false, it is not a winner
+ else
+ data.winner = 0;
+ }
+ // if it is still a winner
+ if ( data.winner )
+ ia.drop.push( data.elem );
+ }
+ // losers...
+ else if ( data.active[x] && data.anyactive == 1 ){
+ // former winner... dropend
+ $special.drag.hijack( drop.event, "dropend", dd, x, data.elem );
+ data.active[x] = 0;
+ data.anyactive -= 1;
+ }
+ } while ( ++i < len ); // loop
+ } while ( ++x < end ) // loop
+ // check if the mouse is still moving or is idle
+ if ( drop.last && xy[0] == drop.last.pageX && xy[1] == drop.last.pageY )
+ delete drop.timer; // idle, don't recurse
+ else // recurse
+ drop.timer = setTimeout(function(){
+ drop.tolerate( dd );
+ }, drop.delay );
+ // remember event, to compare idleness
+ drop.last = drop.event;
+ }
+
+};
+
+// share the same special event configuration with related events...
+$special.dropinit = $special.dropstart = $special.dropend = drop;
+
+})(jQuery); // confine scope
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.css b/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.css
new file mode 100644
index 000000000..bcbb37584
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.css
@@ -0,0 +1,31 @@
+.slick-columnpicker {
+ border: 1px solid #718BB7;
+ background: #f0f0f0;
+ padding: 6px;
+ -moz-box-shadow: 2px 2px 2px silver;
+ -webkit-box-shadow: 2px 2px 2px silver;
+ box-shadow: 2px 2px 2px silver;
+ min-width: 100px;
+ cursor: default;
+}
+
+.slick-columnpicker li {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ background: none;
+}
+
+.slick-columnpicker input {
+ margin: 4px;
+}
+
+.slick-columnpicker li a {
+ display: block;
+ padding: 4px;
+ font-weight: bold;
+}
+
+.slick-columnpicker li a:hover {
+ background: white;
+}
diff --git a/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.js b/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.js
new file mode 100644
index 000000000..dc1672099
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/controls/slick.columnpicker.js
@@ -0,0 +1,152 @@
+(function ($) {
+ function SlickColumnPicker(columns, grid, options) {
+ var $menu;
+ var columnCheckboxes;
+
+ var defaults = {
+ fadeSpeed:250
+ };
+
+ function init() {
+ grid.onHeaderContextMenu.subscribe(handleHeaderContextMenu);
+ grid.onColumnsReordered.subscribe(updateColumnOrder);
+ options = $.extend({}, defaults, options);
+
+ $menu = $("
").appendTo(document.body);
+
+ $menu.bind("mouseleave", function (e) {
+ $(this).fadeOut(options.fadeSpeed)
+ });
+ $menu.bind("click", updateColumn);
+
+ }
+
+ function destroy() {
+ grid.onHeaderContextMenu.unsubscribe(handleHeaderContextMenu);
+ grid.onColumnsReordered.unsubscribe(updateColumnOrder);
+ $menu.remove();
+ }
+
+ function handleHeaderContextMenu(e, args) {
+ e.preventDefault();
+ $menu.empty();
+ updateColumnOrder();
+ columnCheckboxes = [];
+
+ var $li, $input;
+ for (var i = 0; i < columns.length; i++) {
+ $li = $("
").appendTo($menu);
+ $input = $("
").data("column-id", columns[i].id);
+ columnCheckboxes.push($input);
+
+ if (grid.getColumnIndex(columns[i].id) != null) {
+ $input.attr("checked", "checked");
+ }
+
+ $("
")
+ .text(columns[i].name)
+ .prepend($input)
+ .appendTo($li);
+ }
+
+ $("
").appendTo($menu);
+ $li = $("
").appendTo($menu);
+ $input = $("
").data("option", "autoresize");
+ $("
")
+ .text("Force fit columns")
+ .prepend($input)
+ .appendTo($li);
+ if (grid.getOptions().forceFitColumns) {
+ $input.attr("checked", "checked");
+ }
+
+ $li = $("
").appendTo($menu);
+ $input = $("
").data("option", "syncresize");
+ $("
")
+ .text("Synchronous resize")
+ .prepend($input)
+ .appendTo($li);
+ if (grid.getOptions().syncColumnCellResize) {
+ $input.attr("checked", "checked");
+ }
+
+ $menu
+ .css("top", e.pageY - 10)
+ .css("left", e.pageX - 10)
+ .fadeIn(options.fadeSpeed);
+ }
+
+ function updateColumnOrder() {
+ // Because columns can be reordered, we have to update the `columns`
+ // to reflect the new order, however we can't just take `grid.getColumns()`,
+ // as it does not include columns currently hidden by the picker.
+ // We create a new `columns` structure by leaving currently-hidden
+ // columns in their original ordinal position and interleaving the results
+ // of the current column sort.
+ var current = grid.getColumns().slice(0);
+ var ordered = new Array(columns.length);
+ for (var i = 0; i < ordered.length; i++) {
+ if ( grid.getColumnIndex(columns[i].id) === undefined ) {
+ // If the column doesn't return a value from getColumnIndex,
+ // it is hidden. Leave it in this position.
+ ordered[i] = columns[i];
+ } else {
+ // Otherwise, grab the next visible column.
+ ordered[i] = current.shift();
+ }
+ }
+ columns = ordered;
+ }
+
+ function updateColumn(e) {
+ if ($(e.target).data("option") == "autoresize") {
+ if (e.target.checked) {
+ grid.setOptions({forceFitColumns:true});
+ grid.autosizeColumns();
+ } else {
+ grid.setOptions({forceFitColumns:false});
+ }
+ return;
+ }
+
+ if ($(e.target).data("option") == "syncresize") {
+ if (e.target.checked) {
+ grid.setOptions({syncColumnCellResize:true});
+ } else {
+ grid.setOptions({syncColumnCellResize:false});
+ }
+ return;
+ }
+
+ if ($(e.target).is(":checkbox")) {
+ var visibleColumns = [];
+ $.each(columnCheckboxes, function (i, e) {
+ if ($(this).is(":checked")) {
+ visibleColumns.push(columns[i]);
+ }
+ });
+
+ if (!visibleColumns.length) {
+ $(e.target).attr("checked", "checked");
+ return;
+ }
+
+ grid.setColumns(visibleColumns);
+ }
+ }
+
+ function getAllColumns() {
+ return columns;
+ }
+
+ init();
+
+ return {
+ "getAllColumns": getAllColumns,
+ "destroy": destroy
+ };
+ }
+
+ // Slick.Controls.ColumnPicker
+ $.extend(true, window, { Slick:{ Controls:{ ColumnPicker:SlickColumnPicker }}});
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/controls/slick.pager.css b/web/pgadmin/static/js/slickgrid/controls/slick.pager.css
new file mode 100644
index 000000000..e360ec67f
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/controls/slick.pager.css
@@ -0,0 +1,41 @@
+.slick-pager {
+ width: 100%;
+ height: 26px;
+ border: 1px solid gray;
+ border-top: 0;
+ background: url('../images/header-columns-bg.gif') repeat-x center bottom;
+ vertical-align: middle;
+}
+
+.slick-pager .slick-pager-status {
+ display: inline-block;
+ padding: 6px;
+}
+
+.slick-pager .ui-icon-container {
+ display: inline-block;
+ margin: 2px;
+ border-color: gray;
+}
+
+.slick-pager .slick-pager-nav {
+ display: inline-block;
+ float: left;
+ padding: 2px;
+}
+
+.slick-pager .slick-pager-settings {
+ display: block;
+ float: right;
+ padding: 2px;
+}
+
+.slick-pager .slick-pager-settings * {
+ vertical-align: middle;
+}
+
+.slick-pager .slick-pager-settings a {
+ padding: 2px;
+ text-decoration: underline;
+ cursor: pointer;
+}
diff --git a/web/pgadmin/static/js/slickgrid/controls/slick.pager.js b/web/pgadmin/static/js/slickgrid/controls/slick.pager.js
new file mode 100644
index 000000000..b9c6b7413
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/controls/slick.pager.js
@@ -0,0 +1,147 @@
+(function ($) {
+ function SlickGridPager(dataView, grid, $container) {
+ var $status;
+
+ function init() {
+ dataView.onPagingInfoChanged.subscribe(function (e, pagingInfo) {
+ updatePager(pagingInfo);
+ });
+
+ constructPagerUI();
+ updatePager(dataView.getPagingInfo());
+ }
+
+ function getNavState() {
+ var cannotLeaveEditMode = !Slick.GlobalEditorLock.commitCurrentEdit();
+ var pagingInfo = dataView.getPagingInfo();
+ var lastPage = pagingInfo.totalPages - 1;
+
+ return {
+ canGotoFirst: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
+ canGotoLast: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum != lastPage,
+ canGotoPrev: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum > 0,
+ canGotoNext: !cannotLeaveEditMode && pagingInfo.pageSize != 0 && pagingInfo.pageNum < lastPage,
+ pagingInfo: pagingInfo
+ }
+ }
+
+ function setPageSize(n) {
+ dataView.setRefreshHints({
+ isFilterUnchanged: true
+ });
+ dataView.setPagingOptions({pageSize: n});
+ }
+
+ function gotoFirst() {
+ if (getNavState().canGotoFirst) {
+ dataView.setPagingOptions({pageNum: 0});
+ }
+ }
+
+ function gotoLast() {
+ var state = getNavState();
+ if (state.canGotoLast) {
+ dataView.setPagingOptions({pageNum: state.pagingInfo.totalPages - 1});
+ }
+ }
+
+ function gotoPrev() {
+ var state = getNavState();
+ if (state.canGotoPrev) {
+ dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum - 1});
+ }
+ }
+
+ function gotoNext() {
+ var state = getNavState();
+ if (state.canGotoNext) {
+ dataView.setPagingOptions({pageNum: state.pagingInfo.pageNum + 1});
+ }
+ }
+
+ function constructPagerUI() {
+ $container.empty();
+
+ var $nav = $("").appendTo($container);
+ var $settings = $("").appendTo($container);
+ $status = $("").appendTo($container);
+
+ $settings
+ .append("
Show: AllAuto2550100");
+
+ $settings.find("a[data]").click(function (e) {
+ var pagesize = $(e.target).attr("data");
+ if (pagesize != undefined) {
+ if (pagesize == -1) {
+ var vp = grid.getViewport();
+ setPageSize(vp.bottom - vp.top);
+ } else {
+ setPageSize(parseInt(pagesize));
+ }
+ }
+ });
+
+ var icon_prefix = "
";
+
+ $(icon_prefix + "ui-icon-lightbulb" + icon_suffix)
+ .click(function () {
+ $(".slick-pager-settings-expanded").toggle()
+ })
+ .appendTo($settings);
+
+ $(icon_prefix + "ui-icon-seek-first" + icon_suffix)
+ .click(gotoFirst)
+ .appendTo($nav);
+
+ $(icon_prefix + "ui-icon-seek-prev" + icon_suffix)
+ .click(gotoPrev)
+ .appendTo($nav);
+
+ $(icon_prefix + "ui-icon-seek-next" + icon_suffix)
+ .click(gotoNext)
+ .appendTo($nav);
+
+ $(icon_prefix + "ui-icon-seek-end" + icon_suffix)
+ .click(gotoLast)
+ .appendTo($nav);
+
+ $container.find(".ui-icon-container")
+ .hover(function () {
+ $(this).toggleClass("ui-state-hover");
+ });
+
+ $container.children().wrapAll("");
+ }
+
+
+ function updatePager(pagingInfo) {
+ var state = getNavState();
+
+ $container.find(".slick-pager-nav span").removeClass("ui-state-disabled");
+ if (!state.canGotoFirst) {
+ $container.find(".ui-icon-seek-first").addClass("ui-state-disabled");
+ }
+ if (!state.canGotoLast) {
+ $container.find(".ui-icon-seek-end").addClass("ui-state-disabled");
+ }
+ if (!state.canGotoNext) {
+ $container.find(".ui-icon-seek-next").addClass("ui-state-disabled");
+ }
+ if (!state.canGotoPrev) {
+ $container.find(".ui-icon-seek-prev").addClass("ui-state-disabled");
+ }
+
+ if (pagingInfo.pageSize == 0) {
+ $status.text("Showing all " + pagingInfo.totalRows + " rows");
+ } else {
+ $status.text("Showing page " + (pagingInfo.pageNum + 1) + " of " + pagingInfo.totalPages);
+ }
+ }
+
+ init();
+ }
+
+ // Slick.Controls.Pager
+ $.extend(true, window, { Slick:{ Controls:{ Pager:SlickGridPager }}});
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.autotooltips.js b/web/pgadmin/static/js/slickgrid/plugins/slick.autotooltips.js
new file mode 100644
index 000000000..6633bb70f
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.autotooltips.js
@@ -0,0 +1,83 @@
+(function ($) {
+ // Register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "AutoTooltips": AutoTooltips
+ }
+ });
+
+ /**
+ * AutoTooltips plugin to show/hide tooltips when columns are too narrow to fit content.
+ * @constructor
+ * @param {boolean} [options.enableForCells=true] - Enable tooltip for grid cells
+ * @param {boolean} [options.enableForHeaderCells=false] - Enable tooltip for header cells
+ * @param {number} [options.maxToolTipLength=null] - The maximum length for a tooltip
+ */
+ function AutoTooltips(options) {
+ var _grid;
+ var _self = this;
+ var _defaults = {
+ enableForCells: true,
+ enableForHeaderCells: false,
+ maxToolTipLength: null
+ };
+
+ /**
+ * Initialize plugin.
+ */
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ if (options.enableForCells) _grid.onMouseEnter.subscribe(handleMouseEnter);
+ if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.subscribe(handleHeaderMouseEnter);
+ }
+
+ /**
+ * Destroy plugin.
+ */
+ function destroy() {
+ if (options.enableForCells) _grid.onMouseEnter.unsubscribe(handleMouseEnter);
+ if (options.enableForHeaderCells) _grid.onHeaderMouseEnter.unsubscribe(handleHeaderMouseEnter);
+ }
+
+ /**
+ * Handle mouse entering grid cell to add/remove tooltip.
+ * @param {jQuery.Event} e - The event
+ */
+ function handleMouseEnter(e) {
+ var cell = _grid.getCellFromEvent(e);
+ if (cell) {
+ var $node = $(_grid.getCellNode(cell.row, cell.cell));
+ var text;
+ if ($node.innerWidth() < $node[0].scrollWidth) {
+ text = $.trim($node.text());
+ if (options.maxToolTipLength && text.length > options.maxToolTipLength) {
+ text = text.substr(0, options.maxToolTipLength - 3) + "...";
+ }
+ } else {
+ text = "";
+ }
+ $node.attr("title", text);
+ }
+ }
+
+ /**
+ * Handle mouse entering header cell to add/remove tooltip.
+ * @param {jQuery.Event} e - The event
+ * @param {object} args.column - The column definition
+ */
+ function handleHeaderMouseEnter(e, args) {
+ var column = args.column,
+ $node = $(e.target).closest(".slick-header-column");
+ if (column && !column.toolTip) {
+ $node.attr("title", ($node.innerWidth() < $node[0].scrollWidth) ? column.name : "");
+ }
+ }
+
+ // Public API
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.cellcopymanager.js b/web/pgadmin/static/js/slickgrid/plugins/slick.cellcopymanager.js
new file mode 100644
index 000000000..c74018de8
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.cellcopymanager.js
@@ -0,0 +1,86 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellCopyManager": CellCopyManager
+ }
+ });
+
+
+ function CellCopyManager() {
+ var _grid;
+ var _self = this;
+ var _copiedRanges;
+
+ function init(grid) {
+ _grid = grid;
+ _grid.onKeyDown.subscribe(handleKeyDown);
+ }
+
+ function destroy() {
+ _grid.onKeyDown.unsubscribe(handleKeyDown);
+ }
+
+ function handleKeyDown(e, args) {
+ var ranges;
+ if (!_grid.getEditorLock().isActive()) {
+ if (e.which == $.ui.keyCode.ESCAPE) {
+ if (_copiedRanges) {
+ e.preventDefault();
+ clearCopySelection();
+ _self.onCopyCancelled.notify({ranges: _copiedRanges});
+ _copiedRanges = null;
+ }
+ }
+
+ if (e.which == 67 && (e.ctrlKey || e.metaKey)) {
+ ranges = _grid.getSelectionModel().getSelectedRanges();
+ if (ranges.length != 0) {
+ e.preventDefault();
+ _copiedRanges = ranges;
+ markCopySelection(ranges);
+ _self.onCopyCells.notify({ranges: ranges});
+ }
+ }
+
+ if (e.which == 86 && (e.ctrlKey || e.metaKey)) {
+ if (_copiedRanges) {
+ e.preventDefault();
+ clearCopySelection();
+ ranges = _grid.getSelectionModel().getSelectedRanges();
+ _self.onPasteCells.notify({from: _copiedRanges, to: ranges});
+ _copiedRanges = null;
+ }
+ }
+ }
+ }
+
+ function markCopySelection(ranges) {
+ var columns = _grid.getColumns();
+ var hash = {};
+ for (var i = 0; i < ranges.length; i++) {
+ for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
+ hash[j] = {};
+ for (var k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {
+ hash[j][columns[k].id] = "copied";
+ }
+ }
+ }
+ _grid.setCellCssStyles("copy-manager", hash);
+ }
+
+ function clearCopySelection() {
+ _grid.removeCellCssStyles("copy-manager");
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+ "clearCopySelection": clearCopySelection,
+
+ "onCopyCells": new Slick.Event(),
+ "onCopyCancelled": new Slick.Event(),
+ "onPasteCells": new Slick.Event()
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangedecorator.js b/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangedecorator.js
new file mode 100644
index 000000000..0cbe71d48
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangedecorator.js
@@ -0,0 +1,66 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellRangeDecorator": CellRangeDecorator
+ }
+ });
+
+ /***
+ * Displays an overlay on top of a given cell range.
+ *
+ * TODO:
+ * Currently, it blocks mouse events to DOM nodes behind it.
+ * Use FF and WebKit-specific "pointer-events" CSS style, or some kind of event forwarding.
+ * Could also construct the borders separately using 4 individual DIVs.
+ *
+ * @param {Grid} grid
+ * @param {Object} options
+ */
+ function CellRangeDecorator(grid, options) {
+ var _elem;
+ var _defaults = {
+ selectionCssClass: 'slick-range-decorator',
+ selectionCss: {
+ "zIndex": "9999",
+ "border": "2px dashed red"
+ }
+ };
+
+ options = $.extend(true, {}, _defaults, options);
+
+
+ function show(range) {
+ if (!_elem) {
+ _elem = $("
", {css: options.selectionCss})
+ .addClass(options.selectionCssClass)
+ .css("position", "absolute")
+ .appendTo(grid.getCanvasNode());
+ }
+
+ var from = grid.getCellNodeBox(range.fromRow, range.fromCell);
+ var to = grid.getCellNodeBox(range.toRow, range.toCell);
+
+ _elem.css({
+ top: from.top - 1,
+ left: from.left - 1,
+ height: to.bottom - from.top - 2,
+ width: to.right - from.left - 2
+ });
+
+ return _elem;
+ }
+
+ function hide() {
+ if (_elem) {
+ _elem.remove();
+ _elem = null;
+ }
+ }
+
+ $.extend(this, {
+ "show": show,
+ "hide": hide
+ });
+ }
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangeselector.js b/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangeselector.js
new file mode 100644
index 000000000..520b17f3c
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.cellrangeselector.js
@@ -0,0 +1,113 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellRangeSelector": CellRangeSelector
+ }
+ });
+
+
+ function CellRangeSelector(options) {
+ var _grid;
+ var _canvas;
+ var _dragging;
+ var _decorator;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ selectionCss: {
+ "border": "2px dashed blue"
+ }
+ };
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _decorator = new Slick.CellRangeDecorator(grid, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _handler
+ .subscribe(_grid.onDragInit, handleDragInit)
+ .subscribe(_grid.onDragStart, handleDragStart)
+ .subscribe(_grid.onDrag, handleDrag)
+ .subscribe(_grid.onDragEnd, handleDragEnd);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleDragInit(e, dd) {
+ // prevent the grid from cancelling drag'n'drop by default
+ e.stopImmediatePropagation();
+ }
+
+ function handleDragStart(e, dd) {
+ var cell = _grid.getCellFromEvent(e);
+ if (_self.onBeforeCellRangeSelected.notify(cell) !== false) {
+ if (_grid.canCellBeSelected(cell.row, cell.cell)) {
+ _dragging = true;
+ e.stopImmediatePropagation();
+ }
+ }
+ if (!_dragging) {
+ return;
+ }
+
+ _grid.focus();
+
+ var start = _grid.getCellFromPoint(
+ dd.startX - $(_canvas).offset().left,
+ dd.startY - $(_canvas).offset().top);
+
+ dd.range = {start: start, end: {}};
+
+ return _decorator.show(new Slick.Range(start.row, start.cell));
+ }
+
+ function handleDrag(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+ e.stopImmediatePropagation();
+
+ var end = _grid.getCellFromPoint(
+ e.pageX - $(_canvas).offset().left,
+ e.pageY - $(_canvas).offset().top);
+
+ if (!_grid.canCellBeSelected(end.row, end.cell)) {
+ return;
+ }
+
+ dd.range.end = end;
+ _decorator.show(new Slick.Range(dd.range.start.row, dd.range.start.cell, end.row, end.cell));
+ }
+
+ function handleDragEnd(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+
+ _dragging = false;
+ e.stopImmediatePropagation();
+
+ _decorator.hide();
+ _self.onCellRangeSelected.notify({
+ range: new Slick.Range(
+ dd.range.start.row,
+ dd.range.start.cell,
+ dd.range.end.row,
+ dd.range.end.cell
+ )
+ });
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onBeforeCellRangeSelected": new Slick.Event(),
+ "onCellRangeSelected": new Slick.Event()
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.cellselectionmodel.js b/web/pgadmin/static/js/slickgrid/plugins/slick.cellselectionmodel.js
new file mode 100644
index 000000000..07453ed19
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.cellselectionmodel.js
@@ -0,0 +1,157 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CellSelectionModel": CellSelectionModel
+ }
+ });
+
+
+ function CellSelectionModel(options) {
+ var _grid;
+ var _canvas;
+ var _ranges = [];
+ var _self = this;
+ var _selector = new Slick.CellRangeSelector({
+ "selectionCss": {
+ "border": "2px solid black"
+ }
+ });
+ var _options;
+ var _defaults = {
+ selectActiveCell: true
+ };
+
+
+ function init(grid) {
+ _options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _grid.onActiveCellChanged.subscribe(handleActiveCellChange);
+ _grid.onKeyDown.subscribe(handleKeyDown);
+ grid.registerPlugin(_selector);
+ _selector.onCellRangeSelected.subscribe(handleCellRangeSelected);
+ _selector.onBeforeCellRangeSelected.subscribe(handleBeforeCellRangeSelected);
+ }
+
+ function destroy() {
+ _grid.onActiveCellChanged.unsubscribe(handleActiveCellChange);
+ _grid.onKeyDown.unsubscribe(handleKeyDown);
+ _selector.onCellRangeSelected.unsubscribe(handleCellRangeSelected);
+ _selector.onBeforeCellRangeSelected.unsubscribe(handleBeforeCellRangeSelected);
+ _grid.unregisterPlugin(_selector);
+ }
+
+ function removeInvalidRanges(ranges) {
+ var result = [];
+
+ for (var i = 0; i < ranges.length; i++) {
+ var r = ranges[i];
+ if (_grid.canCellBeSelected(r.fromRow, r.fromCell) && _grid.canCellBeSelected(r.toRow, r.toCell)) {
+ result.push(r);
+ }
+ }
+
+ return result;
+ }
+
+ function setSelectedRanges(ranges) {
+ // simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged
+ if ((!_ranges || _ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
+
+ _ranges = removeInvalidRanges(ranges);
+ _self.onSelectedRangesChanged.notify(_ranges);
+ }
+
+ function getSelectedRanges() {
+ return _ranges;
+ }
+
+ function handleBeforeCellRangeSelected(e, args) {
+ if (_grid.getEditorLock().isActive()) {
+ e.stopPropagation();
+ return false;
+ }
+ }
+
+ function handleCellRangeSelected(e, args) {
+ setSelectedRanges([args.range]);
+ }
+
+ function handleActiveCellChange(e, args) {
+ if (_options.selectActiveCell && args.row != null && args.cell != null) {
+ setSelectedRanges([new Slick.Range(args.row, args.cell)]);
+ }
+ }
+
+ function handleKeyDown(e) {
+ /***
+ * Кey codes
+ * 37 left
+ * 38 up
+ * 39 right
+ * 40 down
+ */
+ var ranges, last;
+ var active = _grid.getActiveCell();
+
+ if ( active && e.shiftKey && !e.ctrlKey && !e.altKey &&
+ (e.which == 37 || e.which == 39 || e.which == 38 || e.which == 40) ) {
+
+ ranges = getSelectedRanges();
+ if (!ranges.length)
+ ranges.push(new Slick.Range(active.row, active.cell));
+
+ // keyboard can work with last range only
+ last = ranges.pop();
+
+ // can't handle selection out of active cell
+ if (!last.contains(active.row, active.cell))
+ last = new Slick.Range(active.row, active.cell);
+
+ var dRow = last.toRow - last.fromRow,
+ dCell = last.toCell - last.fromCell,
+ // walking direction
+ dirRow = active.row == last.fromRow ? 1 : -1,
+ dirCell = active.cell == last.fromCell ? 1 : -1;
+
+ if (e.which == 37) {
+ dCell -= dirCell;
+ } else if (e.which == 39) {
+ dCell += dirCell ;
+ } else if (e.which == 38) {
+ dRow -= dirRow;
+ } else if (e.which == 40) {
+ dRow += dirRow;
+ }
+
+ // define new selection range
+ var new_last = new Slick.Range(active.row, active.cell, active.row + dirRow*dRow, active.cell + dirCell*dCell);
+ if (removeInvalidRanges([new_last]).length) {
+ ranges.push(new_last);
+ var viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;
+ var viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;
+ _grid.scrollRowIntoView(viewRow);
+ _grid.scrollCellIntoView(viewRow, viewCell);
+ }
+ else
+ ranges.push(last);
+
+ setSelectedRanges(ranges);
+
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+ $.extend(this, {
+ "getSelectedRanges": getSelectedRanges,
+ "setSelectedRanges": setSelectedRanges,
+
+ "init": init,
+ "destroy": destroy,
+
+ "onSelectedRangesChanged": new Slick.Event()
+ });
+ }
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.checkboxselectcolumn.js b/web/pgadmin/static/js/slickgrid/plugins/slick.checkboxselectcolumn.js
new file mode 100644
index 000000000..83d8d5008
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.checkboxselectcolumn.js
@@ -0,0 +1,153 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "CheckboxSelectColumn": CheckboxSelectColumn
+ }
+ });
+
+
+ function CheckboxSelectColumn(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _selectedRowsLookup = {};
+ var _defaults = {
+ columnId: "_checkbox_selector",
+ cssClass: null,
+ toolTip: "Select/Deselect All",
+ width: 30
+ };
+
+ var _options = $.extend(true, {}, _defaults, options);
+
+ function init(grid) {
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onSelectedRowsChanged, handleSelectedRowsChanged)
+ .subscribe(_grid.onClick, handleClick)
+ .subscribe(_grid.onHeaderClick, handleHeaderClick)
+ .subscribe(_grid.onKeyDown, handleKeyDown);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleSelectedRowsChanged(e, args) {
+ var selectedRows = _grid.getSelectedRows();
+ var lookup = {}, row, i;
+ for (i = 0; i < selectedRows.length; i++) {
+ row = selectedRows[i];
+ lookup[row] = true;
+ if (lookup[row] !== _selectedRowsLookup[row]) {
+ _grid.invalidateRow(row);
+ delete _selectedRowsLookup[row];
+ }
+ }
+ for (i in _selectedRowsLookup) {
+ _grid.invalidateRow(i);
+ }
+ _selectedRowsLookup = lookup;
+ _grid.render();
+
+ if (selectedRows.length && selectedRows.length == _grid.getDataLength()) {
+ _grid.updateColumnHeader(_options.columnId, "
", _options.toolTip);
+ } else {
+ _grid.updateColumnHeader(_options.columnId, "
", _options.toolTip);
+ }
+ }
+
+ function handleKeyDown(e, args) {
+ if (e.which == 32) {
+ if (_grid.getColumns()[args.cell].id === _options.columnId) {
+ // if editing, try to commit
+ if (!_grid.getEditorLock().isActive() || _grid.getEditorLock().commitCurrentEdit()) {
+ toggleRowSelection(args.row);
+ }
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+ }
+
+ function handleClick(e, args) {
+ // clicking on a row select checkbox
+ if (_grid.getColumns()[args.cell].id === _options.columnId && $(e.target).is(":checkbox")) {
+ // if editing, try to commit
+ if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return;
+ }
+
+ toggleRowSelection(args.row);
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ function toggleRowSelection(row) {
+ if (_selectedRowsLookup[row]) {
+ _grid.setSelectedRows($.grep(_grid.getSelectedRows(), function (n) {
+ return n != row
+ }));
+ } else {
+ _grid.setSelectedRows(_grid.getSelectedRows().concat(row));
+ }
+ }
+
+ function handleHeaderClick(e, args) {
+ if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
+ // if editing, try to commit
+ if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ return;
+ }
+
+ if ($(e.target).is(":checked")) {
+ var rows = [];
+ for (var i = 0; i < _grid.getDataLength(); i++) {
+ rows.push(i);
+ }
+ _grid.setSelectedRows(rows);
+ } else {
+ _grid.setSelectedRows([]);
+ }
+ e.stopPropagation();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ function getColumnDefinition() {
+ return {
+ id: _options.columnId,
+ name: "
",
+ toolTip: _options.toolTip,
+ field: "sel",
+ width: _options.width,
+ resizable: false,
+ sortable: false,
+ cssClass: _options.cssClass,
+ formatter: checkboxSelectionFormatter
+ };
+ }
+
+ function checkboxSelectionFormatter(row, cell, value, columnDef, dataContext) {
+ if (dataContext) {
+ return _selectedRowsLookup[row]
+ ? "
"
+ : "
";
+ }
+ return null;
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "getColumnDefinition": getColumnDefinition
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.css b/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.css
new file mode 100644
index 000000000..0ba79ea0d
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.css
@@ -0,0 +1,39 @@
+.slick-column-name,
+.slick-sort-indicator {
+ /**
+ * This makes all "float:right" elements after it that spill over to the next line
+ * display way below the lower boundary of the column thus hiding them.
+ */
+ display: inline-block;
+ float: left;
+ margin-bottom: 100px;
+}
+
+.slick-header-button {
+ display: inline-block;
+ float: right;
+ vertical-align: top;
+ margin: 1px;
+ /**
+ * This makes all "float:right" elements after it that spill over to the next line
+ * display way below the lower boundary of the column thus hiding them.
+ */
+ margin-bottom: 100px;
+ height: 15px;
+ width: 15px;
+ background-repeat: no-repeat;
+ background-position: center center;
+ cursor: pointer;
+}
+
+.slick-header-button-hidden {
+ width: 0;
+
+ -webkit-transition: 0.2s width;
+ -ms-transition: 0.2s width;
+ transition: 0.2s width;
+}
+
+.slick-header-column:hover > .slick-header-button {
+ width: 15px;
+}
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.js b/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.js
new file mode 100644
index 000000000..8e612735e
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.headerbuttons.js
@@ -0,0 +1,177 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Plugins": {
+ "HeaderButtons": HeaderButtons
+ }
+ }
+ });
+
+
+ /***
+ * A plugin to add custom buttons to column headers.
+ *
+ * USAGE:
+ *
+ * Add the plugin .js & .css files and register it with the grid.
+ *
+ * To specify a custom button in a column header, extend the column definition like so:
+ *
+ * var columns = [
+ * {
+ * id: 'myColumn',
+ * name: 'My column',
+ *
+ * // This is the relevant part
+ * header: {
+ * buttons: [
+ * {
+ * // button options
+ * },
+ * {
+ * // button options
+ * }
+ * ]
+ * }
+ * }
+ * ];
+ *
+ * Available button options:
+ * cssClass: CSS class to add to the button.
+ * image: Relative button image path.
+ * tooltip: Button tooltip.
+ * showOnHover: Only show the button on hover.
+ * handler: Button click handler.
+ * command: A command identifier to be passed to the onCommand event handlers.
+ *
+ * The plugin exposes the following events:
+ * onCommand: Fired on button click for buttons with 'command' specified.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * command: Button command identified.
+ * button: Button options. Note that you can change the button options in your
+ * event handler, and the column header will be automatically updated to
+ * reflect them. This is useful if you want to implement something like a
+ * toggle button.
+ *
+ *
+ * @param options {Object} Options:
+ * buttonCssClass: a CSS class to use for buttons (default 'slick-header-button')
+ * @class Slick.Plugins.HeaderButtons
+ * @constructor
+ */
+ function HeaderButtons(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ buttonCssClass: "slick-header-button"
+ };
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
+ .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
+
+ // Force the grid to re-render the header now that the events are hooked up.
+ _grid.setColumns(_grid.getColumns());
+ }
+
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+
+ function handleHeaderCellRendered(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.buttons) {
+ // Append buttons in reverse order since they are floated to the right.
+ var i = column.header.buttons.length;
+ while (i--) {
+ var button = column.header.buttons[i];
+ var btn = $("
")
+ .addClass(options.buttonCssClass)
+ .data("column", column)
+ .data("button", button);
+
+ if (button.showOnHover) {
+ btn.addClass("slick-header-button-hidden");
+ }
+
+ if (button.image) {
+ btn.css("backgroundImage", "url(" + button.image + ")");
+ }
+
+ if (button.cssClass) {
+ btn.addClass(button.cssClass);
+ }
+
+ if (button.tooltip) {
+ btn.attr("title", button.tooltip);
+ }
+
+ if (button.command) {
+ btn.data("command", button.command);
+ }
+
+ if (button.handler) {
+ btn.bind("click", button.handler);
+ }
+
+ btn
+ .bind("click", handleButtonClick)
+ .appendTo(args.node);
+ }
+ }
+ }
+
+
+ function handleBeforeHeaderCellDestroy(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.buttons) {
+ // Removing buttons via jQuery will also clean up any event handlers and data.
+ // NOTE: If you attach event handlers directly or using a different framework,
+ // you must also clean them up here to avoid memory leaks.
+ $(args.node).find("." + options.buttonCssClass).remove();
+ }
+ }
+
+
+ function handleButtonClick(e) {
+ var command = $(this).data("command");
+ var columnDef = $(this).data("column");
+ var button = $(this).data("button");
+
+ if (command != null) {
+ _self.onCommand.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "command": command,
+ "button": button
+ }, e, _self);
+
+ // Update the header in case the user updated the button definition in the handler.
+ _grid.updateColumnHeader(columnDef.id);
+ }
+
+ // Stop propagation so that it doesn't register as a header click event.
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onCommand": new Slick.Event()
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.css b/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.css
new file mode 100644
index 000000000..8b0b6a9f7
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.css
@@ -0,0 +1,59 @@
+/* Menu button */
+.slick-header-menubutton {
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ width: 14px;
+ background-repeat: no-repeat;
+ background-position: left center;
+ background-image: url(../images/down.gif);
+ cursor: pointer;
+
+ display: none;
+ border-left: thin ridge silver;
+}
+
+.slick-header-column:hover > .slick-header-menubutton,
+.slick-header-column-active .slick-header-menubutton {
+ display: inline-block;
+}
+
+/* Menu */
+.slick-header-menu {
+ position: absolute;
+ display: inline-block;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+
+/* Menu items */
+.slick-header-menuitem {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ cursor: pointer;
+}
+
+.slick-header-menuicon {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+ margin-right: 4px;
+ background-repeat: no-repeat;
+ background-position: center center;
+}
+
+.slick-header-menucontent {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+
+/* Disabled */
+.slick-header-menuitem-disabled {
+ color: silver;
+}
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.js b/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.js
new file mode 100644
index 000000000..ec8244daa
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.headermenu.js
@@ -0,0 +1,275 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Plugins": {
+ "HeaderMenu": HeaderMenu
+ }
+ }
+ });
+
+
+ /***
+ * A plugin to add drop-down menus to column headers.
+ *
+ * USAGE:
+ *
+ * Add the plugin .js & .css files and register it with the grid.
+ *
+ * To specify a menu in a column header, extend the column definition like so:
+ *
+ * var columns = [
+ * {
+ * id: 'myColumn',
+ * name: 'My column',
+ *
+ * // This is the relevant part
+ * header: {
+ * menu: {
+ * items: [
+ * {
+ * // menu item options
+ * },
+ * {
+ * // menu item options
+ * }
+ * ]
+ * }
+ * }
+ * }
+ * ];
+ *
+ *
+ * Available menu options:
+ * tooltip: Menu button tooltip.
+ *
+ *
+ * Available menu item options:
+ * title: Menu item text.
+ * disabled: Whether the item is disabled.
+ * tooltip: Item tooltip.
+ * command: A command identifier to be passed to the onCommand event handlers.
+ * iconCssClass: A CSS class to be added to the menu item icon.
+ * iconImage: A url to the icon image.
+ *
+ *
+ * The plugin exposes the following events:
+ * onBeforeMenuShow: Fired before the menu is shown. You can customize the menu or dismiss it by returning false.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * menu: Menu options. Note that you can change the menu items here.
+ *
+ * onCommand: Fired on menu item click for buttons with 'command' specified.
+ * Event args:
+ * grid: Reference to the grid.
+ * column: Column definition.
+ * command: Button command identified.
+ * button: Button options. Note that you can change the button options in your
+ * event handler, and the column header will be automatically updated to
+ * reflect them. This is useful if you want to implement something like a
+ * toggle button.
+ *
+ *
+ * @param options {Object} Options:
+ * buttonCssClass: an extra CSS class to add to the menu button
+ * buttonImage: a url to the menu button image (default '../images/down.gif')
+ * @class Slick.Plugins.HeaderButtons
+ * @constructor
+ */
+ function HeaderMenu(options) {
+ var _grid;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ buttonCssClass: null,
+ buttonImage: null
+ };
+ var $menu;
+ var $activeHeaderColumn;
+
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler
+ .subscribe(_grid.onHeaderCellRendered, handleHeaderCellRendered)
+ .subscribe(_grid.onBeforeHeaderCellDestroy, handleBeforeHeaderCellDestroy);
+
+ // Force the grid to re-render the header now that the events are hooked up.
+ _grid.setColumns(_grid.getColumns());
+
+ // Hide the menu on outside click.
+ $(document.body).bind("mousedown", handleBodyMouseDown);
+ }
+
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ $(document.body).unbind("mousedown", handleBodyMouseDown);
+ }
+
+
+ function handleBodyMouseDown(e) {
+ if ($menu && $menu[0] != e.target && !$.contains($menu[0], e.target)) {
+ hideMenu();
+ }
+ }
+
+
+ function hideMenu() {
+ if ($menu) {
+ $menu.remove();
+ $menu = null;
+ $activeHeaderColumn
+ .removeClass("slick-header-column-active");
+ }
+ }
+
+ function handleHeaderCellRendered(e, args) {
+ var column = args.column;
+ var menu = column.header && column.header.menu;
+
+ if (menu) {
+ var $el = $("
")
+ .addClass("slick-header-menubutton")
+ .data("column", column)
+ .data("menu", menu);
+
+ if (options.buttonCssClass) {
+ $el.addClass(options.buttonCssClass);
+ }
+
+ if (options.buttonImage) {
+ $el.css("background-image", "url(" + options.buttonImage + ")");
+ }
+
+ if (menu.tooltip) {
+ $el.attr("title", menu.tooltip);
+ }
+
+ $el
+ .bind("click", showMenu)
+ .appendTo(args.node);
+ }
+ }
+
+
+ function handleBeforeHeaderCellDestroy(e, args) {
+ var column = args.column;
+
+ if (column.header && column.header.menu) {
+ $(args.node).find(".slick-header-menubutton").remove();
+ }
+ }
+
+
+ function showMenu(e) {
+ var $menuButton = $(this);
+ var menu = $menuButton.data("menu");
+ var columnDef = $menuButton.data("column");
+
+ // Let the user modify the menu or cancel altogether,
+ // or provide alternative menu implementation.
+ if (_self.onBeforeMenuShow.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "menu": menu
+ }, e, _self) == false) {
+ return;
+ }
+
+
+ if (!$menu) {
+ $menu = $("")
+ .appendTo(_grid.getContainerNode());
+ }
+ $menu.empty();
+
+
+ // Construct the menu items.
+ for (var i = 0; i < menu.items.length; i++) {
+ var item = menu.items[i];
+
+ var $li = $("")
+ .data("command", item.command || '')
+ .data("column", columnDef)
+ .data("item", item)
+ .bind("click", handleMenuItemClick)
+ .appendTo($menu);
+
+ if (item.disabled) {
+ $li.addClass("slick-header-menuitem-disabled");
+ }
+
+ if (item.tooltip) {
+ $li.attr("title", item.tooltip);
+ }
+
+ var $icon = $("")
+ .appendTo($li);
+
+ if (item.iconCssClass) {
+ $icon.addClass(item.iconCssClass);
+ }
+
+ if (item.iconImage) {
+ $icon.css("background-image", "url(" + item.iconImage + ")");
+ }
+
+ $("")
+ .text(item.title)
+ .appendTo($li);
+ }
+
+
+ // Position the menu.
+ $menu
+ .offset({ top: $(this).offset().top + $(this).height(), left: $(this).offset().left });
+
+
+ // Mark the header as active to keep the highlighting.
+ $activeHeaderColumn = $menuButton.closest(".slick-header-column");
+ $activeHeaderColumn
+ .addClass("slick-header-column-active");
+
+ // Stop propagation so that it doesn't register as a header click event.
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+
+ function handleMenuItemClick(e) {
+ var command = $(this).data("command");
+ var columnDef = $(this).data("column");
+ var item = $(this).data("item");
+
+ if (item.disabled) {
+ return;
+ }
+
+ hideMenu();
+
+ if (command != null && command != '') {
+ _self.onCommand.notify({
+ "grid": _grid,
+ "column": columnDef,
+ "command": command,
+ "item": item
+ }, e, _self);
+ }
+
+ // Stop propagation so that it doesn't register as a header click event.
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ $.extend(this, {
+ "init": init,
+ "destroy": destroy,
+
+ "onBeforeMenuShow": new Slick.Event(),
+ "onCommand": new Slick.Event()
+ });
+ }
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.rowmovemanager.js b/web/pgadmin/static/js/slickgrid/plugins/slick.rowmovemanager.js
new file mode 100644
index 000000000..5f87a1edd
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.rowmovemanager.js
@@ -0,0 +1,138 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "RowMoveManager": RowMoveManager
+ }
+ });
+
+ function RowMoveManager(options) {
+ var _grid;
+ var _canvas;
+ var _dragging;
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _defaults = {
+ cancelEditOnDrag: false
+ };
+
+ function init(grid) {
+ options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _canvas = _grid.getCanvasNode();
+ _handler
+ .subscribe(_grid.onDragInit, handleDragInit)
+ .subscribe(_grid.onDragStart, handleDragStart)
+ .subscribe(_grid.onDrag, handleDrag)
+ .subscribe(_grid.onDragEnd, handleDragEnd);
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function handleDragInit(e, dd) {
+ // prevent the grid from cancelling drag'n'drop by default
+ e.stopImmediatePropagation();
+ }
+
+ function handleDragStart(e, dd) {
+ var cell = _grid.getCellFromEvent(e);
+
+ if (options.cancelEditOnDrag && _grid.getEditorLock().isActive()) {
+ _grid.getEditorLock().cancelCurrentEdit();
+ }
+
+ if (_grid.getEditorLock().isActive() || !/move|selectAndMove/.test(_grid.getColumns()[cell.cell].behavior)) {
+ return false;
+ }
+
+ _dragging = true;
+ e.stopImmediatePropagation();
+
+ var selectedRows = _grid.getSelectedRows();
+
+ if (selectedRows.length == 0 || $.inArray(cell.row, selectedRows) == -1) {
+ selectedRows = [cell.row];
+ _grid.setSelectedRows(selectedRows);
+ }
+
+ var rowHeight = _grid.getOptions().rowHeight;
+
+ dd.selectedRows = selectedRows;
+
+ dd.selectionProxy = $("
")
+ .css("position", "absolute")
+ .css("zIndex", "99999")
+ .css("width", $(_canvas).innerWidth())
+ .css("height", rowHeight * selectedRows.length)
+ .appendTo(_canvas);
+
+ dd.guide = $("
")
+ .css("position", "absolute")
+ .css("zIndex", "99998")
+ .css("width", $(_canvas).innerWidth())
+ .css("top", -1000)
+ .appendTo(_canvas);
+
+ dd.insertBefore = -1;
+ }
+
+ function handleDrag(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+
+ e.stopImmediatePropagation();
+
+ var top = e.pageY - $(_canvas).offset().top;
+ dd.selectionProxy.css("top", top - 5);
+
+ var insertBefore = Math.max(0, Math.min(Math.round(top / _grid.getOptions().rowHeight), _grid.getDataLength()));
+ if (insertBefore !== dd.insertBefore) {
+ var eventData = {
+ "rows": dd.selectedRows,
+ "insertBefore": insertBefore
+ };
+
+ if (_self.onBeforeMoveRows.notify(eventData) === false) {
+ dd.guide.css("top", -1000);
+ dd.canMove = false;
+ } else {
+ dd.guide.css("top", insertBefore * _grid.getOptions().rowHeight);
+ dd.canMove = true;
+ }
+
+ dd.insertBefore = insertBefore;
+ }
+ }
+
+ function handleDragEnd(e, dd) {
+ if (!_dragging) {
+ return;
+ }
+ _dragging = false;
+ e.stopImmediatePropagation();
+
+ dd.guide.remove();
+ dd.selectionProxy.remove();
+
+ if (dd.canMove) {
+ var eventData = {
+ "rows": dd.selectedRows,
+ "insertBefore": dd.insertBefore
+ };
+ // TODO: _grid.remapCellCssClasses ?
+ _self.onMoveRows.notify(eventData);
+ }
+ }
+
+ $.extend(this, {
+ "onBeforeMoveRows": new Slick.Event(),
+ "onMoveRows": new Slick.Event(),
+
+ "init": init,
+ "destroy": destroy
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/plugins/slick.rowselectionmodel.js b/web/pgadmin/static/js/slickgrid/plugins/slick.rowselectionmodel.js
new file mode 100644
index 000000000..190b0f71d
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/plugins/slick.rowselectionmodel.js
@@ -0,0 +1,189 @@
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "RowSelectionModel": RowSelectionModel
+ }
+ });
+
+ function RowSelectionModel(options) {
+ var _grid;
+ var _ranges = [];
+ var _self = this;
+ var _handler = new Slick.EventHandler();
+ var _inHandler;
+ var _options;
+ var _defaults = {
+ selectActiveRow: true
+ };
+
+ function init(grid) {
+ _options = $.extend(true, {}, _defaults, options);
+ _grid = grid;
+ _handler.subscribe(_grid.onActiveCellChanged,
+ wrapHandler(handleActiveCellChange));
+ _handler.subscribe(_grid.onKeyDown,
+ wrapHandler(handleKeyDown));
+ _handler.subscribe(_grid.onClick,
+ wrapHandler(handleClick));
+ }
+
+ function destroy() {
+ _handler.unsubscribeAll();
+ }
+
+ function wrapHandler(handler) {
+ return function () {
+ if (!_inHandler) {
+ _inHandler = true;
+ handler.apply(this, arguments);
+ _inHandler = false;
+ }
+ };
+ }
+
+ function rangesToRows(ranges) {
+ var rows = [];
+ for (var i = 0; i < ranges.length; i++) {
+ for (var j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {
+ rows.push(j);
+ }
+ }
+ return rows;
+ }
+
+ function rowsToRanges(rows) {
+ var ranges = [];
+ var lastCell = _grid.getColumns().length - 1;
+ for (var i = 0; i < rows.length; i++) {
+ ranges.push(new Slick.Range(rows[i], 0, rows[i], lastCell));
+ }
+ return ranges;
+ }
+
+ function getRowsRange(from, to) {
+ var i, rows = [];
+ for (i = from; i <= to; i++) {
+ rows.push(i);
+ }
+ for (i = to; i < from; i++) {
+ rows.push(i);
+ }
+ return rows;
+ }
+
+ function getSelectedRows() {
+ return rangesToRows(_ranges);
+ }
+
+ function setSelectedRows(rows) {
+ setSelectedRanges(rowsToRanges(rows));
+ }
+
+ function setSelectedRanges(ranges) {
+ // simle check for: empty selection didn't change, prevent firing onSelectedRangesChanged
+ if ((!_ranges || _ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }
+ _ranges = ranges;
+ _self.onSelectedRangesChanged.notify(_ranges);
+ }
+
+ function getSelectedRanges() {
+ return _ranges;
+ }
+
+ function handleActiveCellChange(e, data) {
+ if (_options.selectActiveRow && data.row != null) {
+ setSelectedRanges([new Slick.Range(data.row, 0, data.row, _grid.getColumns().length - 1)]);
+ }
+ }
+
+ function handleKeyDown(e) {
+ var activeRow = _grid.getActiveCell();
+ if (activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which == 38 || e.which == 40)) {
+ var selectedRows = getSelectedRows();
+ selectedRows.sort(function (x, y) {
+ return x - y
+ });
+
+ if (!selectedRows.length) {
+ selectedRows = [activeRow.row];
+ }
+
+ var top = selectedRows[0];
+ var bottom = selectedRows[selectedRows.length - 1];
+ var active;
+
+ if (e.which == 40) {
+ active = activeRow.row < bottom || top == bottom ? ++bottom : ++top;
+ } else {
+ active = activeRow.row < bottom ? --bottom : --top;
+ }
+
+ if (active >= 0 && active < _grid.getDataLength()) {
+ _grid.scrollRowIntoView(active);
+ _ranges = rowsToRanges(getRowsRange(top, bottom));
+ setSelectedRanges(_ranges);
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+
+ function handleClick(e) {
+ var cell = _grid.getCellFromEvent(e);
+ if (!cell || !_grid.canCellBeActive(cell.row, cell.cell)) {
+ return false;
+ }
+
+ if (!_grid.getOptions().multiSelect || (
+ !e.ctrlKey && !e.shiftKey && !e.metaKey)) {
+ return false;
+ }
+
+ var selection = rangesToRows(_ranges);
+ var idx = $.inArray(cell.row, selection);
+
+ if (idx === -1 && (e.ctrlKey || e.metaKey)) {
+ selection.push(cell.row);
+ _grid.setActiveCell(cell.row, cell.cell);
+ } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {
+ selection = $.grep(selection, function (o, i) {
+ return (o !== cell.row);
+ });
+ _grid.setActiveCell(cell.row, cell.cell);
+ } else if (selection.length && e.shiftKey) {
+ var last = selection.pop();
+ var from = Math.min(cell.row, last);
+ var to = Math.max(cell.row, last);
+ selection = [];
+ for (var i = from; i <= to; i++) {
+ if (i !== last) {
+ selection.push(i);
+ }
+ }
+ selection.push(last);
+ _grid.setActiveCell(cell.row, cell.cell);
+ }
+
+ _ranges = rowsToRanges(selection);
+ setSelectedRanges(_ranges);
+ e.stopImmediatePropagation();
+
+ return true;
+ }
+
+ $.extend(this, {
+ "getSelectedRows": getSelectedRows,
+ "setSelectedRows": setSelectedRows,
+
+ "getSelectedRanges": getSelectedRanges,
+ "setSelectedRanges": setSelectedRanges,
+
+ "init": init,
+ "destroy": destroy,
+
+ "onSelectedRangesChanged": new Slick.Event()
+ });
+ }
+})(jQuery);
\ No newline at end of file
diff --git a/web/pgadmin/static/js/slickgrid/slick.core.js b/web/pgadmin/static/js/slickgrid/slick.core.js
new file mode 100644
index 000000000..c4f04ec45
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/slick.core.js
@@ -0,0 +1,484 @@
+/***
+ * Contains core SlickGrid classes.
+ * @module Core
+ * @namespace Slick
+ */
+
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Event": Event,
+ "EventData": EventData,
+ "EventHandler": EventHandler,
+ "Range": Range,
+ "NonDataRow": NonDataItem,
+ "Group": Group,
+ "GroupTotals": GroupTotals,
+ "EditorLock": EditorLock,
+
+ /***
+ * A global singleton editor lock.
+ * @class GlobalEditorLock
+ * @static
+ * @constructor
+ */
+ "GlobalEditorLock": new EditorLock(),
+
+ "keyCode": {
+ BACKSPACE: 8,
+ DELETE: 46,
+ DOWN: 40,
+ END: 35,
+ ENTER: 13,
+ ESCAPE: 27,
+ HOME: 36,
+ INSERT: 45,
+ LEFT: 37,
+ PAGE_DOWN: 34,
+ PAGE_UP: 33,
+ RIGHT: 39,
+ TAB: 9,
+ UP: 38
+ }
+ }
+ });
+
+ /***
+ * An event object for passing data to event handlers and letting them control propagation.
+ *
This is pretty much identical to how W3C and jQuery implement events.
+ * @class EventData
+ * @constructor
+ */
+ function EventData() {
+ var isPropagationStopped = false;
+ var isImmediatePropagationStopped = false;
+
+ /***
+ * Stops event from propagating up the DOM tree.
+ * @method stopPropagation
+ */
+ this.stopPropagation = function () {
+ isPropagationStopped = true;
+ };
+
+ /***
+ * Returns whether stopPropagation was called on this event object.
+ * @method isPropagationStopped
+ * @return {Boolean}
+ */
+ this.isPropagationStopped = function () {
+ return isPropagationStopped;
+ };
+
+ /***
+ * Prevents the rest of the handlers from being executed.
+ * @method stopImmediatePropagation
+ */
+ this.stopImmediatePropagation = function () {
+ isImmediatePropagationStopped = true;
+ };
+
+ /***
+ * Returns whether stopImmediatePropagation was called on this event object.\
+ * @method isImmediatePropagationStopped
+ * @return {Boolean}
+ */
+ this.isImmediatePropagationStopped = function () {
+ return isImmediatePropagationStopped;
+ }
+ }
+
+ /***
+ * A simple publisher-subscriber implementation.
+ * @class Event
+ * @constructor
+ */
+ function Event() {
+ var handlers = [];
+
+ /***
+ * Adds an event handler to be called when the event is fired.
+ *
Event handler will receive two arguments - an EventData
and the data
+ * object the event was fired with.
+ * @method subscribe
+ * @param fn {Function} Event handler.
+ */
+ this.subscribe = function (fn) {
+ handlers.push(fn);
+ };
+
+ /***
+ * Removes an event handler added with subscribe(fn)
.
+ * @method unsubscribe
+ * @param fn {Function} Event handler to be removed.
+ */
+ this.unsubscribe = function (fn) {
+ for (var i = handlers.length - 1; i >= 0; i--) {
+ if (handlers[i] === fn) {
+ handlers.splice(i, 1);
+ }
+ }
+ };
+
+ /***
+ * Fires an event notifying all subscribers.
+ * @method notify
+ * @param args {Object} Additional data object to be passed to all handlers.
+ * @param e {EventData}
+ * Optional.
+ * An EventData
object to be passed to all handlers.
+ * For DOM events, an existing W3C/jQuery event object can be passed in.
+ * @param scope {Object}
+ * Optional.
+ * The scope ("this") within which the handler will be executed.
+ * If not specified, the scope will be set to the Event
instance.
+ */
+ this.notify = function (args, e, scope) {
+ e = e || new EventData();
+ scope = scope || this;
+
+ var returnValue;
+ for (var i = 0; i < handlers.length && !(e.isPropagationStopped() || e.isImmediatePropagationStopped()); i++) {
+ returnValue = handlers[i].call(scope, e, args);
+ }
+
+ return returnValue;
+ };
+ }
+
+ function EventHandler() {
+ var handlers = [];
+
+ this.subscribe = function (event, handler) {
+ handlers.push({
+ event: event,
+ handler: handler
+ });
+ event.subscribe(handler);
+
+ return this; // allow chaining
+ };
+
+ this.unsubscribe = function (event, handler) {
+ var i = handlers.length;
+ while (i--) {
+ if (handlers[i].event === event &&
+ handlers[i].handler === handler) {
+ handlers.splice(i, 1);
+ event.unsubscribe(handler);
+ return;
+ }
+ }
+
+ return this; // allow chaining
+ };
+
+ this.unsubscribeAll = function () {
+ var i = handlers.length;
+ while (i--) {
+ handlers[i].event.unsubscribe(handlers[i].handler);
+ }
+ handlers = [];
+
+ return this; // allow chaining
+ }
+ }
+
+ /***
+ * A structure containing a range of cells.
+ * @class Range
+ * @constructor
+ * @param fromRow {Integer} Starting row.
+ * @param fromCell {Integer} Starting cell.
+ * @param toRow {Integer} Optional. Ending row. Defaults to fromRow
.
+ * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell
.
+ */
+ function Range(fromRow, fromCell, toRow, toCell) {
+ if (toRow === undefined && toCell === undefined) {
+ toRow = fromRow;
+ toCell = fromCell;
+ }
+
+ /***
+ * @property fromRow
+ * @type {Integer}
+ */
+ this.fromRow = Math.min(fromRow, toRow);
+
+ /***
+ * @property fromCell
+ * @type {Integer}
+ */
+ this.fromCell = Math.min(fromCell, toCell);
+
+ /***
+ * @property toRow
+ * @type {Integer}
+ */
+ this.toRow = Math.max(fromRow, toRow);
+
+ /***
+ * @property toCell
+ * @type {Integer}
+ */
+ this.toCell = Math.max(fromCell, toCell);
+
+ /***
+ * Returns whether a range represents a single row.
+ * @method isSingleRow
+ * @return {Boolean}
+ */
+ this.isSingleRow = function () {
+ return this.fromRow == this.toRow;
+ };
+
+ /***
+ * Returns whether a range represents a single cell.
+ * @method isSingleCell
+ * @return {Boolean}
+ */
+ this.isSingleCell = function () {
+ return this.fromRow == this.toRow && this.fromCell == this.toCell;
+ };
+
+ /***
+ * Returns whether a range contains a given cell.
+ * @method contains
+ * @param row {Integer}
+ * @param cell {Integer}
+ * @return {Boolean}
+ */
+ this.contains = function (row, cell) {
+ return row >= this.fromRow && row <= this.toRow &&
+ cell >= this.fromCell && cell <= this.toCell;
+ };
+
+ /***
+ * Returns a readable representation of a range.
+ * @method toString
+ * @return {String}
+ */
+ this.toString = function () {
+ if (this.isSingleCell()) {
+ return "(" + this.fromRow + ":" + this.fromCell + ")";
+ }
+ else {
+ return "(" + this.fromRow + ":" + this.fromCell + " - " + this.toRow + ":" + this.toCell + ")";
+ }
+ }
+ }
+
+
+ /***
+ * A base class that all special / non-data rows (like Group and GroupTotals) derive from.
+ * @class NonDataItem
+ * @constructor
+ */
+ function NonDataItem() {
+ this.__nonDataRow = true;
+ }
+
+
+ /***
+ * Information about a group of rows.
+ * @class Group
+ * @extends Slick.NonDataItem
+ * @constructor
+ */
+ function Group() {
+ this.__group = true;
+
+ /**
+ * Grouping level, starting with 0.
+ * @property level
+ * @type {Number}
+ */
+ this.level = 0;
+
+ /***
+ * Number of rows in the group.
+ * @property count
+ * @type {Integer}
+ */
+ this.count = 0;
+
+ /***
+ * Grouping value.
+ * @property value
+ * @type {Object}
+ */
+ this.value = null;
+
+ /***
+ * Formatted display value of the group.
+ * @property title
+ * @type {String}
+ */
+ this.title = null;
+
+ /***
+ * Whether a group is collapsed.
+ * @property collapsed
+ * @type {Boolean}
+ */
+ this.collapsed = false;
+
+ /***
+ * GroupTotals, if any.
+ * @property totals
+ * @type {GroupTotals}
+ */
+ this.totals = null;
+
+ /**
+ * Rows that are part of the group.
+ * @property rows
+ * @type {Array}
+ */
+ this.rows = [];
+
+ /**
+ * Sub-groups that are part of the group.
+ * @property groups
+ * @type {Array}
+ */
+ this.groups = null;
+
+ /**
+ * A unique key used to identify the group. This key can be used in calls to DataView
+ * collapseGroup() or expandGroup().
+ * @property groupingKey
+ * @type {Object}
+ */
+ this.groupingKey = null;
+ }
+
+ Group.prototype = new NonDataItem();
+
+ /***
+ * Compares two Group instances.
+ * @method equals
+ * @return {Boolean}
+ * @param group {Group} Group instance to compare to.
+ */
+ Group.prototype.equals = function (group) {
+ return this.value === group.value &&
+ this.count === group.count &&
+ this.collapsed === group.collapsed &&
+ this.title === group.title;
+ };
+
+ /***
+ * Information about group totals.
+ * An instance of GroupTotals will be created for each totals row and passed to the aggregators
+ * so that they can store arbitrary data in it. That data can later be accessed by group totals
+ * formatters during the display.
+ * @class GroupTotals
+ * @extends Slick.NonDataItem
+ * @constructor
+ */
+ function GroupTotals() {
+ this.__groupTotals = true;
+
+ /***
+ * Parent Group.
+ * @param group
+ * @type {Group}
+ */
+ this.group = null;
+
+ /***
+ * Whether the totals have been fully initialized / calculated.
+ * Will be set to false for lazy-calculated group totals.
+ * @param initialized
+ * @type {Boolean}
+ */
+ this.initialized = false;
+ }
+
+ GroupTotals.prototype = new NonDataItem();
+
+ /***
+ * A locking helper to track the active edit controller and ensure that only a single controller
+ * can be active at a time. This prevents a whole class of state and validation synchronization
+ * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress
+ * and attempt a commit or cancel before proceeding.
+ * @class EditorLock
+ * @constructor
+ */
+ function EditorLock() {
+ var activeEditController = null;
+
+ /***
+ * Returns true if a specified edit controller is active (has the edit lock).
+ * If the parameter is not specified, returns true if any edit controller is active.
+ * @method isActive
+ * @param editController {EditController}
+ * @return {Boolean}
+ */
+ this.isActive = function (editController) {
+ return (editController ? activeEditController === editController : activeEditController !== null);
+ };
+
+ /***
+ * Sets the specified edit controller as the active edit controller (acquire edit lock).
+ * If another edit controller is already active, and exception will be thrown.
+ * @method activate
+ * @param editController {EditController} edit controller acquiring the lock
+ */
+ this.activate = function (editController) {
+ if (editController === activeEditController) { // already activated?
+ return;
+ }
+ if (activeEditController !== null) {
+ throw "SlickGrid.EditorLock.activate: an editController is still active, can't activate another editController";
+ }
+ if (!editController.commitCurrentEdit) {
+ throw "SlickGrid.EditorLock.activate: editController must implement .commitCurrentEdit()";
+ }
+ if (!editController.cancelCurrentEdit) {
+ throw "SlickGrid.EditorLock.activate: editController must implement .cancelCurrentEdit()";
+ }
+ activeEditController = editController;
+ };
+
+ /***
+ * Unsets the specified edit controller as the active edit controller (release edit lock).
+ * If the specified edit controller is not the active one, an exception will be thrown.
+ * @method deactivate
+ * @param editController {EditController} edit controller releasing the lock
+ */
+ this.deactivate = function (editController) {
+ if (activeEditController !== editController) {
+ throw "SlickGrid.EditorLock.deactivate: specified editController is not the currently active one";
+ }
+ activeEditController = null;
+ };
+
+ /***
+ * Attempts to commit the current edit by calling "commitCurrentEdit" method on the active edit
+ * controller and returns whether the commit attempt was successful (commit may fail due to validation
+ * errors, etc.). Edit controller's "commitCurrentEdit" must return true if the commit has succeeded
+ * and false otherwise. If no edit controller is active, returns true.
+ * @method commitCurrentEdit
+ * @return {Boolean}
+ */
+ this.commitCurrentEdit = function () {
+ return (activeEditController ? activeEditController.commitCurrentEdit() : true);
+ };
+
+ /***
+ * Attempts to cancel the current edit by calling "cancelCurrentEdit" method on the active edit
+ * controller and returns whether the edit was successfully cancelled. If no edit controller is
+ * active, returns true.
+ * @method cancelCurrentEdit
+ * @return {Boolean}
+ */
+ this.cancelCurrentEdit = function cancelCurrentEdit() {
+ return (activeEditController ? activeEditController.cancelCurrentEdit() : true);
+ };
+ }
+})(jQuery);
+
+
diff --git a/web/pgadmin/static/js/slickgrid/slick.dataview.js b/web/pgadmin/static/js/slickgrid/slick.dataview.js
new file mode 100644
index 000000000..f62df669c
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/slick.dataview.js
@@ -0,0 +1,1141 @@
+(function ($) {
+ $.extend(true, window, {
+ Slick: {
+ Data: {
+ DataView: DataView,
+ Aggregators: {
+ Avg: AvgAggregator,
+ Min: MinAggregator,
+ Max: MaxAggregator,
+ Sum: SumAggregator
+ }
+ }
+ }
+ });
+
+
+ /***
+ * A sample Model implementation.
+ * Provides a filtered view of the underlying data.
+ *
+ * Relies on the data item having an "id" property uniquely identifying it.
+ */
+ function DataView(options) {
+ var self = this;
+
+ var defaults = {
+ groupItemMetadataProvider: null,
+ inlineFilters: false
+ };
+
+
+ // private
+ var idProperty = "id"; // property holding a unique row id
+ var items = []; // data by index
+ var rows = []; // data by row
+ var idxById = {}; // indexes by id
+ var rowsById = null; // rows by id; lazy-calculated
+ var filter = null; // filter function
+ var updated = null; // updated item ids
+ var suspend = false; // suspends the recalculation
+ var sortAsc = true;
+ var fastSortField;
+ var sortComparer;
+ var refreshHints = {};
+ var prevRefreshHints = {};
+ var filterArgs;
+ var filteredItems = [];
+ var compiledFilter;
+ var compiledFilterWithCaching;
+ var filterCache = [];
+
+ // grouping
+ var groupingInfoDefaults = {
+ getter: null,
+ formatter: null,
+ comparer: function(a, b) {
+ return (a.value === b.value ? 0 :
+ (a.value > b.value ? 1 : -1)
+ );
+ },
+ predefinedValues: [],
+ aggregators: [],
+ aggregateEmpty: false,
+ aggregateCollapsed: false,
+ aggregateChildGroups: false,
+ collapsed: false,
+ displayTotalsRow: true,
+ lazyTotalsCalculation: false
+ };
+ var groupingInfos = [];
+ var groups = [];
+ var toggledGroupsByLevel = [];
+ var groupingDelimiter = ':|:';
+
+ var pagesize = 0;
+ var pagenum = 0;
+ var totalRows = 0;
+
+ // events
+ var onRowCountChanged = new Slick.Event();
+ var onRowsChanged = new Slick.Event();
+ var onPagingInfoChanged = new Slick.Event();
+
+ options = $.extend(true, {}, defaults, options);
+
+
+ function beginUpdate() {
+ suspend = true;
+ }
+
+ function endUpdate() {
+ suspend = false;
+ refresh();
+ }
+
+ function setRefreshHints(hints) {
+ refreshHints = hints;
+ }
+
+ function setFilterArgs(args) {
+ filterArgs = args;
+ }
+
+ function updateIdxById(startingIndex) {
+ startingIndex = startingIndex || 0;
+ var id;
+ for (var i = startingIndex, l = items.length; i < l; i++) {
+ id = items[i][idProperty];
+ if (id === undefined) {
+ throw "Each data element must implement a unique 'id' property";
+ }
+ idxById[id] = i;
+ }
+ }
+
+ function ensureIdUniqueness() {
+ var id;
+ for (var i = 0, l = items.length; i < l; i++) {
+ id = items[i][idProperty];
+ if (id === undefined || idxById[id] !== i) {
+ throw "Each data element must implement a unique 'id' property";
+ }
+ }
+ }
+
+ function getItems() {
+ return items;
+ }
+
+ function setItems(data, objectIdProperty) {
+ if (objectIdProperty !== undefined) {
+ idProperty = objectIdProperty;
+ }
+ items = filteredItems = data;
+ idxById = {};
+ updateIdxById();
+ ensureIdUniqueness();
+ refresh();
+ }
+
+ function setPagingOptions(args) {
+ if (args.pageSize != undefined) {
+ pagesize = args.pageSize;
+ pagenum = pagesize ? Math.min(pagenum, Math.max(0, Math.ceil(totalRows / pagesize) - 1)) : 0;
+ }
+
+ if (args.pageNum != undefined) {
+ pagenum = Math.min(args.pageNum, Math.max(0, Math.ceil(totalRows / pagesize) - 1));
+ }
+
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
+
+ refresh();
+ }
+
+ function getPagingInfo() {
+ var totalPages = pagesize ? Math.max(1, Math.ceil(totalRows / pagesize)) : 1;
+ return {pageSize: pagesize, pageNum: pagenum, totalRows: totalRows, totalPages: totalPages, dataView: self};
+ }
+
+ function sort(comparer, ascending) {
+ sortAsc = ascending;
+ sortComparer = comparer;
+ fastSortField = null;
+ if (ascending === false) {
+ items.reverse();
+ }
+ items.sort(comparer);
+ if (ascending === false) {
+ items.reverse();
+ }
+ idxById = {};
+ updateIdxById();
+ refresh();
+ }
+
+ /***
+ * Provides a workaround for the extremely slow sorting in IE.
+ * Does a [lexicographic] sort on a give column by temporarily overriding Object.prototype.toString
+ * to return the value of that field and then doing a native Array.sort().
+ */
+ function fastSort(field, ascending) {
+ sortAsc = ascending;
+ fastSortField = field;
+ sortComparer = null;
+ var oldToString = Object.prototype.toString;
+ Object.prototype.toString = (typeof field == "function") ? field : function () {
+ return this[field]
+ };
+ // an extra reversal for descending sort keeps the sort stable
+ // (assuming a stable native sort implementation, which isn't true in some cases)
+ if (ascending === false) {
+ items.reverse();
+ }
+ items.sort();
+ Object.prototype.toString = oldToString;
+ if (ascending === false) {
+ items.reverse();
+ }
+ idxById = {};
+ updateIdxById();
+ refresh();
+ }
+
+ function reSort() {
+ if (sortComparer) {
+ sort(sortComparer, sortAsc);
+ } else if (fastSortField) {
+ fastSort(fastSortField, sortAsc);
+ }
+ }
+
+ function setFilter(filterFn) {
+ filter = filterFn;
+ if (options.inlineFilters) {
+ compiledFilter = compileFilter();
+ compiledFilterWithCaching = compileFilterWithCaching();
+ }
+ refresh();
+ }
+
+ function getGrouping() {
+ return groupingInfos;
+ }
+
+ function setGrouping(groupingInfo) {
+ if (!options.groupItemMetadataProvider) {
+ options.groupItemMetadataProvider = new Slick.Data.GroupItemMetadataProvider();
+ }
+
+ groups = [];
+ toggledGroupsByLevel = [];
+ groupingInfo = groupingInfo || [];
+ groupingInfos = (groupingInfo instanceof Array) ? groupingInfo : [groupingInfo];
+
+ for (var i = 0; i < groupingInfos.length; i++) {
+ var gi = groupingInfos[i] = $.extend(true, {}, groupingInfoDefaults, groupingInfos[i]);
+ gi.getterIsAFn = typeof gi.getter === "function";
+
+ // pre-compile accumulator loops
+ gi.compiledAccumulators = [];
+ var idx = gi.aggregators.length;
+ while (idx--) {
+ gi.compiledAccumulators[idx] = compileAccumulatorLoop(gi.aggregators[idx]);
+ }
+
+ toggledGroupsByLevel[i] = {};
+ }
+
+ refresh();
+ }
+
+ /**
+ * @deprecated Please use {@link setGrouping}.
+ */
+ function groupBy(valueGetter, valueFormatter, sortComparer) {
+ if (valueGetter == null) {
+ setGrouping([]);
+ return;
+ }
+
+ setGrouping({
+ getter: valueGetter,
+ formatter: valueFormatter,
+ comparer: sortComparer
+ });
+ }
+
+ /**
+ * @deprecated Please use {@link setGrouping}.
+ */
+ function setAggregators(groupAggregators, includeCollapsed) {
+ if (!groupingInfos.length) {
+ throw new Error("At least one grouping must be specified before calling setAggregators().");
+ }
+
+ groupingInfos[0].aggregators = groupAggregators;
+ groupingInfos[0].aggregateCollapsed = includeCollapsed;
+
+ setGrouping(groupingInfos);
+ }
+
+ function getItemByIdx(i) {
+ return items[i];
+ }
+
+ function getIdxById(id) {
+ return idxById[id];
+ }
+
+ function ensureRowsByIdCache() {
+ if (!rowsById) {
+ rowsById = {};
+ for (var i = 0, l = rows.length; i < l; i++) {
+ rowsById[rows[i][idProperty]] = i;
+ }
+ }
+ }
+
+ function getRowById(id) {
+ ensureRowsByIdCache();
+ return rowsById[id];
+ }
+
+ function getItemById(id) {
+ return items[idxById[id]];
+ }
+
+ function mapIdsToRows(idArray) {
+ var rows = [];
+ ensureRowsByIdCache();
+ for (var i = 0, l = idArray.length; i < l; i++) {
+ var row = rowsById[idArray[i]];
+ if (row != null) {
+ rows[rows.length] = row;
+ }
+ }
+ return rows;
+ }
+
+ function mapRowsToIds(rowArray) {
+ var ids = [];
+ for (var i = 0, l = rowArray.length; i < l; i++) {
+ if (rowArray[i] < rows.length) {
+ ids[ids.length] = rows[rowArray[i]][idProperty];
+ }
+ }
+ return ids;
+ }
+
+ function updateItem(id, item) {
+ if (idxById[id] === undefined || id !== item[idProperty]) {
+ throw "Invalid or non-matching id";
+ }
+ items[idxById[id]] = item;
+ if (!updated) {
+ updated = {};
+ }
+ updated[id] = true;
+ refresh();
+ }
+
+ function insertItem(insertBefore, item) {
+ items.splice(insertBefore, 0, item);
+ updateIdxById(insertBefore);
+ refresh();
+ }
+
+ function addItem(item) {
+ items.push(item);
+ updateIdxById(items.length - 1);
+ refresh();
+ }
+
+ function deleteItem(id) {
+ var idx = idxById[id];
+ if (idx === undefined) {
+ throw "Invalid id";
+ }
+ delete idxById[id];
+ items.splice(idx, 1);
+ updateIdxById(idx);
+ refresh();
+ }
+
+ function getLength() {
+ return rows.length;
+ }
+
+ function getItem(i) {
+ var item = rows[i];
+
+ // if this is a group row, make sure totals are calculated and update the title
+ if (item && item.__group && item.totals && !item.totals.initialized) {
+ var gi = groupingInfos[item.level];
+ if (!gi.displayTotalsRow) {
+ calculateTotals(item.totals);
+ item.title = gi.formatter ? gi.formatter(item) : item.value;
+ }
+ }
+ // if this is a totals row, make sure it's calculated
+ else if (item && item.__groupTotals && !item.initialized) {
+ calculateTotals(item);
+ }
+
+ return item;
+ }
+
+ function getItemMetadata(i) {
+ var item = rows[i];
+ if (item === undefined) {
+ return null;
+ }
+
+ // overrides for grouping rows
+ if (item.__group) {
+ return options.groupItemMetadataProvider.getGroupRowMetadata(item);
+ }
+
+ // overrides for totals rows
+ if (item.__groupTotals) {
+ return options.groupItemMetadataProvider.getTotalsRowMetadata(item);
+ }
+
+ return null;
+ }
+
+ function expandCollapseAllGroups(level, collapse) {
+ if (level == null) {
+ for (var i = 0; i < groupingInfos.length; i++) {
+ toggledGroupsByLevel[i] = {};
+ groupingInfos[i].collapsed = collapse;
+ }
+ } else {
+ toggledGroupsByLevel[level] = {};
+ groupingInfos[level].collapsed = collapse;
+ }
+ refresh();
+ }
+
+ /**
+ * @param level {Number} Optional level to collapse. If not specified, applies to all levels.
+ */
+ function collapseAllGroups(level) {
+ expandCollapseAllGroups(level, true);
+ }
+
+ /**
+ * @param level {Number} Optional level to expand. If not specified, applies to all levels.
+ */
+ function expandAllGroups(level) {
+ expandCollapseAllGroups(level, false);
+ }
+
+ function expandCollapseGroup(level, groupingKey, collapse) {
+ toggledGroupsByLevel[level][groupingKey] = groupingInfos[level].collapsed ^ collapse;
+ refresh();
+ }
+
+ /**
+ * @param varArgs Either a Slick.Group's "groupingKey" property, or a
+ * variable argument list of grouping values denoting a unique path to the row. For
+ * example, calling collapseGroup('high', '10%') will collapse the '10%' subgroup of
+ * the 'high' group.
+ */
+ function collapseGroup(varArgs) {
+ var args = Array.prototype.slice.call(arguments);
+ var arg0 = args[0];
+ if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
+ expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, true);
+ } else {
+ expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), true);
+ }
+ }
+
+ /**
+ * @param varArgs Either a Slick.Group's "groupingKey" property, or a
+ * variable argument list of grouping values denoting a unique path to the row. For
+ * example, calling expandGroup('high', '10%') will expand the '10%' subgroup of
+ * the 'high' group.
+ */
+ function expandGroup(varArgs) {
+ var args = Array.prototype.slice.call(arguments);
+ var arg0 = args[0];
+ if (args.length == 1 && arg0.indexOf(groupingDelimiter) != -1) {
+ expandCollapseGroup(arg0.split(groupingDelimiter).length - 1, arg0, false);
+ } else {
+ expandCollapseGroup(args.length - 1, args.join(groupingDelimiter), false);
+ }
+ }
+
+ function getGroups() {
+ return groups;
+ }
+
+ function extractGroups(rows, parentGroup) {
+ var group;
+ var val;
+ var groups = [];
+ var groupsByVal = {};
+ var r;
+ var level = parentGroup ? parentGroup.level + 1 : 0;
+ var gi = groupingInfos[level];
+
+ for (var i = 0, l = gi.predefinedValues.length; i < l; i++) {
+ val = gi.predefinedValues[i];
+ group = groupsByVal[val];
+ if (!group) {
+ group = new Slick.Group();
+ group.value = val;
+ group.level = level;
+ group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
+ groups[groups.length] = group;
+ groupsByVal[val] = group;
+ }
+ }
+
+ for (var i = 0, l = rows.length; i < l; i++) {
+ r = rows[i];
+ val = gi.getterIsAFn ? gi.getter(r) : r[gi.getter];
+ group = groupsByVal[val];
+ if (!group) {
+ group = new Slick.Group();
+ group.value = val;
+ group.level = level;
+ group.groupingKey = (parentGroup ? parentGroup.groupingKey + groupingDelimiter : '') + val;
+ groups[groups.length] = group;
+ groupsByVal[val] = group;
+ }
+
+ group.rows[group.count++] = r;
+ }
+
+ if (level < groupingInfos.length - 1) {
+ for (var i = 0; i < groups.length; i++) {
+ group = groups[i];
+ group.groups = extractGroups(group.rows, group);
+ }
+ }
+
+ groups.sort(groupingInfos[level].comparer);
+
+ return groups;
+ }
+
+ function calculateTotals(totals) {
+ var group = totals.group;
+ var gi = groupingInfos[group.level];
+ var isLeafLevel = (group.level == groupingInfos.length);
+ var agg, idx = gi.aggregators.length;
+
+ if (!isLeafLevel && gi.aggregateChildGroups) {
+ // make sure all the subgroups are calculated
+ var i = group.groups.length;
+ while (i--) {
+ if (!group.groups[i].totals.initialized) {
+ calculateTotals(group.groups[i].totals);
+ }
+ }
+ }
+
+ while (idx--) {
+ agg = gi.aggregators[idx];
+ agg.init();
+ if (!isLeafLevel && gi.aggregateChildGroups) {
+ gi.compiledAccumulators[idx].call(agg, group.groups);
+ } else {
+ gi.compiledAccumulators[idx].call(agg, group.rows);
+ }
+ agg.storeResult(totals);
+ }
+ totals.initialized = true;
+ }
+
+ function addGroupTotals(group) {
+ var gi = groupingInfos[group.level];
+ var totals = new Slick.GroupTotals();
+ totals.group = group;
+ group.totals = totals;
+ if (!gi.lazyTotalsCalculation) {
+ calculateTotals(totals);
+ }
+ }
+
+ function addTotals(groups, level) {
+ level = level || 0;
+ var gi = groupingInfos[level];
+ var groupCollapsed = gi.collapsed;
+ var toggledGroups = toggledGroupsByLevel[level];
+ var idx = groups.length, g;
+ while (idx--) {
+ g = groups[idx];
+
+ if (g.collapsed && !gi.aggregateCollapsed) {
+ continue;
+ }
+
+ // Do a depth-first aggregation so that parent group aggregators can access subgroup totals.
+ if (g.groups) {
+ addTotals(g.groups, level + 1);
+ }
+
+ if (gi.aggregators.length && (
+ gi.aggregateEmpty || g.rows.length || (g.groups && g.groups.length))) {
+ addGroupTotals(g);
+ }
+
+ g.collapsed = groupCollapsed ^ toggledGroups[g.groupingKey];
+ g.title = gi.formatter ? gi.formatter(g) : g.value;
+ }
+ }
+
+ function flattenGroupedRows(groups, level) {
+ level = level || 0;
+ var gi = groupingInfos[level];
+ var groupedRows = [], rows, gl = 0, g;
+ for (var i = 0, l = groups.length; i < l; i++) {
+ g = groups[i];
+ groupedRows[gl++] = g;
+
+ if (!g.collapsed) {
+ rows = g.groups ? flattenGroupedRows(g.groups, level + 1) : g.rows;
+ for (var j = 0, jj = rows.length; j < jj; j++) {
+ groupedRows[gl++] = rows[j];
+ }
+ }
+
+ if (g.totals && gi.displayTotalsRow && (!g.collapsed || gi.aggregateCollapsed)) {
+ groupedRows[gl++] = g.totals;
+ }
+ }
+ return groupedRows;
+ }
+
+ function getFunctionInfo(fn) {
+ var fnRegex = /^function[^(]*\(([^)]*)\)\s*{([\s\S]*)}$/;
+ var matches = fn.toString().match(fnRegex);
+ return {
+ params: matches[1].split(","),
+ body: matches[2]
+ };
+ }
+
+ function compileAccumulatorLoop(aggregator) {
+ var accumulatorInfo = getFunctionInfo(aggregator.accumulate);
+ var fn = new Function(
+ "_items",
+ "for (var " + accumulatorInfo.params[0] + ", _i=0, _il=_items.length; _i<_il; _i++) {" +
+ accumulatorInfo.params[0] + " = _items[_i]; " +
+ accumulatorInfo.body +
+ "}"
+ );
+ fn.displayName = fn.name = "compiledAccumulatorLoop";
+ return fn;
+ }
+
+ function compileFilter() {
+ var filterInfo = getFunctionInfo(filter);
+
+ var filterPath1 = "{ continue _coreloop; }$1";
+ var filterPath2 = "{ _retval[_idx++] = $item$; continue _coreloop; }$1";
+ // make some allowances for minification - there's only so far we can go with RegEx
+ var filterBody = filterInfo.body
+ .replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
+ .replace(/return!1([;}]|\}|$)/gi, filterPath1)
+ .replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
+ .replace(/return!0([;}]|\}|$)/gi, filterPath2)
+ .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
+ "{ if ($1) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
+
+ // This preserves the function template code after JS compression,
+ // so that replace() commands still work as expected.
+ var tpl = [
+ //"function(_items, _args) { ",
+ "var _retval = [], _idx = 0; ",
+ "var $item$, $args$ = _args; ",
+ "_coreloop: ",
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
+ "$item$ = _items[_i]; ",
+ "$filter$; ",
+ "} ",
+ "return _retval; "
+ //"}"
+ ].join("");
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
+
+ var fn = new Function("_items,_args", tpl);
+ fn.displayName = fn.name = "compiledFilter";
+ return fn;
+ }
+
+ function compileFilterWithCaching() {
+ var filterInfo = getFunctionInfo(filter);
+
+ var filterPath1 = "{ continue _coreloop; }$1";
+ var filterPath2 = "{ _cache[_i] = true;_retval[_idx++] = $item$; continue _coreloop; }$1";
+ // make some allowances for minification - there's only so far we can go with RegEx
+ var filterBody = filterInfo.body
+ .replace(/return false\s*([;}]|\}|$)/gi, filterPath1)
+ .replace(/return!1([;}]|\}|$)/gi, filterPath1)
+ .replace(/return true\s*([;}]|\}|$)/gi, filterPath2)
+ .replace(/return!0([;}]|\}|$)/gi, filterPath2)
+ .replace(/return ([^;}]+?)\s*([;}]|$)/gi,
+ "{ if ((_cache[_i] = $1)) { _retval[_idx++] = $item$; }; continue _coreloop; }$2");
+
+ // This preserves the function template code after JS compression,
+ // so that replace() commands still work as expected.
+ var tpl = [
+ //"function(_items, _args, _cache) { ",
+ "var _retval = [], _idx = 0; ",
+ "var $item$, $args$ = _args; ",
+ "_coreloop: ",
+ "for (var _i = 0, _il = _items.length; _i < _il; _i++) { ",
+ "$item$ = _items[_i]; ",
+ "if (_cache[_i]) { ",
+ "_retval[_idx++] = $item$; ",
+ "continue _coreloop; ",
+ "} ",
+ "$filter$; ",
+ "} ",
+ "return _retval; "
+ //"}"
+ ].join("");
+ tpl = tpl.replace(/\$filter\$/gi, filterBody);
+ tpl = tpl.replace(/\$item\$/gi, filterInfo.params[0]);
+ tpl = tpl.replace(/\$args\$/gi, filterInfo.params[1]);
+
+ var fn = new Function("_items,_args,_cache", tpl);
+ fn.displayName = fn.name = "compiledFilterWithCaching";
+ return fn;
+ }
+
+ function uncompiledFilter(items, args) {
+ var retval = [], idx = 0;
+
+ for (var i = 0, ii = items.length; i < ii; i++) {
+ if (filter(items[i], args)) {
+ retval[idx++] = items[i];
+ }
+ }
+
+ return retval;
+ }
+
+ function uncompiledFilterWithCaching(items, args, cache) {
+ var retval = [], idx = 0, item;
+
+ for (var i = 0, ii = items.length; i < ii; i++) {
+ item = items[i];
+ if (cache[i]) {
+ retval[idx++] = item;
+ } else if (filter(item, args)) {
+ retval[idx++] = item;
+ cache[i] = true;
+ }
+ }
+
+ return retval;
+ }
+
+ function getFilteredAndPagedItems(items) {
+ if (filter) {
+ var batchFilter = options.inlineFilters ? compiledFilter : uncompiledFilter;
+ var batchFilterWithCaching = options.inlineFilters ? compiledFilterWithCaching : uncompiledFilterWithCaching;
+
+ if (refreshHints.isFilterNarrowing) {
+ filteredItems = batchFilter(filteredItems, filterArgs);
+ } else if (refreshHints.isFilterExpanding) {
+ filteredItems = batchFilterWithCaching(items, filterArgs, filterCache);
+ } else if (!refreshHints.isFilterUnchanged) {
+ filteredItems = batchFilter(items, filterArgs);
+ }
+ } else {
+ // special case: if not filtering and not paging, the resulting
+ // rows collection needs to be a copy so that changes due to sort
+ // can be caught
+ filteredItems = pagesize ? items : items.concat();
+ }
+
+ // get the current page
+ var paged;
+ if (pagesize) {
+ if (filteredItems.length < pagenum * pagesize) {
+ pagenum = Math.floor(filteredItems.length / pagesize);
+ }
+ paged = filteredItems.slice(pagesize * pagenum, pagesize * pagenum + pagesize);
+ } else {
+ paged = filteredItems;
+ }
+
+ return {totalRows: filteredItems.length, rows: paged};
+ }
+
+ function getRowDiffs(rows, newRows) {
+ var item, r, eitherIsNonData, diff = [];
+ var from = 0, to = newRows.length;
+
+ if (refreshHints && refreshHints.ignoreDiffsBefore) {
+ from = Math.max(0,
+ Math.min(newRows.length, refreshHints.ignoreDiffsBefore));
+ }
+
+ if (refreshHints && refreshHints.ignoreDiffsAfter) {
+ to = Math.min(newRows.length,
+ Math.max(0, refreshHints.ignoreDiffsAfter));
+ }
+
+ for (var i = from, rl = rows.length; i < to; i++) {
+ if (i >= rl) {
+ diff[diff.length] = i;
+ } else {
+ item = newRows[i];
+ r = rows[i];
+
+ if ((groupingInfos.length && (eitherIsNonData = (item.__nonDataRow) || (r.__nonDataRow)) &&
+ item.__group !== r.__group ||
+ item.__group && !item.equals(r))
+ || (eitherIsNonData &&
+ // no good way to compare totals since they are arbitrary DTOs
+ // deep object comparison is pretty expensive
+ // always considering them 'dirty' seems easier for the time being
+ (item.__groupTotals || r.__groupTotals))
+ || item[idProperty] != r[idProperty]
+ || (updated && updated[item[idProperty]])
+ ) {
+ diff[diff.length] = i;
+ }
+ }
+ }
+ return diff;
+ }
+
+ function recalc(_items) {
+ rowsById = null;
+
+ if (refreshHints.isFilterNarrowing != prevRefreshHints.isFilterNarrowing ||
+ refreshHints.isFilterExpanding != prevRefreshHints.isFilterExpanding) {
+ filterCache = [];
+ }
+
+ var filteredItems = getFilteredAndPagedItems(_items);
+ totalRows = filteredItems.totalRows;
+ var newRows = filteredItems.rows;
+
+ groups = [];
+ if (groupingInfos.length) {
+ groups = extractGroups(newRows);
+ if (groups.length) {
+ addTotals(groups);
+ newRows = flattenGroupedRows(groups);
+ }
+ }
+
+ var diff = getRowDiffs(rows, newRows);
+
+ rows = newRows;
+
+ return diff;
+ }
+
+ function refresh() {
+ if (suspend) {
+ return;
+ }
+
+ var countBefore = rows.length;
+ var totalRowsBefore = totalRows;
+
+ var diff = recalc(items, filter); // pass as direct refs to avoid closure perf hit
+
+ // if the current page is no longer valid, go to last page and recalc
+ // we suffer a performance penalty here, but the main loop (recalc) remains highly optimized
+ if (pagesize && totalRows < pagenum * pagesize) {
+ pagenum = Math.max(0, Math.ceil(totalRows / pagesize) - 1);
+ diff = recalc(items, filter);
+ }
+
+ updated = null;
+ prevRefreshHints = refreshHints;
+ refreshHints = {};
+
+ if (totalRowsBefore !== totalRows) {
+ onPagingInfoChanged.notify(getPagingInfo(), null, self);
+ }
+ if (countBefore !== rows.length) {
+ onRowCountChanged.notify({previous: countBefore, current: rows.length, dataView: self}, null, self);
+ }
+ if (diff.length > 0) {
+ onRowsChanged.notify({rows: diff, dataView: self}, null, self);
+ }
+ }
+
+ /***
+ * Wires the grid and the DataView together to keep row selection tied to item ids.
+ * This is useful since, without it, the grid only knows about rows, so if the items
+ * move around, the same rows stay selected instead of the selection moving along
+ * with the items.
+ *
+ * NOTE: This doesn't work with cell selection model.
+ *
+ * @param grid {Slick.Grid} The grid to sync selection with.
+ * @param preserveHidden {Boolean} Whether to keep selected items that go out of the
+ * view due to them getting filtered out.
+ * @param preserveHiddenOnSelectionChange {Boolean} Whether to keep selected items
+ * that are currently out of the view (see preserveHidden) as selected when selection
+ * changes.
+ * @return {Slick.Event} An event that notifies when an internal list of selected row ids
+ * changes. This is useful since, in combination with the above two options, it allows
+ * access to the full list selected row ids, and not just the ones visible to the grid.
+ * @method syncGridSelection
+ */
+ function syncGridSelection(grid, preserveHidden, preserveHiddenOnSelectionChange) {
+ var self = this;
+ var inHandler;
+ var selectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
+ var onSelectedRowIdsChanged = new Slick.Event();
+
+ function setSelectedRowIds(rowIds) {
+ if (selectedRowIds.join(",") == rowIds.join(",")) {
+ return;
+ }
+
+ selectedRowIds = rowIds;
+
+ onSelectedRowIdsChanged.notify({
+ "grid": grid,
+ "ids": selectedRowIds,
+ "dataView": self
+ }, new Slick.EventData(), self);
+ }
+
+ function update() {
+ if (selectedRowIds.length > 0) {
+ inHandler = true;
+ var selectedRows = self.mapIdsToRows(selectedRowIds);
+ if (!preserveHidden) {
+ setSelectedRowIds(self.mapRowsToIds(selectedRows));
+ }
+ grid.setSelectedRows(selectedRows);
+ inHandler = false;
+ }
+ }
+
+ grid.onSelectedRowsChanged.subscribe(function(e, args) {
+ if (inHandler) { return; }
+ var newSelectedRowIds = self.mapRowsToIds(grid.getSelectedRows());
+ if (!preserveHiddenOnSelectionChange || !grid.getOptions().multiSelect) {
+ setSelectedRowIds(newSelectedRowIds);
+ } else {
+ // keep the ones that are hidden
+ var existing = $.grep(selectedRowIds, function(id) { return self.getRowById(id) === undefined; });
+ // add the newly selected ones
+ setSelectedRowIds(existing.concat(newSelectedRowIds));
+ }
+ });
+
+ this.onRowsChanged.subscribe(update);
+
+ this.onRowCountChanged.subscribe(update);
+
+ return onSelectedRowIdsChanged;
+ }
+
+ function syncGridCellCssStyles(grid, key) {
+ var hashById;
+ var inHandler;
+
+ // since this method can be called after the cell styles have been set,
+ // get the existing ones right away
+ storeCellCssStyles(grid.getCellCssStyles(key));
+
+ function storeCellCssStyles(hash) {
+ hashById = {};
+ for (var row in hash) {
+ var id = rows[row][idProperty];
+ hashById[id] = hash[row];
+ }
+ }
+
+ function update() {
+ if (hashById) {
+ inHandler = true;
+ ensureRowsByIdCache();
+ var newHash = {};
+ for (var id in hashById) {
+ var row = rowsById[id];
+ if (row != undefined) {
+ newHash[row] = hashById[id];
+ }
+ }
+ grid.setCellCssStyles(key, newHash);
+ inHandler = false;
+ }
+ }
+
+ grid.onCellCssStylesChanged.subscribe(function(e, args) {
+ if (inHandler) { return; }
+ if (key != args.key) { return; }
+ if (args.hash) {
+ storeCellCssStyles(args.hash);
+ }
+ });
+
+ this.onRowsChanged.subscribe(update);
+
+ this.onRowCountChanged.subscribe(update);
+ }
+
+ $.extend(this, {
+ // methods
+ "beginUpdate": beginUpdate,
+ "endUpdate": endUpdate,
+ "setPagingOptions": setPagingOptions,
+ "getPagingInfo": getPagingInfo,
+ "getItems": getItems,
+ "setItems": setItems,
+ "setFilter": setFilter,
+ "sort": sort,
+ "fastSort": fastSort,
+ "reSort": reSort,
+ "setGrouping": setGrouping,
+ "getGrouping": getGrouping,
+ "groupBy": groupBy,
+ "setAggregators": setAggregators,
+ "collapseAllGroups": collapseAllGroups,
+ "expandAllGroups": expandAllGroups,
+ "collapseGroup": collapseGroup,
+ "expandGroup": expandGroup,
+ "getGroups": getGroups,
+ "getIdxById": getIdxById,
+ "getRowById": getRowById,
+ "getItemById": getItemById,
+ "getItemByIdx": getItemByIdx,
+ "mapRowsToIds": mapRowsToIds,
+ "mapIdsToRows": mapIdsToRows,
+ "setRefreshHints": setRefreshHints,
+ "setFilterArgs": setFilterArgs,
+ "refresh": refresh,
+ "updateItem": updateItem,
+ "insertItem": insertItem,
+ "addItem": addItem,
+ "deleteItem": deleteItem,
+ "syncGridSelection": syncGridSelection,
+ "syncGridCellCssStyles": syncGridCellCssStyles,
+
+ // data provider methods
+ "getLength": getLength,
+ "getItem": getItem,
+ "getItemMetadata": getItemMetadata,
+
+ // events
+ "onRowCountChanged": onRowCountChanged,
+ "onRowsChanged": onRowsChanged,
+ "onPagingInfoChanged": onPagingInfoChanged
+ });
+ }
+
+ function AvgAggregator(field) {
+ this.field_ = field;
+
+ this.init = function () {
+ this.count_ = 0;
+ this.nonNullCount_ = 0;
+ this.sum_ = 0;
+ };
+
+ this.accumulate = function (item) {
+ var val = item[this.field_];
+ this.count_++;
+ if (val != null && val !== "" && !isNaN(val)) {
+ this.nonNullCount_++;
+ this.sum_ += parseFloat(val);
+ }
+ };
+
+ this.storeResult = function (groupTotals) {
+ if (!groupTotals.avg) {
+ groupTotals.avg = {};
+ }
+ if (this.nonNullCount_ != 0) {
+ groupTotals.avg[this.field_] = this.sum_ / this.nonNullCount_;
+ }
+ };
+ }
+
+ function MinAggregator(field) {
+ this.field_ = field;
+
+ this.init = function () {
+ this.min_ = null;
+ };
+
+ this.accumulate = function (item) {
+ var val = item[this.field_];
+ if (val != null && val !== "" && !isNaN(val)) {
+ if (this.min_ == null || val < this.min_) {
+ this.min_ = val;
+ }
+ }
+ };
+
+ this.storeResult = function (groupTotals) {
+ if (!groupTotals.min) {
+ groupTotals.min = {};
+ }
+ groupTotals.min[this.field_] = this.min_;
+ }
+ }
+
+ function MaxAggregator(field) {
+ this.field_ = field;
+
+ this.init = function () {
+ this.max_ = null;
+ };
+
+ this.accumulate = function (item) {
+ var val = item[this.field_];
+ if (val != null && val !== "" && !isNaN(val)) {
+ if (this.max_ == null || val > this.max_) {
+ this.max_ = val;
+ }
+ }
+ };
+
+ this.storeResult = function (groupTotals) {
+ if (!groupTotals.max) {
+ groupTotals.max = {};
+ }
+ groupTotals.max[this.field_] = this.max_;
+ }
+ }
+
+ function SumAggregator(field) {
+ this.field_ = field;
+
+ this.init = function () {
+ this.sum_ = null;
+ };
+
+ this.accumulate = function (item) {
+ var val = item[this.field_];
+ if (val != null && val !== "" && !isNaN(val)) {
+ this.sum_ += parseFloat(val);
+ }
+ };
+
+ this.storeResult = function (groupTotals) {
+ if (!groupTotals.sum) {
+ groupTotals.sum = {};
+ }
+ groupTotals.sum[this.field_] = this.sum_;
+ }
+ }
+
+ // TODO: add more built-in aggregators
+ // TODO: merge common aggregators in one to prevent needles iterating
+
+})(jQuery);
diff --git a/web/pgadmin/static/js/slickgrid/slick.editors.js b/web/pgadmin/static/js/slickgrid/slick.editors.js
new file mode 100644
index 000000000..e852592cd
--- /dev/null
+++ b/web/pgadmin/static/js/slickgrid/slick.editors.js
@@ -0,0 +1,631 @@
+/***
+ * Contains basic SlickGrid editors.
+ * @module Editors
+ * @namespace Slick
+ */
+
+(function ($) {
+ // register namespace
+ $.extend(true, window, {
+ "Slick": {
+ "Editors": {
+ "Text": TextEditor,
+ "Integer": IntegerEditor,
+ "Float": FloatEditor,
+ "Date": DateEditor,
+ "YesNoSelect": YesNoSelectEditor,
+ "Checkbox": CheckboxEditor,
+ "PercentComplete": PercentCompleteEditor,
+ "LongText": LongTextEditor
+ }
+ }
+ });
+
+ function TextEditor(args) {
+ var $input;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $input = $("")
+ .appendTo(args.container)
+ .bind("keydown.nav", function (e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ })
+ .focus()
+ .select();
+ };
+
+ this.destroy = function () {
+ $input.remove();
+ };
+
+ this.focus = function () {
+ $input.focus();
+ };
+
+ this.getValue = function () {
+ return $input.val();
+ };
+
+ this.setValue = function (val) {
+ $input.val(val);
+ };
+
+ this.loadValue = function (item) {
+ defaultValue = item[args.column.field] || "";
+ $input.val(defaultValue);
+ $input[0].defaultValue = defaultValue;
+ $input.select();
+ };
+
+ this.serializeValue = function () {
+ return $input.val();
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
+
+ this.validate = function () {
+ if (args.column.validator) {
+ var validationResults = args.column.validator($input.val());
+ if (!validationResults.valid) {
+ return validationResults;
+ }
+ }
+
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ function IntegerEditor(args) {
+ var $input;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $input = $("");
+
+ $input.bind("keydown.nav", function (e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ });
+
+ $input.appendTo(args.container);
+ $input.focus().select();
+ };
+
+ this.destroy = function () {
+ $input.remove();
+ };
+
+ this.focus = function () {
+ $input.focus();
+ };
+
+ this.loadValue = function (item) {
+ defaultValue = item[args.column.field];
+ $input.val(defaultValue);
+ $input[0].defaultValue = defaultValue;
+ $input.select();
+ };
+
+ this.serializeValue = function () {
+ return parseInt($input.val(), 10) || 0;
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
+
+ this.validate = function () {
+ if (isNaN($input.val())) {
+ return {
+ valid: false,
+ msg: "Please enter a valid integer"
+ };
+ }
+
+ if (args.column.validator) {
+ var validationResults = args.column.validator($input.val());
+ if (!validationResults.valid) {
+ return validationResults;
+ }
+ }
+
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ function FloatEditor(args) {
+ var $input;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $input = $("");
+
+ $input.bind("keydown.nav", function (e) {
+ if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
+ e.stopImmediatePropagation();
+ }
+ });
+
+ $input.appendTo(args.container);
+ $input.focus().select();
+ };
+
+ this.destroy = function () {
+ $input.remove();
+ };
+
+ this.focus = function () {
+ $input.focus();
+ };
+
+ function getDecimalPlaces() {
+ // returns the number of fixed decimal places or null
+ var rtn = args.column.editorFixedDecimalPlaces;
+ if (typeof rtn == 'undefined') {
+ rtn = FloatEditor.DefaultDecimalPlaces;
+ }
+ return (!rtn && rtn!==0 ? null : rtn);
+ }
+
+ this.loadValue = function (item) {
+ defaultValue = item[args.column.field];
+
+ var decPlaces = getDecimalPlaces();
+ if (decPlaces !== null
+ && (defaultValue || defaultValue===0)
+ && defaultValue.toFixed) {
+ defaultValue = defaultValue.toFixed(decPlaces);
+ }
+
+ $input.val(defaultValue);
+ $input[0].defaultValue = defaultValue;
+ $input.select();
+ };
+
+ this.serializeValue = function () {
+ var rtn = parseFloat($input.val()) || 0;
+
+ var decPlaces = getDecimalPlaces();
+ if (decPlaces !== null
+ && (rtn || rtn===0)
+ && rtn.toFixed) {
+ rtn = parseFloat(rtn.toFixed(decPlaces));
+ }
+
+ return rtn;
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
+
+ this.validate = function () {
+ if (isNaN($input.val())) {
+ return {
+ valid: false,
+ msg: "Please enter a valid number"
+ };
+ }
+
+ if (args.column.validator) {
+ var validationResults = args.column.validator($input.val());
+ if (!validationResults.valid) {
+ return validationResults;
+ }
+ }
+
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ FloatEditor.DefaultDecimalPlaces = null;
+
+ function DateEditor(args) {
+ var $input;
+ var defaultValue;
+ var scope = this;
+ var calendarOpen = false;
+
+ this.init = function () {
+ $input = $("");
+ $input.appendTo(args.container);
+ $input.focus().select();
+ $input.datepicker({
+ showOn: "button",
+ buttonImageOnly: true,
+ buttonImage: "../images/calendar.gif",
+ beforeShow: function () {
+ calendarOpen = true
+ },
+ onClose: function () {
+ calendarOpen = false
+ }
+ });
+ $input.width($input.width() - 18);
+ };
+
+ this.destroy = function () {
+ $.datepicker.dpDiv.stop(true, true);
+ $input.datepicker("hide");
+ $input.datepicker("destroy");
+ $input.remove();
+ };
+
+ this.show = function () {
+ if (calendarOpen) {
+ $.datepicker.dpDiv.stop(true, true).show();
+ }
+ };
+
+ this.hide = function () {
+ if (calendarOpen) {
+ $.datepicker.dpDiv.stop(true, true).hide();
+ }
+ };
+
+ this.position = function (position) {
+ if (!calendarOpen) {
+ return;
+ }
+ $.datepicker.dpDiv
+ .css("top", position.top + 30)
+ .css("left", position.left);
+ };
+
+ this.focus = function () {
+ $input.focus();
+ };
+
+ this.loadValue = function (item) {
+ defaultValue = item[args.column.field];
+ $input.val(defaultValue);
+ $input[0].defaultValue = defaultValue;
+ $input.select();
+ };
+
+ this.serializeValue = function () {
+ return $input.val();
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (!($input.val() == "" && defaultValue == null)) && ($input.val() != defaultValue);
+ };
+
+ this.validate = function () {
+ if (args.column.validator) {
+ var validationResults = args.column.validator($input.val());
+ if (!validationResults.valid) {
+ return validationResults;
+ }
+ }
+
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ function YesNoSelectEditor(args) {
+ var $select;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $select = $("");
+ $select.appendTo(args.container);
+ $select.focus();
+ };
+
+ this.destroy = function () {
+ $select.remove();
+ };
+
+ this.focus = function () {
+ $select.focus();
+ };
+
+ this.loadValue = function (item) {
+ $select.val((defaultValue = item[args.column.field]) ? "yes" : "no");
+ $select.select();
+ };
+
+ this.serializeValue = function () {
+ return ($select.val() == "yes");
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return ($select.val() != defaultValue);
+ };
+
+ this.validate = function () {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ function CheckboxEditor(args) {
+ var $select;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $select = $("");
+ $select.appendTo(args.container);
+ $select.focus();
+ };
+
+ this.destroy = function () {
+ $select.remove();
+ };
+
+ this.focus = function () {
+ $select.focus();
+ };
+
+ this.loadValue = function (item) {
+ defaultValue = !!item[args.column.field];
+ if (defaultValue) {
+ $select.prop('checked', true);
+ } else {
+ $select.prop('checked', false);
+ }
+ };
+
+ this.serializeValue = function () {
+ return $select.prop('checked');
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (this.serializeValue() !== defaultValue);
+ };
+
+ this.validate = function () {
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ function PercentCompleteEditor(args) {
+ var $input, $picker;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ $input = $("");
+ $input.width($(args.container).innerWidth() - 25);
+ $input.appendTo(args.container);
+
+ $picker = $("
").appendTo(args.container);
+ $picker.append("
");
+
+ $picker.find(".editor-percentcomplete-buttons").append("
");
+
+ $input.focus().select();
+
+ $picker.find(".editor-percentcomplete-slider").slider({
+ orientation: "vertical",
+ range: "min",
+ value: defaultValue,
+ slide: function (event, ui) {
+ $input.val(ui.value)
+ }
+ });
+
+ $picker.find(".editor-percentcomplete-buttons button").bind("click", function (e) {
+ $input.val($(this).attr("val"));
+ $picker.find(".editor-percentcomplete-slider").slider("value", $(this).attr("val"));
+ })
+ };
+
+ this.destroy = function () {
+ $input.remove();
+ $picker.remove();
+ };
+
+ this.focus = function () {
+ $input.focus();
+ };
+
+ this.loadValue = function (item) {
+ $input.val(defaultValue = item[args.column.field]);
+ $input.select();
+ };
+
+ this.serializeValue = function () {
+ return parseInt($input.val(), 10) || 0;
+ };
+
+ this.applyValue = function (item, state) {
+ item[args.column.field] = state;
+ };
+
+ this.isValueChanged = function () {
+ return (!($input.val() == "" && defaultValue == null)) && ((parseInt($input.val(), 10) || 0) != defaultValue);
+ };
+
+ this.validate = function () {
+ if (isNaN(parseInt($input.val(), 10))) {
+ return {
+ valid: false,
+ msg: "Please enter a valid positive number"
+ };
+ }
+
+ return {
+ valid: true,
+ msg: null
+ };
+ };
+
+ this.init();
+ }
+
+ /*
+ * An example of a "detached" editor.
+ * The UI is added onto document BODY and .position(), .show() and .hide() are implemented.
+ * KeyDown events are also handled to provide handling for Tab, Shift-Tab, Esc and Ctrl-Enter.
+ */
+ function LongTextEditor(args) {
+ var $input, $wrapper;
+ var defaultValue;
+ var scope = this;
+
+ this.init = function () {
+ var $container = $("body");
+
+ $wrapper = $("
")
+ .appendTo($container);
+
+ $input = $("