diff --git a/configuration/backup_full.sh b/configuration/backup_full.sh index afca4a1c32c..87b3aa26747 100755 --- a/configuration/backup_full.sh +++ b/configuration/backup_full.sh @@ -18,7 +18,7 @@ BACKUP_DIRECTORIES="/etc /var/log/mongodb" PREFIX="database" # Directory for temporary files -TARGET_DIR=/var/lib/mysql/backup +TARGET_DIR=/var/lib/mongodb/backup # Configuration for MongoDB backup MONGO_PORTS="10202 10201" @@ -26,7 +26,7 @@ MONGO_INSTALLATION=/opt/mongodb-linux-x86_64-2.6.7 MONGO_CMD=$MONGO_INSTALLATION/bin/mongo MONGOEXPORT_CMD=$MONGO_INSTALLATION/bin/mongoexport MONGODB_COLLECTION_WIND="WIND_TRACKS" -MONGODB_COLLECTION_LOCAL="local" +MONGODB_EXCLUDE_DBS="local 29erWorlds2016 29erWorlds2016-replica 49er-n17-euros2017 49er-n17-euros2017-replica 49erEuros2015 49erEuros2016 49erEuros2016-replica 49erJWC2015 49erWorlds_2016 49erWorlds_2016-replica 49erWorlds_2016-replica2 49erWorlds_2016-replica3 49erworlds2015 505-2017 505-2017-replica 505-2017-replica-1 505-2017-replica-2 505Worlds2016 505Worlds2016-replica 505_WORLDS_2015 6mworlds2017 6mworlds2017-replica 8MWORLDS2017 8MWORLDS2017-replica AustrianLeague2015 Bodenseewoche_2015 Championsleague_2015 Championsleague_2015-Replica DANISHLEAGUE2017 DANISHLEAGUE2017-replica DUTCH_LEAGUE_2017 DUTCH_LEAGUE_2017-replica DanishSailingLeague_2015 DutchSailingLeague_2015 FinnishSailingLeague_2015 GermanSailingLeague_2015 GermanSailingLeague_2015-Replica HANSESAIL2017 IDMFINN2017 KW2015 KW2015-replica KW2015-replica2 KW2017 KW2017-replica Norwegianleague_2015 POLISHLEAGUE2017 RosAtomCup_2015 SAPOPTICUP2015 SCL2017 SCL2017-replica SGCN_Smartphone_2016 SGCN_Smartphone_2016-replica SPEEDSAILING2016 SWC2016-Melbourne SWC2016-Melbourne-replica SWC2016-Qingdao SWC2016-Qingdao-replica SWC2017-Aarhus SWC2017-HYERES SWC2017-HYERES-replica SWC2017-Miami SWC2017-Miami-replica SWC2017-SANTANDER SWC2017-SANTANDER-replica SWEDISHLEAGUE2017 SWEDISHLEAGUE2017-replica TW2015 TW2015-REPLICA TW2015-test-replica TW2017 TW2017-replica WORLDCUPSYLT2015 YES2015 YES2015-REPLICA YES2015-REPLICA2 allsvenskan2015 austrianleague2016 austrianleague2017 bartsbash2015 bundesliga2-2017 bundesliga2-2017-replica bundesliga2016 bundesliga2016-replica bundesliga2016_2 bundesliga2017 bundesliga2017-replica crw2016 crw2016-replica danishleague2016 danishleague2016-replica dsl-pokal2017 dutchleague2016 dutchleague2016-replica ess2015 ess2016 ess2017 ess2017_foiling_test ess40-2015-replica2 finnishleague2016 finnishleague2017 forumvostok2016 frenchleague2017 germanleague2016-replica idmstar2016 isafwc2014 kielerwoche2016 kielerwoche2016-replica kiellauf2017 northsearegatta2017 northstreamrace2017 norwegianleague2016 norwegianleague2016-replica norwegianleague2017 norwegianleague2017-replica rsc2016 scl2016 scl2016-replica swedishleague2016 swedishleague2016-replica tw2016 tw2016-replica usoda2016 wmrt2016" # Configuration for MySQL MYSQL_DATABASES="bugs mysql" @@ -40,7 +40,8 @@ BACKUP_SERVER="backup@172.31.25.136" BACKUP_DATE=`date +%s` # Aliases -BUP_CMD="/opt/bup/bup" +BUP_DIR=/var/lib/mongodb/backup/bup +BUP_CMD="/opt/bup/bup -d $BUP_DIR" BUP_ADDITIONAL="-r $BACKUP_SERVER:/home/backup/$PREFIX" BUP_IGNORES='--exclude-rx=/war/$' BUP_CMD_INDEX="$BUP_CMD index $BUP_IGNORES" @@ -92,7 +93,14 @@ show collections" | $MONGO_CMD --port $MONGO_PORT | tail -n +4 | grep -v "^syste else DB_DIR=$TARGET_DIR/mongodb-$MONGO_PORT$WIND_SUFFIX fi - if [ "$i" != "$MONGODB_COLLECTION_LOCAL" -a \( "$i" != "$MONGODB_COLLECTION_WIND" -o "$PARAM" = "weekly" \) ]; then + exclude=0 + for exclude_collection in $MONGODB_EXCLUDE_DBS; do + if [ "$i" = "$exclude_collection" ]; then + echo "Excluding $i from backup based on exclude list" + exclude=1 + fi + done + if [ "$exclude" = "0" -a \( "$j" != "$MONGODB_COLLECTION_WIND" -o "$PARAM" = "weekly" \) ]; then echo " Backing up collection $j in MongoDB $i running on port $MONGO_PORT" echo "$MONGOEXPORT_CMD --port $MONGO_PORT -d $i -c $j > $DB_DIR/$j.json" mkdir -p $DB_DIR diff --git a/java/com.sap.sailing.dashboards.gwt/RibDashboard.html b/java/com.sap.sailing.dashboards.gwt/RibDashboard.html index 44d3171f5e4..3bea985d183 100644 --- a/java/com.sap.sailing.dashboards.gwt/RibDashboard.html +++ b/java/com.sap.sailing.dashboards.gwt/RibDashboard.html @@ -17,7 +17,7 @@ Dashboard - + diff --git a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.1.min.js b/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.1.min.js deleted file mode 100644 index d4d441a7956..00000000000 --- a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.1.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(a,b){function cg(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cd(a){if(!bZ[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bZ[a]=c}return bZ[a]}function cc(a,b){var c={};d.each(cb.concat.apply([],cb.slice(0,b)),function(){c[this]=a});return c}function bY(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bX(){try{return new a.XMLHttpRequest}catch(b){}}function bW(){d(a).unload(function(){for(var a in bU)bU[a](0,1)})}function bQ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(r,"`").replace(s,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,q=[],r=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function w(){return!0}function v(){return!1}function g(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.1",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1){var f=E.call(arguments,0),g=b,h=function(a){return function(b){f[a]=arguments.length>1?E.call(arguments,0):b,--g||c.resolveWith(e,f)}};while(b--)a=f[b],a&&d.isFunction(a.promise)?a.promise().then(h(b),c.reject):--g;g||c.resolveWith(e,f)}else c!==a&&c.resolve(a);return e},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="
a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e),b=e=f=null}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!g(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,h=b.nodeType,i=h?d.cache:b,j=h?b[d.expando]:d.expando;if(!i[j])return;if(c){var k=e?i[j][f]:i[j];if(k){delete k[c];if(!g(k))return}}if(e){delete i[j][f];if(!g(i[j]))return}var l=i[j][f];d.support.deleteExpando||i!=a?delete i[j]:i[j]=null,l?(i[j]={},h||(i[j].toJSON=d.noop),i[j][f]=l):h&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var k=i?f:0,l=i?f+1:h.length;k=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=k.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&l.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:m.test(a.nodeName)||n.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}h&&(a[c]=e);return a[c]}});var p=/\.(.*)$/,q=/^(?:textarea|input|select)$/i,r=/\./g,s=/ /g,t=/[^\w\s.|`]/g,u=function(a){return a.replace(t,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=v;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),u).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(p,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(q.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return q.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return q.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.getAttribute("type")},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}b&&(k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(d||!l.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}return k(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="
";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div
","
"]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){$(a,e),f=_(a),g=_(e);for(h=0;f[h];++h)$(f[h],g[h])}if(b){Z(a,e);if(c){f=_(a),g=_(e);for(h=0;f[h];++h)Z(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bb=/alpha\([^)]*\)/i,bc=/opacity=([^)]*)/,bd=/-([a-z])/ig,be=/([A-Z])/g,bf=/^-?\d+(?:px)?$/i,bg=/^-?\d/,bh={position:"absolute",visibility:"hidden",display:"block"},bi=["Left","Right"],bj=["Top","Bottom"],bk,bl,bm,bn=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bk(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bk)return bk(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bd,bn)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bo(a,b,e):d.swap(a,bh,function(){f=bo(a,b,e)});if(f<=0){f=bk(a,b,b),f==="0px"&&bm&&(f=bm(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bf.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bc.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bb.test(f)?f.replace(bb,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bl=function(a,c,e){var f,g,h;e=e.replace(be,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bm=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bf.test(d)&&bg.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bk=bl||bm,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bp=/%20/g,bq=/\[\]$/,br=/\r?\n/g,bs=/#.*$/,bt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bu=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bv=/(?:^file|^widget|\-extension):$/,bw=/^(?:GET|HEAD)$/,bx=/^\/\//,by=/\?/,bz=/)<[^<]*)*<\/script>/gi,bA=/^(?:select|textarea)/i,bB=/\s+/,bC=/([?&])_=[^&]*/,bD=/(^|\-)([a-z])/g,bE=function(a,b,c){return b+c.toUpperCase()},bF=/^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,bG=d.fn.load,bH={},bI={},bJ,bK;try{bJ=c.location.href}catch(bL){bJ=c.createElement("a"),bJ.href="",bJ=bJ.href}bK=bF.exec(bJ.toLowerCase()),d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bG)return bG.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
").append(c.replace(bz,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bA.test(this.nodeName)||bu.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(br,"\r\n")}}):{name:b.name,value:c.replace(br,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bJ,isLocal:bv.test(bK[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bM(bH),ajaxTransport:bM(bI),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bP(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bQ(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bD,bE)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bt.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bs,"").replace(bx,bK[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bB),e.crossDomain||(q=bF.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bK[1]||q[2]!=bK[2]||(q[3]||(q[1]==="http:"?80:443))!=(bK[3]||(bK[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bN(bH,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!bw.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(by.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bC,"$1_="+w);e.url=x+(x===e.url?(by.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in{success:1,error:1,complete:1})u[t](e[t]);o=bN(bI,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bO(g,a[g],c,f);return e.join("&").replace(bp,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bR=d.now(),bS=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bR++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bS.test(b.url)||f&&bS.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bS,l),b.url===j&&(f&&(k=k.replace(bS,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bT=d.now(),bU,bV;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bX()||bY()}:bX,bV=d.ajaxSettings.xhr(),d.support.ajax=!!bV,d.support.cors=bV&&"withCredentials"in bV,bV=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),(!a.crossDomain||a.hasContent)&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bU[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bU||(bU={},bW()),h=bT++,g.onreadystatechange=bU[h]=c):c()},abort:function(){c&&c(0,1)}}}});var bZ={},b$=/^(?:toggle|show|hide)$/,b_=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,ca,cb=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(cc("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:cc("show",1),slideUp:cc("hide",1),slideToggle:cc("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!ca&&(ca=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=cf.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!cf.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=cg(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=cg(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); \ No newline at end of file diff --git a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.2.min.js b/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.2.min.js deleted file mode 100644 index bdc47404369..00000000000 --- a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.5.2.min.js +++ /dev/null @@ -1 +0,0 @@ -(function(a,b){function ci(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cf(a){if(!b_[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";b_[a]=c}return b_[a]}function ce(a,b){var c={};d.each(cd.concat.apply([],cd.slice(0,b)),function(){c[this]=a});return c}function b$(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bZ(){try{return new a.XMLHttpRequest}catch(b){}}function bY(){d(a).unload(function(){for(var a in bW)bW[a](0,1)})}function bS(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function P(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function H(a,b){return(a&&a!=="*"?a+".":"")+b.replace(t,"`").replace(u,"&")}function G(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,p=[],q=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function E(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function y(){return!0}function x(){return!1}function i(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function h(a,c,e){if(e===b&&a.nodeType===1){e=a.getAttribute("data-"+c);if(typeof e==="string"){try{e=e==="true"?!0:e==="false"?!1:e==="null"?null:d.isNaN(e)?g.test(e)?d.parseJSON(e):e:parseFloat(e)}catch(f){}d.data(a,c,e)}else e=b}return e}var c=a.document,d=function(){function G(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(G,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x,y,z=Object.prototype.toString,A=Object.prototype.hasOwnProperty,B=Array.prototype.push,C=Array.prototype.slice,D=String.prototype.trim,E=Array.prototype.indexOf,F={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.2",length:0,size:function(){return this.length},toArray:function(){return C.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?B.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),x.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(C.apply(this,arguments),"slice",C.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:B,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;x.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=d._Deferred();if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",y,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",y),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&G()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):F[z.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!A.call(a,"constructor")&&!A.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||A.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1?f.call(arguments,0):c,--g||h.resolveWith(h,f.call(b,0))}}var b=arguments,c=0,e=b.length,g=e,h=e<=1&&a&&d.isFunction(a.promise)?a:d.Deferred();if(e>1){for(;c
a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0,reliableMarginRight:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e)}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(a.style.width="1px",a.style.marginRight="0",d.support.reliableMarginRight=(parseInt(c.defaultView.getComputedStyle(a,null).marginRight,10)||0)===0),b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function");return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}}();var g=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!i(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,g=b.nodeType,h=g?d.cache:b,j=g?b[d.expando]:d.expando;if(!h[j])return;if(c){var k=e?h[j][f]:h[j];if(k){delete k[c];if(!i(k))return}}if(e){delete h[j][f];if(!i(h[j]))return}var l=h[j][f];d.support.deleteExpando||h!=a?delete h[j]:h[j]=null,l?(h[j]={},g||(h[j].toJSON=d.noop),h[j][f]=l):g&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var f=this[0].attributes,g;for(var i=0,j=f.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var j=i?f:0,k=i?f+1:h.length;j=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=m.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&n.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var k=a.getAttributeNode("tabIndex");return k&&k.specified?k.value:o.test(a.nodeName)||p.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var l=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return l===null?b:l}h&&(a[c]=e);return a[c]}});var r=/\.(.*)$/,s=/^(?:textarea|input|select)$/i,t=/\./g,u=/ /g,v=/[^\w\s.|`]/g,w=function(a){return a.replace(v,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=x;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(a){return typeof d!=="undefined"&&d.event.triggered!==a.type?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=x);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),w).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(r,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=a.type,l[m]())}catch(p){}k&&(l["on"+m]=k),d.event.triggered=b}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},D=function D(a){var c=a.target,e,f;if(s.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=C(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:D,beforedeactivate:D,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&D.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&D.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",C(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in B)d.event.add(this,c+".specialChange",B[c]);return s.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return s.test(this.nodeName)}},B=d.event.special.change.filters,B.focus=B.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function f(a){var c=d.event.fix(a);c.type=b,c.originalEvent={},d.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var e=0;d.event.special[b]={setup:function(){e++===0&&c.addEventListener(a,f,!0)},teardown:function(){--e===0&&c.removeEventListener(a,f,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return"text"===c&&(b===c||b===null)},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=N.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(P(c[0])||P(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=M.call(arguments);I.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!O[a]?d.unique(f):f,(this.length>1||K.test(e))&&J.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var R=/ jQuery\d+="(?:\d+|null)"/g,S=/^\s+/,T=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,U=/<([\w:]+)/,V=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};Z.optgroup=Z.option,Z.tbody=Z.tfoot=Z.colgroup=Z.caption=Z.thead,Z.th=Z.td,d.support.htmlSerialize||(Z._default=[1,"div
","
"]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(R,""):null;if(typeof a!=="string"||X.test(a)||!d.support.leadingWhitespace&&S.test(a)||Z[(U.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(T,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){ba(a,e),f=bb(a),g=bb(e);for(h=0;f[h];++h)ba(f[h],g[h])}if(b){_(a,e);if(c){f=bb(a),g=bb(e);for(h=0;f[h];++h)_(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||W.test(i)){if(typeof i==="string"){i=i.replace(T,"<$1>");var j=(U.exec(i)||["",""])[1].toLowerCase(),k=Z[j]||Z._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=V.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&S.test(i)&&m.insertBefore(b.createTextNode(S.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bd=/alpha\([^)]*\)/i,be=/opacity=([^)]*)/,bf=/-([a-z])/ig,bg=/([A-Z]|^ms)/g,bh=/^-?\d+(?:px)?$/i,bi=/^-?\d/,bj={position:"absolute",visibility:"hidden",display:"block"},bk=["Left","Right"],bl=["Top","Bottom"],bm,bn,bo,bp=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bm(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bm)return bm(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bf,bp)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bq(a,b,e):d.swap(a,bj,function(){f=bq(a,b,e)});if(f<=0){f=bm(a,b,b),f==="0px"&&bo&&(f=bo(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bh.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return be.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bd.test(f)?f.replace(bd,e):c.filter+" "+e}}),d(function(){d.support.reliableMarginRight||(d.cssHooks.marginRight={get:function(a,b){var c;d.swap(a,{display:"inline-block"},function(){b?c=bm(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bn=function(a,c,e){var f,g,h;e=e.replace(bg,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bo=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bh.test(d)&&bi.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bm=bn||bo,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var br=/%20/g,bs=/\[\]$/,bt=/\r?\n/g,bu=/#.*$/,bv=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bw=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bx=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,by=/^(?:GET|HEAD)$/,bz=/^\/\//,bA=/\?/,bB=/)<[^<]*)*<\/script>/gi,bC=/^(?:select|textarea)/i,bD=/\s+/,bE=/([?&])_=[^&]*/,bF=/(^|\-)([a-z])/g,bG=function(a,b,c){return b+c.toUpperCase()},bH=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bI=d.fn.load,bJ={},bK={},bL,bM;try{bL=c.location.href}catch(bN){bL=c.createElement("a"),bL.href="",bL=bL.href}bM=bH.exec(bL.toLowerCase())||[],d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bI)return bI.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
").append(c.replace(bB,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bC.test(this.nodeName)||bw.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(bt,"\r\n")}}):{name:b.name,value:c.replace(bt,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bL,isLocal:bx.test(bM[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bO(bJ),ajaxTransport:bO(bK),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bR(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bS(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bF,bG)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bv.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bu,"").replace(bz,bM[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bD),e.crossDomain==null&&(q=bH.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bM[1]||q[2]!=bM[2]||(q[3]||(q[1]==="http:"?80:443))!=(bM[3]||(bM[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bP(bJ,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!by.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(bA.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bE,"$1_="+w);e.url=x+(x===e.url?(bA.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in{success:1,error:1,complete:1})u[t](e[t]);o=bP(bK,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bQ(g,a[g],c,f);return e.join("&").replace(br,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bT=d.now(),bU=/(\=)\?(&|$)|\?\?/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bT++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bU.test(b.url)||f&&bU.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bU,l),b.url===j&&(f&&(k=k.replace(bU,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bV=d.now(),bW,bX;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bZ()||b$()}:bZ,bX=d.ajaxSettings.xhr(),d.support.ajax=!!bX,d.support.cors=bX&&"withCredentials"in bX,bX=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),!a.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bW[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bW||(bW={},bY()),h=bV++,g.onreadystatechange=bW[h]=c):c()},abort:function(){c&&c(0,1)}}}});var b_={},ca=/^(?:toggle|show|hide)$/,cb=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cc,cd=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(ce("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:ce("show",1),slideUp:ce("hide",1),slideToggle:ce("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!cc&&(cc=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=(e==="absolute"||e==="fixed")&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=ch.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!ch.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=ci(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=ci(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); \ No newline at end of file diff --git a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.7.1.min.js b/java/com.sap.sailing.dashboards.gwt/js/jquery-1.7.1.min.js deleted file mode 100644 index ced5cbdf977..00000000000 --- a/java/com.sap.sailing.dashboards.gwt/js/jquery-1.7.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!ck[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){cl||(cl=c.createElement("iframe"),cl.frameBorder=cl.width=cl.height=0),b.appendChild(cl);if(!cm||!cl.createElement)cm=(cl.contentWindow||cl.contentDocument).document,cm.write((c.compatMode==="CSS1Compat"?"":"")+""),cm.close();d=cm.createElement(a),cm.body.appendChild(d),e=f.css(d,"display"),b.removeChild(cl)}ck[a]=e}return ck[a]}function cu(a,b){var c={};f.each(cq.concat.apply([],cq.slice(0,b)),function(){c[this]=a});return c}function ct(){cr=b}function cs(){setTimeout(ct,0);return cr=f.now()}function cj(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ci(){try{return new a.XMLHttpRequest}catch(b){}}function cc(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;g=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?parseFloat(d):j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.1",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=q.getElementsByTagName("*"),e=q.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=q.getElementsByTagName("input")[0],b={leadingWhitespace:q.firstChild.nodeType===3,tbody:!q.getElementsByTagName("tbody").length,htmlSerialize:!!q.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:q.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete q.test}catch(s){b.deleteExpando=!1}!q.addEventListener&&q.attachEvent&&q.fireEvent&&(q.attachEvent("onclick",function(){b.noCloneEvent=!1}),q.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),q.appendChild(i),k=c.createDocumentFragment(),k.appendChild(q.lastChild),b.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,k.removeChild(i),k.appendChild(q),q.innerHTML="",a.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",q.style.width="2px",q.appendChild(j),b.reliableMarginRight=(parseInt((a.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0);if(q.attachEvent)for(o in{submit:1,change:1,focusin:1})n="on"+o,p=n in q,p||(q.setAttribute(n,"return;"),p=typeof q[n]=="function"),b[o+"Bubbles"]=p;k.removeChild(q),k=g=h=j=q=i=null,f(function(){var a,d,e,g,h,i,j,k,m,n,o,r=c.getElementsByTagName("body")[0];!r||(j=1,k="position:absolute;top:0;left:0;width:1px;height:1px;margin:0;",m="visibility:hidden;border:0;",n="style='"+k+"border:5px solid #000;padding:0;'",o="
"+""+"
",a=c.createElement("div"),a.style.cssText=m+"width:0;height:0;position:static;top:0;margin-top:"+j+"px",r.insertBefore(a,r.firstChild),q=c.createElement("div"),a.appendChild(q),q.innerHTML="
t
",l=q.getElementsByTagName("td"),p=l[0].offsetHeight===0,l[0].style.display="",l[1].style.display="none",b.reliableHiddenOffsets=p&&l[0].offsetHeight===0,q.innerHTML="",q.style.width=q.style.paddingLeft="1px",f.boxModel=b.boxModel=q.offsetWidth===2,typeof q.style.zoom!="undefined"&&(q.style.display="inline",q.style.zoom=1,b.inlineBlockNeedsLayout=q.offsetWidth===2,q.style.display="",q.innerHTML="
",b.shrinkWrapBlocks=q.offsetWidth!==2),q.style.cssText=k+m,q.innerHTML=o,d=q.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,i={doesNotAddBorder:e.offsetTop!==5,doesAddBorderForTableAndCells:h.offsetTop===5},e.style.position="fixed",e.style.top="20px",i.fixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",i.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,i.doesNotIncludeMarginInBodyOffset=r.offsetTop!==j,r.removeChild(a),q=a=null,f.extend(b,i))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.nodeName.toLowerCase()]||f.valHooks[g.type];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;h=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/\bhover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&i.push({elem:this,matches:d.slice(e)});for(j=0;j0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function() -{for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||!bc.test("<"+a.nodeName)?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!_.test(k))k=b.createTextNode(k);else{k=k.replace(Y,"<$1>");var l=(Z.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");b===c?bh.appendChild(o):U(b).appendChild(o),o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=$.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&X.test(k)&&o.insertBefore(b.createTextNode(X.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof(r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return br.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bq,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bq.test(g)?g.replace(bq,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,b){var c,d,e;b=b.replace(bs,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b)));return c}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f===null&&g&&(e=g[b])&&(f=e),!bt.test(f)&&bu.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f||0,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bD=/%20/g,bE=/\[\]$/,bF=/\r?\n/g,bG=/#.*$/,bH=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bI=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bJ=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bK=/^(?:GET|HEAD)$/,bL=/^\/\//,bM=/\?/,bN=/)<[^<]*)*<\/script>/gi,bO=/^(?:select|textarea)/i,bP=/\s+/,bQ=/([?&])_=[^&]*/,bR=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bS=f.fn.load,bT={},bU={},bV,bW,bX=["*/"]+["*"];try{bV=e.href}catch(bY){bV=c.createElement("a"),bV.href="",bV=bV.href}bW=bR.exec(bV.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bS)return bS.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bN,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bO.test(this.nodeName)||bI.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bF,"\r\n")}}):{name:b.name,value:c.replace(bF,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b_(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b_(a,b);return a},ajaxSettings:{url:bV,isLocal:bJ.test(bW[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bX},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bZ(bT),ajaxTransport:bZ(bU),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?cb(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cc(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bH.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bG,"").replace(bL,bW[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bP),d.crossDomain==null&&(r=bR.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bW[1]&&r[2]==bW[2]&&(r[3]||(r[1]==="http:"?80:443))==(bW[3]||(bW[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bT,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bK.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bM.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bQ,"$1_="+x);d.url=y+(y===d.url?(bM.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bX+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bU,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)ca(g,a[g],c,e);return d.join("&").replace(bD,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cd=f.now(),ce=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cd++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ce.test(b.url)||e&&ce.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ce,l),b.url===j&&(e&&(k=k.replace(ce,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cf=a.ActiveXObject?function(){for(var a in ch)ch[a](0,1)}:!1,cg=0,ch;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ci()||cj()}:ci,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cf&&delete ch[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cg,cf&&(ch||(ch={},f(a).unload(cf)),ch[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var ck={},cl,cm,cn=/^(?:toggle|show|hide)$/,co=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cp,cq=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cr;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,d,"padding")):this[d]():null},f.fn["outer"+c]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,d,a?"margin":"border")):this[d]():null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c],h=e.document.body;return e.document.compatMode==="CSS1Compat"&&g||h&&h["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var i=f.css(e,d),j=parseFloat(i);return f.isNumeric(j)?j:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/java/com.sap.sailing.dashboards.gwt/js/jquery-3.3.1.min.js b/java/com.sap.sailing.dashboards.gwt/js/jquery-3.3.1.min.js new file mode 100644 index 00000000000..4d9b3a25875 --- /dev/null +++ b/java/com.sap.sailing.dashboards.gwt/js/jquery-3.3.1.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + diff --git a/java/com.sap.sailing.gwt.ui/AutoPlay.html b/java/com.sap.sailing.gwt.ui/AutoPlay.html index 66bf31d3024..911b2e94ae1 100644 --- a/java/com.sap.sailing.gwt.ui/AutoPlay.html +++ b/java/com.sap.sailing.gwt.ui/AutoPlay.html @@ -41,7 +41,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/DataMining.css b/java/com.sap.sailing.gwt.ui/DataMining.css index 92ca1290f69..9ef6e46c92d 100644 --- a/java/com.sap.sailing.gwt.ui/DataMining.css +++ b/java/com.sap.sailing.gwt.ui/DataMining.css @@ -160,7 +160,7 @@ input.magnifier { font-size: 20px; height: 70px; margin: 0 auto; - padding: 49px 0 0 80px; + padding: 25px 0 0 80px; width: 400px; color: #7c7c7c; background: url(images/important-message.png) left center no-repeat; @@ -172,7 +172,7 @@ input.magnifier { font-size: 20px; height: 70px; margin: 0 auto; - padding: 49px 0 0 40px; + padding: 25px 0 0 40px; width: 400px; color: #7c7c7c; background: url(images/busy_indicator_circle.gif) left center no-repeat; @@ -211,13 +211,23 @@ input.magnifier { border-bottom: 1px solid rgba(0, 0, 0, 0.25); } +.definitionProviderControlsElements { + margin: 2px; +} + .settingsAnchor { position: relative; - top: 9px; + top: 7.5px; } .groupingProvider { padding: 3px 0px; + overflow-y: auto; +} + +.groupingProviderLabel{ + margin: 2px 2px 2px 0px; + display: inline-block; } .groupingProviderElement { @@ -226,32 +236,82 @@ input.magnifier { } .statisticProvider { - padding: 5px 0px 0px 0px; + padding: 5px 0px; + display: table; +} + +.statisticProviderLabel { + display: table-cell; + white-space: nowrap; + padding: 0px 3px; } .statisticProviderElement { - margin: 2px; - display: inline-block; + display: table-cell; + width: auto; +} + +.unsupportedAggregatorListItem { + color: gray; +} + +.statisticSuggestBoxContainer { + width: 100%; + min-width: 300px; } .statisticSuggestBoxPopup { z-index: 2; } -.statisticSuggestBoxPopup .suggestPopupContent { + +.statisticSuggestBoxPopupContent { max-height: 700px; - overflow-y: scroll; + overflow-y: auto; + background: white; +} +.statisticSuggestBoxPopupContent table { + border-spacing: 0px; } -.definitionProviderControls { - margin: 2px; +.statisticSuggestBoxPopupContent .item { + padding: 2px 5px; + color: #000; + cursor: default; + white-space: nowrap; + font-size: 15px; +} +.statisticSuggestBoxPopupContent .item-even { + background: #fff; +} +.statisticSuggestBoxPopupContent .item-odd { + background: #f3f7fb; +} +.statisticSuggestBoxPopupContent .item-selected { + background: #d5e2ff; +} +.statisticSuggestBoxPopupContent .item-unsupported { + color: gray; } -.definitionProviderControlsElements { - margin: 2px; +.statisticSuggestBoxPopupContent .separator { + padding: 8px 5px 0px 5px; + border-bottom: 2px solid #6f7277; + font-weight: 600; + font-size: 15px; +} +.statisticSuggestBoxPopupContent .separator-message { + padding: 8px 5px 0px 5px; + font-weight: bold; + font-size: 15px; + text-align: center; + white-space: pre-wrap; +} +.statisticSuggestBoxPopupContent .separator-unsupported { + color: gray; } -.queryProviderElementLabel { - margin-left: 0px; +.dataMiningListBox { + min-width: 120px; } .queryDefinitionViewerContent { @@ -261,6 +321,7 @@ input.magnifier { .filterDimensionsTitleLabel { padding-top: 5px; + display: inline-block; } .filterFilterDimensionsPanel { @@ -276,9 +337,12 @@ input.magnifier { padding-top: 5px; } -.dimensionFilterSelectionHeader .gwt-Label { - color: #4b4a4a; - text-shadow: #ddf 1px 1px 0; +.filterSelectionPresenter { + margin: 5px; +} +.filterSelectionPresenter .dimensionLabel { + margin-right: 2px; + text-shadow: 0 0 1px #888; } .dataMiningBorderTop { @@ -288,7 +352,7 @@ input.magnifier { border-left: 2px solid #6f7277; } .dataMiningBorderBottom { - border-border: 2px solid #6f7277; + border-bottom: 2px solid #6f7277; } .dataMiningBorderRight { border-right: 2px solid #6f7277; @@ -323,9 +387,15 @@ input.magnifier { .dataMiningMarginLeft { margin-left: 5px; } +.dataMiningMarginTop { + margin-top: 5px; +} .dataMiningMarginRight { margin-right: 5px; } +.dataMiningMarginBottom { + margin-bottom: 5px; +} .resultsPresenterTabHeader * { display: inline-block; @@ -335,4 +405,27 @@ input.magnifier { .emphasizedLabel { font-weight: 600; +} + +.floatRight { + float: right; +} + +.whiteGlass { + background-color: #fff; + opacity: 0.6; + z-index: 19; +} + +.applyQueryBusyMessage { + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; + font-size: 20px; + height: 70px; + margin: auto auto; + padding: 25px 0 0 40px; + width: 200px; + color: #7c7c7c; + background: url(images/busy_indicator_circle.gif) left center no-repeat; + z-index: 20; } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/DataMining.html b/java/com.sap.sailing.gwt.ui/DataMining.html index 9f93882c9d2..46a0764ba3a 100644 --- a/java/com.sap.sailing.gwt.ui/DataMining.html +++ b/java/com.sap.sailing.gwt.ui/DataMining.html @@ -17,7 +17,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html index 7d41e6e92cc..2fad77c4f0d 100644 --- a/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html +++ b/java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.html @@ -20,7 +20,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/Home.html b/java/com.sap.sailing.gwt.ui/Home.html index 7682288bf02..47a0eaede71 100644 --- a/java/com.sap.sailing.gwt.ui/Home.html +++ b/java/com.sap.sailing.gwt.ui/Home.html @@ -12,7 +12,7 @@ href="/sailing-normalize-3.0.2.cache.css"> - + diff --git a/java/com.sap.sailing.gwt.ui/Leaderboard.html b/java/com.sap.sailing.gwt.ui/Leaderboard.html index 39007c2c267..c3cd86f5304 100755 --- a/java/com.sap.sailing.gwt.ui/Leaderboard.html +++ b/java/com.sap.sailing.gwt.ui/Leaderboard.html @@ -18,7 +18,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/PairingList.html b/java/com.sap.sailing.gwt.ui/PairingList.html index 88c34a0df56..ba01a9bf78a 100644 --- a/java/com.sap.sailing.gwt.ui/PairingList.html +++ b/java/com.sap.sailing.gwt.ui/PairingList.html @@ -18,7 +18,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/RaceBoard.html b/java/com.sap.sailing.gwt.ui/RaceBoard.html index 2d27aa88a80..2d5cf793546 100644 --- a/java/com.sap.sailing.gwt.ui/RaceBoard.html +++ b/java/com.sap.sailing.gwt.ui/RaceBoard.html @@ -20,7 +20,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/Simulator.html b/java/com.sap.sailing.gwt.ui/Simulator.html index 885b59b88fa..88e02232e23 100644 --- a/java/com.sap.sailing.gwt.ui/Simulator.html +++ b/java/com.sap.sailing.gwt.ui/Simulator.html @@ -18,7 +18,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/imprint.json b/java/com.sap.sailing.gwt.ui/imprint.json index 35a4a619717..97ff0810937 100644 --- a/java/com.sap.sailing.gwt.ui/imprint.json +++ b/java/com.sap.sailing.gwt.ui/imprint.json @@ -563,6 +563,16 @@ "Copyright 2011, John Resig" ], "key": "MITL" + }, + { + "name": "JQuery", + "version": "3.3.1", + "owner": "jQuery Foundation and other contributors", + "homepage": "http://jquery.com/", + "acknowledgements": [ + "Copyright 2018 jQuery Foundation and other contributors" + ], + "key": "MITL" }, { "name": "json-simple", @@ -731,7 +741,7 @@ "owner": "\"Mr.doob\"", "homepage": "https://threejs.org/", "acknowledgements": [ - "Copyright © 2010-2017 three.js authors" + "Copyright (c) 2010-2017 three.js authors" ], "key": "MIT" }, diff --git a/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js b/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js deleted file mode 100644 index ab28a24729b..00000000000 --- a/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; -if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m(" - - \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway/utils/tvScreenWithTwoIFrames.html b/java/com.sap.sailing.server.gateway/utils/tvScreenWithTwoIFrames.html deleted file mode 100755 index 5c774e0aa37..00000000000 --- a/java/com.sap.sailing.server.gateway/utils/tvScreenWithTwoIFrames.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - URL 1: 
- URL 2: 
- Position:  Left: Top:
- Size:  Width: Height:
- - - - - - - -
X
- - - - \ No newline at end of file diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/addCompetitorToRace.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/addCompetitorToRace.html new file mode 100644 index 00000000000..19b2fd86d0a --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/addCompetitorToRace.html @@ -0,0 +1,52 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/mark/addCompetitorToRace

+ +Description: +

Updates the competitor registration, note that requires to be passed the whole set every time it is called, +and will perform a delta to the stored state. This will also allow to remove competitors

+
+
+ + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Request method:POST
Examples: + Request: { + "leaderboardName":"test", + "raceColumnName":"R1", + "fleetName":"Default", + "competitors":[ + { + "competitorId":"b9a8a600-c829-0133-a4ec-448a5b8481d8", + "boatId":"c4d04422-19fd-49ec-b1bb-d3871a72527a" + } + ] + } + Response: Status 200 +
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/addCourseDefinitionToRaceLog.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/addCourseDefinitionToRaceLog.html new file mode 100644 index 00000000000..e0aa2aa08d1 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/addCourseDefinitionToRaceLog.html @@ -0,0 +1,83 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/mark/addCourseDefinitionToRaceLog

+ +Description: +

Allows to create a Coursedefinition that is then inserted into the racelog

+
+ + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Request method:POST
Examples: + Request: { + "fleetName":"Default", + "leaderboardName":"test", + "raceColumnName":"R1", + "controlPoints": [ + { + "passingInstruction":"Gate", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab", "305ffb4b-c23d-42a1-be45-123123123123"] + }, + { + "passingInstruction":"Port", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab"] + }, + { + "passingInstruction":"Starboard", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab"] + }, + { + "passingInstruction":"Line", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab", "305ffb4b-c23d-42a1-be45-123123123123"] + }, + { + "passingInstruction":"Offset", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab", "305ffb4b-c23d-42a1-be45-123123123123"] + }, + { + "passingInstruction":"FixedBearing", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab"] + }, + { + "passingInstruction":"Single_Unknown", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab"] + }, + { + "passingInstruction":"None", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab"] + }, + { + "passingInstruction":"None", + "marks":["305ffb4b-c23d-42a1-be45-f76a2bad8fab", "305ffb4b-c23d-42a1-be45-123123123123"] + } + ] + } + Response: Status 200 if changed +
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkFix.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkFix.html new file mode 100644 index 00000000000..21f6db212d9 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkFix.html @@ -0,0 +1,49 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/mark/addMarkFix

+ +Description: +

Adds a fix to a specified Mark, the Marks uuid must be known

+
+ + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Request method:POST
Examples: + Request: { + "markId":"305ffb4b-c23d-42a1-be45-f76a2bad8fab", + "lonDeg":"1", + "latDeg":"1", + "timeMillis":"0", + "fleetName":"Default", + "leaderboardName":"test", + "raceColumnName":"R1" + } + Response: Status 200 if changed +
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkToRegatta.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkToRegatta.html new file mode 100644 index 00000000000..e5cd274dc50 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/addMarkToRegatta.html @@ -0,0 +1,46 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/mark/addMarkToRegatta

+ +Description: +

Adds a Mark to a specified Regatta

+
+ + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Request method:POST
Examples: + Request: { + "markName":"MyMark", + "regattaName":"test" + } + Response: { + "markId":uuidOfGeneratedMark + } +
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/eventCreateEvent.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/eventCreateEvent.html index 0f9317d5776..f2ed5192668 100755 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/eventCreateEvent.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/eventCreateEvent.html @@ -38,7 +38,7 @@ consumption of this service simple.

Optional parameters: -
eventname name of the event; defaults to a name constructed from the user name +
eventName name of the event; defaults to a name constructed from the user name and a timestamp
eventdescription the event description text; defaults to the event name
startdate the event start date in ISO format (e.g., 2017-08-17T10:00:00+0200)
@@ -58,6 +58,7 @@ consumption of this service simple.

boatclassname must be provided if createretatta=true; can be the display name or an alias name of a boat class to use for the new regatta
numberofraces tells how many races shall be created in the default series; by default the series remains empty
+
canBoatsOfCompetitorsChangePerRace If true, boats and competitors can change per race, also it is necessary to register them in this case for each race!
diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html index 2b8218015f4..350f8e056d6 100644 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/index.html @@ -133,6 +133,10 @@ name to make sure you get to the event you're interested in. /api/v1/regattas/{regattaname}/competitors Obtains the competitors participating in a regatta + + /api/v1/regattas/updateOrCreateSeries + Creates a Series in an regatta, and configures the Series it contains + /api/v1/regattas/{regattaname}/competitors/createandadd Creates a competitor and adds it to the regatta @@ -201,6 +205,14 @@ name to make sure you get to the event you're interested in. /api/v1/leaderboards/{name}/stoptracking Stops tracking for a race + + /api/v1/leaderboards/{name}/denoteForTracking + Denotes a race for tracking + + + /api/v1/leaderboards/{name}/leaderboardRaceEnableRaceLogForCompetitorRegistration + Enables racelog useage for competitor registration + /api/v1/leaderboards/{name}/marks/{id}/gps_fixes Pings the location of a mark via the RegattaLog @@ -254,6 +266,26 @@ name to make sure you get to the event you're interested in. /api/v1/wind/putWind Allows to add wind values to 1-x races, similar to AdminConsole + + + /api/v1/mark/addCompetitorToRace + Updates the competitor registrations + + + + /api/v1/mark/addCourseDefinitionToRaceLog + Allows to create a Coursedefinition that is then inserted into the racelog + + + + /api/v1/mark/addMarkFix + Adds a fix to a specified Mark, the Marks uuid must be known + + + + /api/v1/mark/addMarkToRegatta + Adds a Mark to a specified Regatta +
diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceDenoteForTracking.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceDenoteForTracking.html new file mode 100644 index 00000000000..06cbaf4e9f3 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceDenoteForTracking.html @@ -0,0 +1,49 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/leaderboards/{leaderboardName}/denoteForTracking

+ +Description: +

This service will denote a race for tracking.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:StatusCode
Mandatory parameters:raceColumnName
+ fleetName
+ raceName
+
Optional parameters:None
Request method:POST
Example:http://www.sapsailing.com/sailingserver/api/v1/leaderboards/test/denoteForTracking?raceColumnName=R1&fleetName=Default&raceName=R1
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceEnableRaceLogForCompetitorRegistration.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceEnableRaceLogForCompetitorRegistration.html new file mode 100644 index 00000000000..4addaa6b652 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/leaderboardRaceEnableRaceLogForCompetitorRegistration.html @@ -0,0 +1,49 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/leaderboards/{leaderboardName}/enableRaceLogForCompetitorRegistration

+ +Description: +

This service will set a race to use racelog based competitor registrations

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:StatusCode
Mandatory parameters:raceColumnName
+ fleetName
+ raceName
+
Optional parameters:None
Request method:POST
Example:https://sapsailing.com/sailingserver/api/v1/leaderboards/test/enableRaceLogForCompetitorRegistration?raceColumnName=R2&fleetName=Default&raceName=R2
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html new file mode 100644 index 00000000000..d828571628c --- /dev/null +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattaUpdateOrCreateSeries.html @@ -0,0 +1,61 @@ + + + + + + + + SAP Sailing Analytics Webservices API Version 1.0 + + +

SAP Sailing Analytics Webservices API Version 1.0

+

URL: /api/v1/regattas/updateOrCreateSeries

+ +Description: +

Creates a Series in an regatta, and configures the Series it contains

+
+ + + + + + + + + + + + + + + + + +
Webservice Type:REST
Output format:Json
Request method:POST
Examples: + resultDiscardingThresholds and maximumNumberOfDiscards are optional + + Request: { + "regattaName":"test", + "seriesName":"NewRestCreatedSeries2", + "seriesNameNew":"NewRestCreatedSeries2", + "isMedal":false, + "isFleetsCanRunInParallel":false, + "startsWithZeroScore":true, + "firstColumnIsNonDiscardableCarryForward":false, + "hasSplitFleetContiguousScoring":false, + "maximumNumberOfDiscards":1, + "resultDiscardingThresholds":[1,2], + "fleets":[ + { + "fleetName":"Gold 1", + "orderNo":1, + "htmlColor":"#FF0000" + } + ] + } + Response: Status 200 +
+
+Back to Web Service Overview + + diff --git a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasCompetitorsCreateAndAdd.html b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasCompetitorsCreateAndAdd.html index d98ee361996..4915138a711 100755 --- a/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasCompetitorsCreateAndAdd.html +++ b/java/com.sap.sailing.server.gateway/webservices/api/v1/regattasCompetitorsCreateAndAdd.html @@ -1,11 +1,11 @@ - - - - - SAP Sailing Analytics Webservices API Version 1.0 + + + + + SAP Sailing Analytics Webservices API Version 1.0

SAP Sailing Analytics Webservices API Version 1.0

@@ -17,36 +17,38 @@ parameter; defaults will be obtained from the authenticated user invoking the se All slashes contained within the {regattaname} parameter must be replaced by "__" (double underscore).


- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
Webservice Type:POST
Output format:The competitor as a JSON document
Mandatory parameters:boatclass the name of the competitor's boat class
Optional parameters: -
sailid the sail number to use
-
nationalityIOC three-letter IOC country code
-
timeontimefactor a double value telling the ToT / TMF / time-on-time factor for handicap racing
-
timeondistanceallowancepernauticalmileasmillis distance-based allowance in milliseconds for handicap racing (ToD)
-
searchtag a search tag for the competitor
-
Request method:POST
Example:curl -X POST -H "Authorization: Bearer KMTuXevrA0CYyM6O1W2E0QB9gVeP8tIpgjafJOvj/6A=" "http://127.0.0.1:8888/sailingserver/api/v1/regattas/Smartphone%20Tracking%20Test%2022.06/competitors/createandadd?boatclass=49er&sailid=GER+123&nationalityIOC=GER"
Webservice Type:POST
Output format:The competitor as a JSON document
Mandatory parameters:boatclass the name of the competitor's boat class
Optional parameters: +
sailid the sail number to use
+
nationalityIOC three-letter IOC country code
+
timeontimefactor a double value telling the ToT / TMF / time-on-time factor for handicap racing
+
timeondistanceallowancepernauticalmileasmillis distance-based allowance in milliseconds for handicap racing (ToD)
+
searchtag a search tag for the competitor
+
competitorName the name to use for the newly created/added competitor
+
competitorEmail the email to use for the newly created/added competitor
+
Request method:POST
Example:curl -X POST -H "Authorization: Bearer KMTuXevrA0CYyM6O1W2E0QB9gVeP8tIpgjafJOvj/6A=" "http://127.0.0.1:8888/sailingserver/api/v1/regattas/Smartphone%20Tracking%20Test%2022.06/competitors/createandadd?boatclass=49er&sailid=GER+123&nationalityIOC=GER"
Back to Web Service Overview diff --git a/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ColorMapperTest.java b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ColorMapperTest.java new file mode 100755 index 00000000000..57b32c76382 --- /dev/null +++ b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ColorMapperTest.java @@ -0,0 +1,78 @@ +package com.sap.sse.common.test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sse.common.ColorMapper; +import com.sap.sse.common.ColorMapperChangedListener; +import com.sap.sse.common.ValueRangeFlexibleBoundaries; + +public class ColorMapperTest { + private static final double EPSILON = 0.00000001; + ColorMapper colorMapper; + ColorMapperChangedListener listener; + ValueRangeFlexibleBoundaries valueRange; + Set valueSet; + Set colorSet; + + @Before + public void setUp() { + valueRange = new ValueRangeFlexibleBoundaries(-5.0, 5.0, 0.0); + colorMapper = new ColorMapper(valueRange, false); + listener = mock(ColorMapperChangedListener.class); + colorMapper.addListener(listener); + valueSet = new HashSet<>(); + updateValueSet(valueSet); + colorSet = new HashSet<>(); + fillColorSet(); + + } + + public void updateValueSet(Set valueSet) { + valueSet.clear(); + for (int i = (int) Math.round(valueRange.getMinLeft()); i <= (int) Math.round(valueRange.getMaxRight()); i++) { + valueSet.add(i); + } + } + + public void fillColorSet() { + for (int i = 0; i <= 240; i++) { + colorSet.add("hsl(" + i + ", 100%, 50%)"); + } + } + + @Test + public void testForValueOutOfBoundaries() { + assertTrue(colorMapper.getColor(valueRange.getMinLeft() - EPSILON) == "hsl(0, 100%, 50%)"); + assertTrue(colorMapper.getColor(valueRange.getMaxRight() + EPSILON) == "hsl(240, 100%, 50%)"); + } + + @Test + public void testGetColorColored() { + // first set the color and verify listener will be notified + colorMapper.setGrey(false); + verify(listener, times(1)).onColorMappingChanged(); + // Check for each value in valueSet that getColor() returns a color within the colorSet. + for (int i : valueSet) { + assertTrue(colorSet.contains(colorMapper.getColor(i))); + } + } + + @Test + public void changeValueRangeAndTestGetColorColored() { + valueRange.setMinMax(12.0, 26.0); + // verify Listener + verify(listener, times(1)).onColorMappingChanged(); + updateValueSet(valueSet); + // check if new values are within the colorSet + for (int i : valueSet) { + assertTrue(colorSet.contains(colorMapper.getColor(i))); + } + } +} diff --git a/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ValueRangeFlexibleBoundariesTest.java b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ValueRangeFlexibleBoundariesTest.java new file mode 100755 index 00000000000..f68eff1d443 --- /dev/null +++ b/java/com.sap.sse.common.test/src/com/sap/sse/common/test/ValueRangeFlexibleBoundariesTest.java @@ -0,0 +1,98 @@ +package com.sap.sse.common.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.*; + +import org.junit.Before; +import org.junit.Test; + +import com.sap.sse.common.ValueRangeFlexibleBoundaries; +import com.sap.sse.common.ValueRangeFlexibleBoundariesChangedListener; + +public class ValueRangeFlexibleBoundariesTest { + private static final double EPSILON = 0.00000001; + //Create new valueRange, boundaries should be [-6,-4] and [4,6]. + private ValueRangeFlexibleBoundaries valueRange; + private double minLeft; + private double maxRight; + private ValueRangeFlexibleBoundariesChangedListener listener; + + @Before + public void setUp() { + // a ten percent range at each end of an original range of length 10, so length 1 at each end, + // spread symmetrically around the min/max value + valueRange = new ValueRangeFlexibleBoundaries(-5.0, 5.0, 0.10); + minLeft = -6.0; + maxRight = 6.0; + listener = mock(ValueRangeFlexibleBoundariesChangedListener.class); + valueRange.addListener(listener); + verify(listener, times(0)).onValueRangeBoundariesChanged(); + } + + @Test + public void testExceptionForGreaterMinThanMax() { + try { + valueRange.setMinMax(6, 5); + fail("Excpected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testCheckIfValueIsInLeftBoundaryRangeAndUpdateIfNecessary() { + // update left border to 0.0, that means boundaries now should be [-0.5,0.5] and [4.5,5.5] + // because the new range has length 5, so 10% of that is .5, so the left range is -0.5..0.5 + // and the right range is 4.5..5.5. We expect the update to be delivered by exactly one + // call to the listener + valueRange.setMinMax(0.0, 5.0); + double newMinLeft = -0.5; + double newMaxRight = 5.5; + assertEquals(newMinLeft, valueRange.getMinLeft(), EPSILON); + assertEquals(newMaxRight, valueRange.getMaxRight(), EPSILON); + verify(listener, times(1)).onValueRangeBoundariesChanged(); + } + + @Test + public void testUpdateWithinBoundaries() { + valueRange.setMinMax(-5.5, 5.5); + verify(listener, times(0)).onValueRangeBoundariesChanged(); + valueRange.setMinMax(-4.5, 4.5); + verify(listener, times(0)).onValueRangeBoundariesChanged(); + } + + @Test + public void testGradualUpdateCrossingBoundariesEventuallyTriggerUpdate() { + valueRange.setMinMax(-5.5, 5.0); + verify(listener, times(0)).onValueRangeBoundariesChanged(); + valueRange.setMinMax(-6-EPSILON, 5.0); // just barely cross the *original* range boundary to the left, + // thus asserting that it wasn't expanded earlier by coming closer (from -5.0 to -5.5) to its end + verify(listener, times(1)).onValueRangeBoundariesChanged(); + verify(listener, times(1)).onValueRangeBoundariesChanged(); // ensure that calling verify doesn't clean counter + // and now it *was* expanded to cover -6..5, so the tolerance has become slightly greater, and 6+EPSILON + // should fit into the new bounds easily + valueRange.setMinMax(-6-EPSILON, 6+EPSILON); + verify(listener, times(1)).onValueRangeBoundariesChanged(); + } + + @Test + public void testCheckIfValueIsInRightBoundaryRangeAndUpdateIfNecessary() { + // update right border to 20, that means the boundaries now should be [-7.5,-2.5] and [17.5,22.5] + valueRange.setMinMax(-5.0, 20.0); + Double newMaxRight = 22.5; + Double newMinLeft = -7.5; + assertEquals(newMaxRight, (Double) valueRange.getMaxRight()); + assertEquals(newMinLeft, (Double) valueRange.getMinLeft()); + verify(listener, times(1)).onValueRangeBoundariesChanged(); + } + + @Test + public void testGetMinLeft() { + assertEquals(minLeft, valueRange.getMinLeft(), EPSILON); + } + + @Test + public void testGetMaxRight() { + assertEquals(maxRight, valueRange.getMaxRight(), EPSILON); + } +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapper.java b/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapper.java new file mode 100755 index 00000000000..d6642be8fdc --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapper.java @@ -0,0 +1,88 @@ +package com.sap.sse.common; + +import java.util.HashSet; +import java.util.Set; + +/** + * Maps a given range of values to a full color spectrum. The color spectrum could also be a grey scale. Therefore it + * needs the max and the min value to be mapped. It uses a {@link ValueRangeFlexibleBoundaries} to get those. The + * ColorMapper has listeners that will be notified if the colormapping has been changed. + * + * @author D073259 (Alessandro Stoltenberg) + * + */ +public class ColorMapper implements ValueRangeFlexibleBoundariesChangedListener { + private final ValueRangeFlexibleBoundaries valueRange; + private double minValue; + private double maxValue; + private boolean isGrey; + private final Set colorMapperChangedListeners; + + public ColorMapper(ValueRangeFlexibleBoundaries valueRange, boolean isGrey) { + this.valueRange = valueRange; + this.valueRange.addListener(this); + minValue = this.valueRange.getMinLeft(); + maxValue = this.valueRange.getMaxRight(); + this.isGrey = isGrey; + colorMapperChangedListeners = new HashSet<>(); + } + + public void setGrey(boolean isGrey) { + this.isGrey = isGrey; + notifyListeners(); + } + + /** + * Returns a string with a rgba representation of a the value mapped to the color spectrum in case {@link #isGrey} + * = true or the hsl representation of the color in case {@link #isGrey} = false . If called with a + * value outside the {@link #valueRange} the return will be the minimum or the maximum of the spectrum, depending if + * the input value is smaller or greater then the {@link #minValue} or {@link #maxValue} of the {@link #valueRange}. + */ + public String getColor(double value) { + final String result; + if (isGrey) { + if (value < minValue) { + result = "rgba(255,255,255,0.0)"; + } else if (value > maxValue) { + result = "rgba(255,255,255,1.0)"; + } else { + result = "rgba(255,255,255," + Math.min(1.0, (value - minValue) / (maxValue - minValue)) + ")"; + } + } else { + if (value < minValue) { + result = "hsl(0, 100%, 50%)"; + } else if (value > maxValue) { + result = "hsl(240, 100%, 50%)"; + } else { + double h = (1 - (value - minValue) / (maxValue - minValue)) * 240; + result = "hsl(" + Math.round(h) + ", 100%, 50%)"; + } + } + return result; + } + + private void updateMinMax() { + minValue = valueRange.getMinLeft(); + maxValue = valueRange.getMaxRight(); + } + + @Override + public void onValueRangeBoundariesChanged() { + updateMinMax(); + notifyListeners(); + } + + public void addListener(ColorMapperChangedListener listener) { + colorMapperChangedListeners.add(listener); + } + + public void removeListener(ColorMapperChangedListener listener) { + colorMapperChangedListeners.remove(listener); + } + + public void notifyListeners() { + for (ColorMapperChangedListener listener : colorMapperChangedListeners) { + listener.onColorMappingChanged(); + } + } +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapperChangedListener.java b/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapperChangedListener.java new file mode 100755 index 00000000000..d0c25c11113 --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/ColorMapperChangedListener.java @@ -0,0 +1,5 @@ +package com.sap.sse.common; + +public interface ColorMapperChangedListener { + void onColorMappingChanged(); +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundaries.java b/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundaries.java new file mode 100755 index 00000000000..4d42bac3c13 --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundaries.java @@ -0,0 +1,126 @@ +package com.sap.sse.common; + +import java.util.HashSet; +import java.util.Set; + +/** + * Manages a number range that contains a minimum and a maximum value. Those minimum and maximum values can be updated. + * If at least one of the two new values is outside of the range, the range is adjusted to ensure it again contains both + * values. The range will also shrink if the new minimum/maximum values are too far away from the range boundaries. This + * compares to a "Schmitt Trigger" for each end of the range, such that for the minimum and the maximum value there is + * each a value range at the respective end of the overall range, and when the minimum/maximum value is outside of its + * range the overall range will be adjusted. + *

+ * + * When the range is initialized or updated, the ranges for the minimum and the maximum value at each end of the overall + * range are determined symmetrically around the minimum/maximum value, with a given {@link #percentage} of the absolute + * difference between the minimum and maximum value as leeway either way. This percentage is provided to this class's + * constructor. For example, if the overall range is -5..5, its length is 10. If a 10% (0.1) leeway is configured, this + * means that 10% of the length of 10 which equals 1 is provided around each extremal value, yielding a range from + * -6..-4 around the minimum value -5, and a range from 4..6 around the maximum value 5. + *

+ * + * Visually, if we have: + *

+ * + * a) minLeft
+ * b) maxLeft
+ * c) minRight
+ * d) maxRight + *

+ * then the range can be imagined like this: + *

+ * + * (a)<--minValue-->(b)..........(c)<--maxValue-->(d) + *

+ * + * If an extremal value is not within the left or the right boundaries the boundaries will be adjusted as described + * above, and all {@link ValueRangeFlexibleBoundariesChangedListener listeners} that were + * {@link #addListener(ValueRangeFlexibleBoundariesChangedListener) added} will be notified. + *

+ * + * A ValueRangeFlexibleBoundaries needs an initialMin, an initialMax and a percentage that indicates the width of the + * value range surrounding initialMin and Max. + * + * @author D073259 (Alessandro Stoltenberg) + * + */ +public class ValueRangeFlexibleBoundaries { + private double minLeft; + private double maxLeft; + private double minRight; + private double maxRight; + private double min; + private double max; + private final double percentage; + private double halfBoundaryWidth; + private final Set valueRangeChangedListeners; + + public ValueRangeFlexibleBoundaries(double initialMin, double initialMax, double percentage) { + this.percentage = percentage; + valueRangeChangedListeners = new HashSet<>(); + min = initialMin; + max = initialMax; + update(); + } + + private void update() { + halfBoundaryWidth = (max - min) * (percentage); + minLeft = min - halfBoundaryWidth; + maxLeft = min + halfBoundaryWidth; + minRight = max - halfBoundaryWidth; + maxRight = max + halfBoundaryWidth; + notifyListeners(); + } + + /** + * Updates the minimum and maximum value. If {@code minValue} is greater than {@code maxValue}, + * an {@link IllegalArgumentException} will be thrown. If any of the values exceeds its range at + * its respective end of this overall range then the overall range will be adjusted, and the + * listeners will be notified. + */ + public void setMinMax(double minValue, double maxValue) { + if (minValue > maxValue) { + throw new IllegalArgumentException("minValue "+minValue+" is greater than maxValue "+maxValue); + } + min = minValue; + max = maxValue; + if (!isValueInRightRange(maxValue) || !isValueInLeftRange(minValue)) { + update(); + } + assert minLeft <= minValue; + assert minValue <= maxLeft; + assert minRight <= maxValue; + assert maxValue <= maxRight; + } + + private boolean isValueInLeftRange(double value) { + return value >= minLeft && value <= maxLeft; + } + + private boolean isValueInRightRange(double value) { + return value >= minRight && value <= maxRight; + } + + public double getMinLeft() { + return minLeft; + } + + public double getMaxRight() { + return maxRight; + } + + public void addListener(ValueRangeFlexibleBoundariesChangedListener listener) { + valueRangeChangedListeners.add(listener); + } + + public void removeListener(ValueRangeFlexibleBoundariesChangedListener listener) { + valueRangeChangedListeners.remove(listener); + } + + public void notifyListeners() { + for (ValueRangeFlexibleBoundariesChangedListener listener : valueRangeChangedListeners) { + listener.onValueRangeBoundariesChanged(); + } + } +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundariesChangedListener.java b/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundariesChangedListener.java new file mode 100755 index 00000000000..5581b6b1e7e --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/ValueRangeFlexibleBoundariesChangedListener.java @@ -0,0 +1,5 @@ +package com.sap.sse.common; + +public interface ValueRangeFlexibleBoundariesChangedListener { + void onValueRangeBoundariesChanged(); +} diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/dto/StatisticQueryDefinitionDTO.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/dto/StatisticQueryDefinitionDTO.java index 95c529f6204..d711e2b50f3 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/dto/StatisticQueryDefinitionDTO.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/dto/StatisticQueryDefinitionDTO.java @@ -7,19 +7,37 @@ import java.util.HashSet; import com.sap.sse.common.settings.SerializableSettings; import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.ClusterDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; public interface StatisticQueryDefinitionDTO extends Serializable { - public String getLocaleInfoName(); - public DataRetrieverChainDefinitionDTO getDataRetrieverChainDefinition(); + /** + * @return The settings to be used for the retriever levels. Can be empty, if no retriever level has settings. + */ public HashMap getRetrieverSettings(); + /** + * The values used to filter the data elements during the retrieval process. Each set of values is mapped by the + * dimension used to extract the value from a data element and the retriever level the dimension belongs to. The + * dimensions are declared by the data type retrieved by the corresponding retriever level and not by the data type + * the query is based on (return type of the retriever chain). + * + * The actual filter values can be anything that is returned by a dimension, which will be mostly simple data like + * Strings or Enums, but can also be more complex data structures like {@link ClusterDTO}. + * + * @return The values used to filter the data elements during the retrieval process. + */ public HashMap>> getFilterSelection(); public ArrayList getDimensionsToGroupBy(); public FunctionDTO getStatisticToCalculate(); public AggregationProcessorDefinitionDTO getAggregatorDefinition(); + + /** + * @return The LocalInfo name that will be used for internationalization. + */ + public String getLocaleInfoName(); } diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/AggregationProcessorDefinitionDTO.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/AggregationProcessorDefinitionDTO.java index 7e75da98312..88c1092f58d 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/AggregationProcessorDefinitionDTO.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/AggregationProcessorDefinitionDTO.java @@ -2,13 +2,31 @@ package com.sap.sse.datamining.shared.impl.dto; import java.io.Serializable; +/** + * A data mining aggregator definition that can be shared between client and server. Instances are usually constructed + * by a DataMiningDTOFactory based on a given backend AggregationProcessorDefinition. The corresponding backend instance + * can be determined using the DataMiningServer. + */ public class AggregationProcessorDefinitionDTO implements Serializable, Comparable { private static final long serialVersionUID = -434497637456305118L; + /** + * Additional identification feature, since the extracted and aggregated type wouldn't suffice. + * Has to be included when persisting a AggregationProcessorDefinitionDTO. + */ private String messageKey; + /** + * The fully qualified name of the aggregators input type. + */ private String extractedTypeName; + /** + * The fully qualified name of the aggregators result type. + */ private String aggregatedTypeName; + /** + * A human readable string representation. Should be omitted when persisting a AggregationProcessorDefinitionDTO. + */ private String displayName; /** diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverChainDefinitionDTO.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverChainDefinitionDTO.java index 898c2cd3c9e..b136faf93dc 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverChainDefinitionDTO.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverChainDefinitionDTO.java @@ -6,12 +6,26 @@ import java.util.HashMap; import com.sap.sse.common.settings.SerializableSettings; +/** + * A data mining retriever chain definition that can be shared between client and server. Instances are usually + * constructed by a DataMiningDTOFactory based on a given backend DataRetrieverChainDefinition. The corresponding + * backend instance can be determined using the DataMiningServer. + */ public class DataRetrieverChainDefinitionDTO implements Serializable, Comparable { private static final long serialVersionUID = 7806173601799997214L; + /** + * The fully qualified name of the initial data source type. + */ private String dataSourceTypeName; + /** + * The ordered list of retriever levels. + */ private ArrayList retrieverLevels; + /** + * A human readable string representation. Should be omitted when persisting a DataRetrieverChainDefinition. + */ private String displayName; /** diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverLevelDTO.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverLevelDTO.java index f007fc20a94..99b6d832942 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverLevelDTO.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/DataRetrieverLevelDTO.java @@ -4,12 +4,34 @@ import java.io.Serializable; import com.sap.sse.common.settings.SerializableSettings; +/** + * A data mining retriever level that can be shared between client and server. Instances are usually constructed by a + * DataMiningDTOFactory based on a given backend DataRetrieverLevel. + */ public class DataRetrieverLevelDTO implements Serializable, Comparable { private static final long serialVersionUID = 6911713148350359643L; + /** + * The index of this retriever level in the retriever chain. + */ private int retrieverLevel; + /** + * The fully qualified name of the Processor performing the retrieval of this level. + */ private String retrieverTypeName; + /** + * The type of the retrieved data elements in form of a {@link LocalizedTypeDTO}. Its type name is the fully + * qualified name of the retrieved data type and is used for identification purposes. + * + * The display name is used as human readable string representation of this retriever level and should be omitted + * when persisting a DataRetrieverLevelDTO. + */ private LocalizedTypeDTO retrievedDataType; + + /** + * The default settings for this retriever level or null, if the level doesn't have settings. Should be + * omitted when persisting a DataRetrieverLevelDTO. + */ private SerializableSettings defaultSettings; /** @@ -60,7 +82,7 @@ public class DataRetrieverLevelDTO implements Serializable, Comparable { private static final long serialVersionUID = 4587389541910498505L; private final boolean isDimension; + /** + * The functions name including its parameters in parenthesis or empty parenthesis, if this function doesn't have + * parameters. Can consist of multiple concatenated function names, if the backend Function encapsulated multiple + * data mining functions. + */ private final String functionName; + /** + * The fully qualified name of the type that declares this function. + */ private final String sourceTypeName; + /** + * The fully qualified name of the type returned by this function. + */ private final String returnTypeName; + /** + * The fully qualified names of the functions parameter types. + */ private final List parameterTypeNames; + + /** + * Meta-data for the natural ordering of FunctionDTOs. Should be omitted when persisting a FunctionDTO. + */ private final int ordinal; + /** + * A human readable string representation. Should be omitted when persisting a FunctionDTO. + */ private String displayName; /** * If the {@link #displayName} is not set and this provider is valid, the {@F @@ -56,7 +82,15 @@ public class FunctionDTO implements Serializable, Comparable { public List getParameterTypeNames() { return parameterTypeNames; } - + + /** + * The functions name including its parameters in parenthesis or empty parenthesis, if this function doesn't have + * parameters. Can consist of multiple concatenated function names, if the backend Function encapsulated multiple + * data mining functions. + * + * @return The functions name including its parameters in parenthesis or empty parenthesis, if this function doesn't + * have parameters. + */ public String getFunctionName() { return functionName; } diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/ModifiableStatisticQueryDefinitionDTO.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/ModifiableStatisticQueryDefinitionDTO.java index 04d800fbf5e..6b153d44180 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/ModifiableStatisticQueryDefinitionDTO.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/dto/ModifiableStatisticQueryDefinitionDTO.java @@ -8,16 +8,50 @@ import java.util.HashSet; import com.sap.sse.common.settings.SerializableSettings; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; +/** + * Definition for a data mining query that can be shared between client and server. Instances are usually created in the + * Data Mining UI, but can also by constructed from a backend instance (StatisticQueryDefinition) using a + * DataMiningDTOFactory. The corresponding backend instance for a DTO can be created using the DataMiningServer. + */ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefinitionDTO { private static final long serialVersionUID = -6438771277564908352L; - private String localeInfoName; + /** + * The extraction function used to get the statistic from the retrieved data elements. + */ private FunctionDTO statisticToCalculate; + /** + * The aggregator used to aggregate the extracted statistics. + */ private AggregationProcessorDefinitionDTO aggregatorDefinition; + /** + * The list of dimensions to group the results by. + */ private ArrayList dimensionsToGroupBy; + /** + * The retriever chain used to retrieve the data elements. + */ private DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition; + /** + * The settings to be used for the retriever levels. Can be empty, if no retriever level has settings. + */ private HashMap retrieverSettings; + /** + * The values used to filter the data elements during the retrieval process. Each set of values is mapped by the + * dimension used to extract the value from a data element and the retriever level the dimension belongs to. The + * dimensions are declared by the data type retrieved by the corresponding retriever level and not by the data type + * the query is based on (return type of the retriever chain). + * + * The actual filter values can be anything that is returned by a dimension, which will be mostly simple data like + * Strings or Enums, but can also be more complex data structures like {@link ClusterDTO}. + */ private HashMap>> filterSelection; + + /** + * The LocalInfo name that will be used for internationalization. Should be omitted when persisting a + * ModifiableStatisticQueryDefinitionDTO. + */ + private String localeInfoName; /** * Constructor for the GWT-Serialization. Don't use this! @@ -25,7 +59,9 @@ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefi @Deprecated ModifiableStatisticQueryDefinitionDTO() { } - public ModifiableStatisticQueryDefinitionDTO(String localeInfoName, FunctionDTO statisticToCalculate, AggregationProcessorDefinitionDTO aggregatorDefinition, DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition) { + public ModifiableStatisticQueryDefinitionDTO(String localeInfoName, FunctionDTO statisticToCalculate, + AggregationProcessorDefinitionDTO aggregatorDefinition, + DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition) { this.localeInfoName = localeInfoName; this.statisticToCalculate = statisticToCalculate; this.aggregatorDefinition = aggregatorDefinition; @@ -48,18 +84,6 @@ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefi filterSelection.put(retrieverLevel, new HashMap<>(filterSelectionToCopy.get(retrieverLevel))); } } - - @Override - public String getLocaleInfoName() { - return localeInfoName; - } - - public void setLocaleInfoName(String localeInfoName) { - if (localeInfoName == null) { - throw new NullPointerException("The locale info name mustn't be null"); - } - this.localeInfoName = localeInfoName; - } @Override public DataRetrieverChainDefinitionDTO getDataRetrieverChainDefinition() { @@ -137,6 +161,18 @@ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefi this.aggregatorDefinition = aggregatorDefinition; } + @Override + public String getLocaleInfoName() { + return localeInfoName; + } + + public void setLocaleInfoName(String localeInfoName) { + if (localeInfoName == null) { + throw new NullPointerException("The locale info name mustn't be null"); + } + this.localeInfoName = localeInfoName; + } + @Override public int hashCode() { final int prime = 31; @@ -146,7 +182,6 @@ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefi + ((dataRetrieverChainDefinition == null) ? 0 : dataRetrieverChainDefinition.hashCode()); result = prime * result + ((dimensionsToGroupBy == null) ? 0 : dimensionsToGroupBy.hashCode()); result = prime * result + ((filterSelection == null) ? 0 : filterSelection.hashCode()); - result = prime * result + ((localeInfoName == null) ? 0 : localeInfoName.hashCode()); result = prime * result + ((retrieverSettings == null) ? 0 : retrieverSettings.hashCode()); result = prime * result + ((statisticToCalculate == null) ? 0 : statisticToCalculate.hashCode()); return result; @@ -181,11 +216,6 @@ public class ModifiableStatisticQueryDefinitionDTO implements StatisticQueryDefi return false; } else if (!filterSelection.equals(other.filterSelection)) return false; - if (localeInfoName == null) { - if (other.localeInfoName != null) - return false; - } else if (!localeInfoName.equals(other.localeInfoName)) - return false; if (retrieverSettings == null) { if (other.retrieverSettings != null) return false; diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestAbortingHeavyLoadQuery.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestAbortingHeavyLoadQuery.java index 3e6a5494596..dd1a5828429 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestAbortingHeavyLoadQuery.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestAbortingHeavyLoadQuery.java @@ -333,7 +333,7 @@ public class TestAbortingHeavyLoadQuery { protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) { } }; - Processor retriever1 = new AbstractRetrievalProcessor(String.class, Element.class, executor, Collections.singleton(heavyLoadProcessor), 1) { + Processor retriever1 = new AbstractRetrievalProcessor(String.class, Element.class, executor, Collections.singleton(heavyLoadProcessor), 1, "") { @Override protected ProcessorInstruction createInstruction(String element) { AbstractProcessorInstruction instruction = (AbstractProcessorInstruction) super.createInstruction(element); @@ -365,7 +365,7 @@ public class TestAbortingHeavyLoadQuery { if(canProcessElements()) unfinishedInstructions.remove(instruction); } }; - Processor, String> retriever0 = new AbstractRetrievalProcessor, String>(dataSourceType, String.class, executor, Collections.singleton(retriever1), 0) { + Processor, String> retriever0 = new AbstractRetrievalProcessor, String>(dataSourceType, String.class, executor, Collections.singleton(retriever1), 0, "") { @Override protected ProcessorInstruction createInstruction(Iterable element) { AbstractProcessorInstruction instruction = (AbstractProcessorInstruction) super.createInstruction(element); diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestStatisticQuery.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestStatisticQuery.java index ff637110dc6..7187145ea3f 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestStatisticQuery.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/TestStatisticQuery.java @@ -89,8 +89,8 @@ public class TestStatisticQuery { private static class NumberRetrievalProcessor extends AbstractRetrievalProcessor, Number> { @SuppressWarnings("unchecked") - public NumberRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { - super((Class>)(Class) Collection.class, Number.class, executor, resultReceivers, retrievalLevel); + public NumberRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel, String retrievedDataTypeMessageKey) { + super((Class>)(Class) Collection.class, Number.class, executor, resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); } @Override diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/TestRetrieverFilterProcessorChain.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/TestRetrieverFilterProcessorChain.java index 8ef8b80a8c5..43980ec291a 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/TestRetrieverFilterProcessorChain.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/TestRetrieverFilterProcessorChain.java @@ -21,6 +21,7 @@ import com.sap.sse.datamining.test.util.components.NullProcessor; public class TestRetrieverFilterProcessorChain { + private String retrievedDataTypeMessageKey; private Processor, Integer> retrievalProcessor; private Collection dataSource; @@ -59,6 +60,7 @@ public class TestRetrieverFilterProcessorChain { OverwritingResultDataBuilder resultDataBuilder = new OverwritingResultDataBuilder(); retrievalProcessor.getAdditionalResultData(resultDataBuilder); assertThat(resultDataBuilder.getRetrievedDataAmount(), is(expectedRetrievedDataAmount)); + assertThat(resultDataBuilder.getDataTypeMessageKey(), is(retrievedDataTypeMessageKey)); } @Test @@ -67,12 +69,17 @@ public class TestRetrieverFilterProcessorChain { layeredDataSource.add(dataSource); layeredDataSource.add(dataSource); layeredDataSource.add(dataSource); - + + @SuppressWarnings("unchecked") + Class>> inputType = (Class>>)(Class) Iterable.class; + @SuppressWarnings("unchecked") + Class> retrievedType = (Class>)(Class) Iterable.class; Collection, ?>> resultReceivers = new ArrayList<>(); resultReceivers.add(retrievalProcessor); - @SuppressWarnings("unchecked") - Processor>, Iterable> layeredRetrievalProcessor = new AbstractRetrievalProcessor>, Iterable>((Class>>)(Class) Iterable.class, (Class>)(Class) Iterable.class, - ConcurrencyTestsUtil.getSharedExecutor(), resultReceivers, 0) { + String layeredRetrievedDataTypeMessageKey = "NumberList"; + Processor>, Iterable> layeredRetrievalProcessor = new AbstractRetrievalProcessor>, Iterable>( + inputType, retrievedType, ConcurrencyTestsUtil.getSharedExecutor(), resultReceivers, 0, + layeredRetrievedDataTypeMessageKey) { @Override protected Iterable> retrieveData(Iterable> element) { return element; @@ -88,6 +95,7 @@ public class TestRetrieverFilterProcessorChain { OverwritingResultDataBuilder resultDataBuilder = new OverwritingResultDataBuilder(); layeredRetrievalProcessor.getAdditionalResultData(resultDataBuilder); assertThat(resultDataBuilder.getRetrievedDataAmount(), is(expectedRetrievedDataAmount)); + assertThat(resultDataBuilder.getDataTypeMessageKey(), is(retrievedDataTypeMessageKey)); } @SuppressWarnings("unchecked") @@ -112,9 +120,12 @@ public class TestRetrieverFilterProcessorChain { }; Processor filtrationProcessor = new ParallelFilteringProcessor<>(Integer.class, ConcurrencyTestsUtil.getSharedExecutor(), filtrationResultReceivers, elementGreaterZeroFilterCriteria); + Class> inputType = (Class>) (Class) Iterable.class; Collection> retrievalResultReceivers = new ArrayList<>(); retrievalResultReceivers.add(filtrationProcessor); - retrievalProcessor = new AbstractRetrievalProcessor, Integer>((Class>)(Class) Iterable.class, Integer.class, ConcurrencyTestsUtil.getSharedExecutor(), retrievalResultReceivers, 1) { + retrievedDataTypeMessageKey = "Number"; + retrievalProcessor = new AbstractRetrievalProcessor, Integer>(inputType, Integer.class, + ConcurrencyTestsUtil.getSharedExecutor(), retrievalResultReceivers, 1, retrievedDataTypeMessageKey) { @Override protected Iterable retrieveData(Iterable element) { return element; diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/management/TestDataRetrieverChainDefinitionManagement.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/management/TestDataRetrieverChainDefinitionManagement.java index cbe4aab3c19..d972e3cbac3 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/management/TestDataRetrieverChainDefinitionManagement.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/components/management/TestDataRetrieverChainDefinitionManagement.java @@ -169,8 +169,8 @@ public class TestDataRetrieverChainDefinitionManagement { private class TestSeriesRetrievalProcessor extends AbstractRetrievalProcessor { public TestSeriesRetrievalProcessor(ExecutorService executor, - Collection> resultReceivers, int retrievalLevel) { - super(Test_Regatta.class, Test_Series.class, executor, resultReceivers, retrievalLevel); + Collection> resultReceivers, int retrievalLevel, String retrievedDataTypeMessageKey) { + super(Test_Regatta.class, Test_Series.class, executor, resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); } @Override @@ -183,8 +183,8 @@ public class TestDataRetrieverChainDefinitionManagement { private class TestRaceFromSeriesRetrievalProcessor extends AbstractRetrievalProcessor { public TestRaceFromSeriesRetrievalProcessor(ExecutorService executor, - Collection> resultReceivers, int retrievalLevel) { - super(Test_Series.class, Test_HasRaceContext.class, executor, resultReceivers, retrievalLevel); + Collection> resultReceivers, int retrievalLevel, String retrievedDataTypeMessageKey) { + super(Test_Series.class, Test_HasRaceContext.class, executor, resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); } @Override diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestFunctionManagerAsFunctionProvider.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestFunctionManagerAsFunctionProvider.java index 5299793fbbc..145fcad3cd9 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestFunctionManagerAsFunctionProvider.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/impl/functions/TestFunctionManagerAsFunctionProvider.java @@ -17,6 +17,7 @@ import java.util.Map; import org.junit.Before; import org.junit.Test; +import com.sap.sse.common.Util; import com.sap.sse.datamining.ModifiableDataMiningServer; import com.sap.sse.datamining.components.DataRetrieverChainDefinition; import com.sap.sse.datamining.components.Processor; @@ -66,6 +67,11 @@ public class TestFunctionManagerAsFunctionProvider { server.registerAllWithExternalFunctionPolicy(externalClassesToScan); } + @Test + public void testGetIdentityFunction() { + assertThat(server.getIdentityFunction(), is(new IdentityFunction())); + } + @Test public void testGetAllStatistics() { Collection> expectedFunctions = functionRegistryUtil.getAllExpectedStatistics(); @@ -153,6 +159,11 @@ public class TestFunctionManagerAsFunctionProvider { assertThat(server.getFunctionsFor(Test_HasLegOfCompetitorContextImpl.class), is(expectedFunctions)); } + @Test + public void testGetAllFunctionsForObject() { + assertThat(Util.size(server.getFunctionsFor(Object.class)), is(0)); + } + @SuppressWarnings("unchecked") // Hamcrest requires type matching of actual and expected type, so the Functions have to be specific (without ) @Test public void testGetFunctionForDimensionDTO() throws NoSuchMethodException, SecurityException { @@ -240,6 +251,21 @@ public class TestFunctionManagerAsFunctionProvider { assertThat(server.getFunctionForDTO(sideEffectFreeValueDTO), is(nullValue())); } + @Test + public void testGetFunctionForIdentityDTO() { + DataMiningDTOFactory dtoFactory = FunctionTestsUtil.getDTOFactory(); + IdentityFunction identityFunction = new IdentityFunction(); + + FunctionDTO identityFunctionDTO_English = dtoFactory.createFunctionDTO(identityFunction, stringMessages, Locale.ENGLISH); + assertThat(server.getFunctionForDTO(identityFunctionDTO_English), is(identityFunction)); + + FunctionDTO identityFunctionDTO_German = dtoFactory.createFunctionDTO(identityFunction, stringMessages, Locale.GERMAN); + assertThat(server.getFunctionForDTO(identityFunctionDTO_German), is(identityFunction)); + + FunctionDTO identityFunctionDTO = dtoFactory.createFunctionDTO(identityFunction); + assertThat(server.getFunctionForDTO(identityFunctionDTO), is(identityFunction)); + } + @Test public void testGetFunctionForNullDTO() { assertThat(server.getFunctionForDTO(null), is(nullValue())); diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestLegOfCompetitorWithContextRetrievalProcessor.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestLegOfCompetitorWithContextRetrievalProcessor.java index 5155f230876..9f790a1aa5e 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestLegOfCompetitorWithContextRetrievalProcessor.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestLegOfCompetitorWithContextRetrievalProcessor.java @@ -14,8 +14,11 @@ import com.sap.sse.datamining.test.domain.Test_Leg; public class TestLegOfCompetitorWithContextRetrievalProcessor extends AbstractRetrievalProcessor{ - public TestLegOfCompetitorWithContextRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { - super(Test_HasRaceContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers, retrievalLevel); + public TestLegOfCompetitorWithContextRetrievalProcessor(ExecutorService executor, + Collection> resultReceivers, int retrievalLevel, + String retrievedDataTypeMessageKey) { + super(Test_HasRaceContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers, + retrievalLevel, retrievedDataTypeMessageKey); } @Override diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRaceWithContextRetrievalProcessor.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRaceWithContextRetrievalProcessor.java index faae5dc60d2..8700463c06f 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRaceWithContextRetrievalProcessor.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRaceWithContextRetrievalProcessor.java @@ -14,8 +14,9 @@ import com.sap.sse.datamining.test.domain.Test_Regatta; public class TestRaceWithContextRetrievalProcessor extends AbstractRetrievalProcessor { public TestRaceWithContextRetrievalProcessor(ExecutorService executor, - Collection> resultReceivers, int retrievalLevel) { - super(Test_Regatta.class, Test_HasRaceContext.class, executor, resultReceivers, retrievalLevel); + Collection> resultReceivers, int retrievalLevel, + String retrievedDataTypeMessageKey) { + super(Test_Regatta.class, Test_HasRaceContext.class, executor, resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); } @Override diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRegattaRetrievalProcessor.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRegattaRetrievalProcessor.java index add66772929..32954cd102a 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRegattaRetrievalProcessor.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/TestRegattaRetrievalProcessor.java @@ -10,8 +10,11 @@ import com.sap.sse.datamining.test.domain.Test_Regatta; public class TestRegattaRetrievalProcessor extends AbstractRetrievalProcessor, Test_Regatta> { @SuppressWarnings("unchecked") - public TestRegattaRetrievalProcessor(ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { - super((Class>)(Class) Collection.class, Test_Regatta.class, executor, resultReceivers, retrievalLevel); + public TestRegattaRetrievalProcessor(ExecutorService executor, + Collection> resultReceivers, int retrievalLevel, + String retrievedDataTypeMessageKey) { + super((Class>) (Class) Collection.class, Test_Regatta.class, executor, + resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); } @Override diff --git a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/Test_RetrievalProcessorWithSettings.java b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/Test_RetrievalProcessorWithSettings.java index cde14e8188b..a198f3277f0 100644 --- a/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/Test_RetrievalProcessorWithSettings.java +++ b/java/com.sap.sse.datamining.test/src/com/sap/sse/datamining/test/util/components/Test_RetrievalProcessorWithSettings.java @@ -13,8 +13,8 @@ public class Test_RetrievalProcessorWithSettings extends AbstractRetrievalProces private Test_RetrievalProcessorSettings settings; public Test_RetrievalProcessorWithSettings(ExecutorService executor, Collection> resultReceivers, - Test_RetrievalProcessorSettings settings, int retrievalLevel) { - super(Test_HasLegOfCompetitorContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers, retrievalLevel); + Test_RetrievalProcessorSettings settings, int retrievalLevel, String retrievedDataTypeMessageKey) { + super(Test_HasLegOfCompetitorContext.class, Test_HasLegOfCompetitorContext.class, executor, resultReceivers, retrievalLevel, retrievedDataTypeMessageKey); this.settings = settings; } diff --git a/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java index eeb53fde4c6..8e5c43ee5f7 100644 --- a/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java +++ b/java/com.sap.sse.datamining.ui.test/src/com/sap/sse/datamining/ui/test/client/presentation/DummyResultsPresenter.java @@ -6,6 +6,7 @@ import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.presentation.AbstractResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter; @@ -35,7 +36,7 @@ public class DummyResultsPresenter extends AbstractResultsPresenter { * Stores this ResultsPresenter for test purposes. */ @Override - public void showResult(QueryResultDTO result) { + public void showResult(StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result) { executedResultsPresenter.add(this); } diff --git a/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java index 59e4dc4ef87..aee18c998e5 100644 --- a/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java +++ b/java/com.sap.sse.datamining.ui.test/test/com/sap/sse/datamining/ui/test/client/TestSSEDataMiningUi.java @@ -4,10 +4,13 @@ import java.util.ArrayList; import java.util.List; import com.google.gwt.junit.client.GWTTestCase; +import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; +import com.sap.sse.datamining.ui.client.ResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.MultiResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.PlainResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.ResultsChart; import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter; +import com.sap.sse.datamining.ui.client.presentation.TabbedResultsPresenter.ResultsPresenterFactory; import com.sap.sse.datamining.ui.test.client.presentation.DummyQueryResultDTO; import com.sap.sse.datamining.ui.test.client.presentation.DummyResultsPresenter; @@ -31,10 +34,13 @@ public class TestSSEDataMiningUi extends GWTTestCase { public void testAvoidDuplicateResultTypeRegistration() { TabbedResultsPresenter presenter = new TabbedResultsPresenter(null, null, null); - presenter.registerResultsPresenter(String.class, new PlainResultsPresenter(null, null)); - presenter.registerResultsPresenter(Double.class, new MultiResultsPresenter(null, null, null)); + presenter.registerResultsPresenter(String.class, new ResultsPresenterFactory<>(PlainResultsPresenter.class, + () -> new PlainResultsPresenter(null, null))); + presenter.registerResultsPresenter(Double.class, new ResultsPresenterFactory<>(MultiResultsPresenter.class, + () -> new MultiResultsPresenter(null, null, null))); try { - presenter.registerResultsPresenter(String.class, new ResultsChart(null, null, true, null)); + presenter.registerResultsPresenter(String.class, + new ResultsPresenterFactory<>(ResultsChart.class, () -> new ResultsChart(null, null, true, null))); fail(); } catch (IllegalStateException e) { assertTrue(true); @@ -48,23 +54,34 @@ public class TestSSEDataMiningUi extends GWTTestCase { TabbedResultsPresenter presenter = new TabbedResultsPresenter(null, null, null); List executedPresenter = new ArrayList<>(); - DummyResultsPresenter stringResultsPresenter = new DummyResultsPresenter(executedPresenter); - DummyResultsPresenter doubleResultsPresenter = new DummyResultsPresenter(executedPresenter); + presenter.registerResultsPresenter(String.class, new ResultsPresenterFactory<>( + StringDummyResultsPresenter.class, () -> new StringDummyResultsPresenter(executedPresenter))); + presenter.registerResultsPresenter(Double.class, new ResultsPresenterFactory<>( + DoubleDummyResultsPresenter.class, () -> new DoubleDummyResultsPresenter(executedPresenter))); - presenter.registerResultsPresenter(String.class, stringResultsPresenter); - presenter.registerResultsPresenter(Double.class, doubleResultsPresenter); - - presenter.showResult(new DummyQueryResultDTO<>(String.class)); + presenter.showResult(null, new DummyQueryResultDTO<>(String.class)); assertTrue(executedPresenter.size() == 1); - assertEquals(executedPresenter.get(0), stringResultsPresenter); + assertTrue(executedPresenter.get(0) instanceof StringDummyResultsPresenter); - presenter.showResult(new DummyQueryResultDTO<>(Double.class)); + presenter.showResult(null, new DummyQueryResultDTO<>(Double.class)); assertTrue(executedPresenter.size() == 2); - assertEquals(executedPresenter.get(1), doubleResultsPresenter); + assertTrue(executedPresenter.get(1) instanceof DoubleDummyResultsPresenter); - presenter.showResult(new DummyQueryResultDTO<>(Number.class)); + presenter.showResult(null, new DummyQueryResultDTO<>(Number.class)); assertTrue(executedPresenter.size() == 2); - assertEquals(executedPresenter.get(0), stringResultsPresenter); - assertEquals(executedPresenter.get(1), doubleResultsPresenter); + assertTrue(executedPresenter.get(0) instanceof StringDummyResultsPresenter); + assertTrue(executedPresenter.get(1) instanceof DoubleDummyResultsPresenter); + } + + private static class StringDummyResultsPresenter extends DummyResultsPresenter { + public StringDummyResultsPresenter(List executeResultsPresenter) { + super(executeResultsPresenter); + } + } + + private static class DoubleDummyResultsPresenter extends DummyResultsPresenter { + public DoubleDummyResultsPresenter(List executeResultsPresenter) { + super(executeResultsPresenter); + } } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java index 012e8f0d12f..b79d64885e1 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ChartToCsvExporter.java @@ -5,7 +5,8 @@ import org.moxieapps.gwt.highcharts.client.Point; import org.moxieapps.gwt.highcharts.client.Series; import com.google.gwt.i18n.client.NumberFormat; -import com.google.gwt.user.client.Window; +import com.sap.sse.gwt.client.Notification; +import com.sap.sse.gwt.client.Notification.NotificationType; import com.sap.sse.gwt.shared.TextExporter; /** @@ -32,7 +33,7 @@ public class ChartToCsvExporter { public void exportChartAsCsvToClipboard(Chart chartToExport) { String csvContentExport = createCsvExportContentForStatisticsCurve(chartToExport); TextExporter.exportToClipboard(csvContentExport); - Window.alert(exportedMessage); + Notification.notify(exportedMessage, NotificationType.INFO); } private static String createCsvExportContentForStatisticsCurve(Chart chartToExport) { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/CompositeResultsPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/CompositeResultsPresenter.java new file mode 100644 index 00000000000..abb586a0a9a --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/CompositeResultsPresenter.java @@ -0,0 +1,123 @@ +package com.sap.sse.datamining.ui.client; + +import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; + +/** + * Interface for a result presenter composing multiple inner result presenters. + * + * @author Lennart Hensler + */ +public interface CompositeResultsPresenter extends ResultsPresenter { + + @FunctionalInterface + public interface CurrentPresenterChangedListener { + + void currentPresenterChanged(String presenterId); + + } + + /** + * @return The identifier for the currently active presenter + */ + String getCurrentPresenterId(); + + /** + * @return All ids of the currently contained presenters + */ + Iterable getPresenterIds(); + + /** + * @param presenterId The id of the presenter to check for + * @return true, if a presenter with the given id exists + */ + boolean containsPresenter(String presenterId); + + /** + * @param presenterId The id of the presenter to get the results from + * @return The results of the presenter with the given id or null, + * if no presenter for the id exists. + */ + QueryResultDTO getResult(String presenterId); + + /** + * @param presenterId The id of the presenter to get the query definition from + * @return The query definition of the presenter with the given id or null, + * if no presenter for the id exists. + */ + StatisticQueryDefinitionDTO getQueryDefinition(String presenterId); + + /** + * Displays the given result for the given query definition int the presenter for the given id. + * Does nothing if no presenter for the given id exists. The given result may be null + * to clear the presenter. The given query definition may be null, but this is + * discouraged unless the result is also null. + * + * @param presenterId The id of the presenter used to show the result + * @param queryDefinition The query definition of the result to display + * @param result The result to display + */ + void showResult(String presenterId, StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result); + + /** + * Shows the given error in the presenter for the given id. Does nothing if no + * presenter for the given id exists. + * + * @param presenterId The id of the presenter used to show the error + * @param error The error message + */ + void showError(String presenterId, String error); + + /** + * Shows the given error in the presenter for the given id. Does nothing if no + * presenter for the given id exists. + * + * @param presenterId The id of the presenter used to show the error + * @param mainError The main error message + * @param detailedErrors The detailed error messages + */ + void showError(String presenterId, String mainError, Iterable detailedErrors); + + /** + * Shows the busy indicator of the presenter for the given id. Does nothing if no + * presenter for the given id exists. + * + * @param presenterId The id of the presenter used to show the busy indicator + */ + void showBusyIndicator(String presenterId); + + void addCurrentPresenterChangedListener(CurrentPresenterChangedListener listener); + void removeCurrentPresenterChangedListener(CurrentPresenterChangedListener listener); + + @Override + default QueryResultDTO getCurrentResult() { + return getResult(getCurrentPresenterId()); + } + + @Override + default StatisticQueryDefinitionDTO getCurrentQueryDefinition() { + return getQueryDefinition(getCurrentPresenterId()); + } + + @Override + default void showResult(StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result) { + showResult(getCurrentPresenterId(), queryDefinition, result); + } + + @Override + default void showError(String error) { + showError(getCurrentPresenterId(), error); + } + + @Override + default void showError(String mainError, Iterable detailedErrors) { + showError(getCurrentPresenterId(), mainError, detailedErrors); + } + + @Override + default void showBusyIndicator() { + showBusyIndicator(getCurrentPresenterId()); + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningComponentProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningComponentProvider.java index 2a97d6dd7d8..c898331506c 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningComponentProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningComponentProvider.java @@ -1,24 +1,28 @@ package com.sap.sse.datamining.ui.client; +import java.util.function.Consumer; + import com.sap.sse.common.settings.Settings; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.gwt.client.shared.components.Component; public interface DataMiningComponentProvider extends Component { + + static final Consumer> EmptyApplyCallback = m -> { }; /** * Tells the provider, that {@link #reloadComponents()} will be called in the future. */ - public void awaitReloadComponents(); + void awaitReloadComponents(); /** * @return true, if the provider is reloading its components or is waiting for the * {@link #reloadComponents()} call. */ - public boolean isAwaitingReload(); + boolean isAwaitingReload(); - public void reloadComponents(); + void reloadComponents(); - public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition); + void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition, Consumer> callback); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningService.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningService.java index dcfbe1ef7ff..f1c69314e0b 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningService.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningService.java @@ -23,10 +23,13 @@ public interface DataMiningService extends RemoteService { Date getComponentsChangedTimepoint(); + FunctionDTO getIdentityFunction(String localeInfoName); HashSet getAllStatistics(String localeInfoName); HashSet getStatisticsFor(DataRetrieverChainDefinitionDTO retrieverChainDefinition, String localeInfoName); + + HashSet getAggregatorDefinitions(String localeInfoName); HashSet getAggregatorDefinitionsFor(FunctionDTO extractionFunction, String localeInfoName); @@ -52,9 +55,13 @@ public interface DataMiningService extends RemoteService { StatisticQueryDefinitionDTO queryDefinition); HashSet getPredefinedQueryIdentifiers(); + + StatisticQueryDefinitionDTO getPredefinedQueryDefinition(PredefinedQueryIdentifier identifier, String localeInfoName); QueryResultDTO runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName); + + StatisticQueryDefinitionDTO localize(StatisticQueryDefinitionDTO queryDefinition, String localeInfoName); SerializationDummy pseudoMethodSoThatSomeClassesAreAddedToTheGWTSerializationPolicy(); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningServiceAsync.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningServiceAsync.java index 64ca3ff13a0..50b2833fe84 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningServiceAsync.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/DataMiningServiceAsync.java @@ -24,10 +24,15 @@ public interface DataMiningServiceAsync { public void getComponentsChangedTimepoint(AsyncCallback asyncCallback); + void getIdentityFunction(String localeInfoName, AsyncCallback callback); + void getAllStatistics(String localeInfoName, AsyncCallback> callback); void getStatisticsFor(DataRetrieverChainDefinitionDTO currentRetrieverChainDefinition, String localeName, AsyncCallback> asyncCallback); + + void getAggregatorDefinitions(String localeInfoName, + AsyncCallback> callback); void getAggregatorDefinitionsFor(FunctionDTO extractionFunction, String localeInfoName, AsyncCallback> asyncCallback); @@ -54,10 +59,16 @@ public interface DataMiningServiceAsync { StatisticQueryDefinitionDTO queryDefinition, AsyncCallback> callback); void getPredefinedQueryIdentifiers(AsyncCallback> callback); + + void getPredefinedQueryDefinition(PredefinedQueryIdentifier identifier, String localeInfoName, + AsyncCallback callback); void runPredefinedQuery(DataMiningSession session, PredefinedQueryIdentifier identifier, String localeInfoName, AsyncCallback> callback); + + void localize(StatisticQueryDefinitionDTO queryDefinition, String localeInfoName, + AsyncCallback callback); /** * This method does nothing, but is needed to ensure, that some classes for the data mining (like diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionPresenter.java index 88aa8787e8e..f0ae243afd1 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionPresenter.java @@ -3,6 +3,6 @@ package com.sap.sse.datamining.ui.client; import com.sap.sse.common.settings.AbstractSettings; import com.sap.sse.gwt.client.shared.components.Component; -public interface FilterSelectionPresenter extends Component { +public interface FilterSelectionPresenter extends Component, FilterSelectionChangedListener { } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionProvider.java index 3161015cb61..9ceb0084d27 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/FilterSelectionProvider.java @@ -17,6 +17,6 @@ public interface FilterSelectionProvider public void clearSelection(); - void setHighestRetrieverLevelWithFilterDimension(FunctionDTO dimension, Serializable groupKey); + void setFilterSelectionForUnreducedDimension(FunctionDTO unreducedDimension, Serializable filterValue, Runnable onCompletion); } \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/GroupingProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/GroupingProvider.java index 88af5fadb97..d5311f8a23c 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/GroupingProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/GroupingProvider.java @@ -4,17 +4,12 @@ import java.util.Collection; import com.google.gwt.user.client.ui.ValueListBox; import com.sap.sse.common.settings.SerializableSettings; -import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; public interface GroupingProvider extends DataMiningComponentProvider, DataRetrieverChainDefinitionChangedListener { Collection getDimensionsToGroupBy(); - String getCustomGrouperScriptText(); - - void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition); - void addGroupingChangedListener(GroupingChangedListener listener); void removeDimensionToGroupBy(FunctionDTO dimension); diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryDefinitionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryDefinitionProvider.java index 54f1b5ad05b..2358ac5204b 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryDefinitionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryDefinitionProvider.java @@ -2,16 +2,19 @@ package com.sap.sse.datamining.ui.client; import com.sap.sse.common.settings.Settings; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; +import com.sap.sse.gwt.client.shared.components.Component; -public interface QueryDefinitionProvider - extends DataMiningComponentProvider { +public interface QueryDefinitionProvider extends Component { - public Iterable validateQueryDefinition(StatisticQueryDefinitionDTO queryDefinition); + void reloadComponents(); - public StatisticQueryDefinitionDTO getQueryDefinition(); + StatisticQueryDefinitionDTO getQueryDefinition(); + Iterable validateQueryDefinition(StatisticQueryDefinitionDTO queryDefinition); + void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition); + + void queryDefinitionChangesHaveBeenStored(); - public void addQueryDefinitionChangedListener(QueryDefinitionChangedListener listener); - - public void removeQueryDefinitionChangedListener(QueryDefinitionChangedListener listener); + void addQueryDefinitionChangedListener(QueryDefinitionChangedListener listener); + void removeQueryDefinitionChangedListener(QueryDefinitionChangedListener listener); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryRunner.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryRunner.java index 8c2b55e3be0..b0fbd119ff4 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryRunner.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/QueryRunner.java @@ -8,6 +8,4 @@ public interface QueryRunner extends QueryDefinitionChangedListener, Component extends Component { - public QueryResultDTO getCurrentResult(); + /** + * @return The currently displayed result or null, if no result is shown. + */ + QueryResultDTO getCurrentResult(); + + /** + * @return The query definition of the currently displayed result or null, + * if not result is shown or no query definition has been set. + */ + StatisticQueryDefinitionDTO getCurrentQueryDefinition(); - public void showResult(QueryResultDTO result); - - public void showError(String error); - - public void showError(String mainError, Iterable detailedErrors); - - public void showBusyIndicator(); + /** + * Displays the given result for the given query definition. The given result may be + * null to clear the presenter. The given query definition may be null, + * but this is discouraged unless the result is also null. + * + * @param queryDefinition The query definition of the results to display + * @param result The results to display + */ + void showResult(StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result); + + void showError(String error); + void showError(String mainError, Iterable detailedErrors); + void showBusyIndicator(); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ResultsPresenterWithControls.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ResultsPresenterWithControls.java deleted file mode 100644 index dfc2ae967b9..00000000000 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/ResultsPresenterWithControls.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.sap.sse.datamining.ui.client; - -import com.sap.sse.common.settings.Settings; - -public interface ResultsPresenterWithControls - extends ResultsPresenter, WithControls { - -} diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java index 9a5d2bb0314..8d758090476 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.java @@ -24,7 +24,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String useClassGetName(); String useStringLiteralsTooltip(); String queryDefinitionViewer(); - String runAsSubstantive(); + String run(); String queryNotValidBecause(); String queryRunner(); String runAQuery(); @@ -46,6 +46,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String noDimensionToGroupBySelectedError(); String noDataRetrieverChainDefinitonSelectedError(); String noStatisticSelectedError(); + String noAggregatorSelectedError(); String calculateThe(); String statisticProvider(); String groupingProvider(); @@ -65,5 +66,34 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String filter(); String filterShownDimensions(); String selectDimensionsToFilterBy(); + String clear(); + String changesWillBeLost(); + String confirmQueryDefinitionChangeLoss(); + String discardChanges(); + String keepChanges(); + String anErrorOccurredWhileApplyingTheQuery(); + String queryBasedOnRetrieverChainCanNotBeApplied(String retrieverChainName); + String statisticNotAvailable(String statisticName); + String aggregatorNotAvailable(String aggregatorName); + String filterDimensionsAreNotAvailable(String listedDimensions); + String filterValuesOfDimensionAreNotAvailable(String dimensionName, String listedValues); + String groupingDimensionsAreNotAvailable(String listedDimensions); + String rememberDecisionCanBeChangedInSettings(); + String changeLossStrategy(); + String changeLossStrageyTooltip(); + String askChangeLossStrategyName(); + String askChangeLossStrategyTooltip(); + String discardChangesChangeLossStrategyName(); + String discardChangesChangeLossStrategyTooltip(); + String keepChangesChangeLossStrategyName(); + String keepChangesChangeLossStrategyTooltip(); + String applyingQuery(); + String searchAvailableStatistics(); + String reloadComponents(); + String any(); + String of(); + String the(); + String and(); + String followingStatisticsAreNotSupportedByAggregatorWarning(); } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties index 522283d4f2b..114e6fb3123 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages.properties @@ -8,8 +8,11 @@ useClassGetNameTooltip=More robust against changes in the code base, but the cod useClassGetName=Use Class.getName() for type names useStringLiteralsTooltip=The code snippet can be used anywhere, but it will break if the code base changes. queryDefinitionViewer=Query Definition Viewer -runAsSubstantive=Run -queryNotValidBecause=No query possible, because +run=Run +any=Any +of=of +the=the +queryNotValidBecause=Query not possible, because queryRunner=Query Runner runAQuery=Run a Query runningQuery=Running Query @@ -30,6 +33,7 @@ dataMiningComponentsNeedReloadDialogMessage=Click Reload to reload the component noDimensionToGroupBySelectedError=No dimension to group by is selected noDataRetrieverChainDefinitonSelectedError=No data retriever is selected noStatisticSelectedError=No statistic to calculate is selected +noAggregatorSelectedError=No aggregator is selected calculateThe=Calculate the statisticProvider=Statistic Provider groupingProvider=Grouping Provider @@ -48,4 +52,30 @@ search=Search csvCopiedToClipboard=CSV content has been copied to clipboard. filter=Filter filterShownDimensions=Filter shown dimensions -selectDimensionsToFilterBy=Select dimensions to filter by \ No newline at end of file +selectDimensionsToFilterBy=Select dimensions to filter by +clear=Clear +changesWillBeLost=Changes may be lost +confirmQueryDefinitionChangeLoss=A different query will be displayed and there are changes that would be lost.\nClick Discard Changes to display the query, which will overwrite your selection.\nClick Keep Changes to keep your current selection, which would prevent the other query from being displayed. +discardChanges=Discard Changes +keepChanges=Keep Changes +anErrorOccurredWhileApplyingTheQuery=An error occurred while applying the query +queryBasedOnRetrieverChainCanNotBeApplied=The query based on {0} can not be applied +statisticNotAvailable=The statistic {0} is not available +aggregatorNotAvailable=The aggregator {0} is not available +filterDimensionsAreNotAvailable=The following filter dimensions aren''t available: {0} +filterValuesOfDimensionAreNotAvailable=The following filter values of the dimension {0} aren''t available: {1} +groupingDimensionsAreNotAvailable=The following grouping dimensions aren''t available: {0} +rememberDecisionCanBeChangedInSettings=Remember decision (can be changed in the settings) +changeLossStrategy=Change Loss Strategy +changeLossStrageyTooltip=Strategy for the possible loss of changes, when a query should be displayed +askChangeLossStrategyName=Ask +askChangeLossStrategyTooltip=Asks for the strategy to use with a dialog +discardChangesChangeLossStrategyName=Discard Changes +discardChangesChangeLossStrategyTooltip=Display the query overwriting the current selection +keepChangesChangeLossStrategyName=Keep Changes +keepChangesChangeLossStrategyTooltip=Keep current selection preventing the query to be displayed +applyingQuery=Applying Query +searchAvailableStatistics=Search the available statistics +reloadComponents=Reload Components +and=and +followingStatisticsAreNotSupportedByAggregatorWarning=The following statistics aren''t supported by the current aggregator.\nSelecting one of them will reset it. \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties index 7fd0a304851..6cf5d3f2be5 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_de.properties @@ -8,8 +8,11 @@ useClassGetNameTooltip=Robuster gegnüber Änderungen im Code, aber der Code Sni useClassGetName=Verwende Class.getName() für Typnamen useStringLiteralsTooltip=Der Code Snippet kann überall verwendet werden, wird aber nicht mehr funktionieren, wenn sich der Code ändert. queryDefinitionViewer=Query Definition Anzeiger -runAsSubstantive=Durchlauf -queryNotValidBecause=Keine Datenabfrage möglich, da +run=Ausführen +any=Beliebig +of=von +the=der +queryNotValidBecause=Keine Datenabfrage möglich, denn queryRunner=Abfrageverarbeiter runAQuery=Führe eine Anfrage aus runningQuery=Datenabfrage läuft @@ -24,12 +27,12 @@ plainResultsPresenter=Einfacher Ergebnis-Darsteller multiResultsPresenter=Multi Ergebnis-Darsteller columnChart=Säulendiagramm columnChartWithErrorBars=Säulendiagramm mit Fehlerbalken -errorFetchingComponentsChangedTimepoint=Error fetching components changed timepoint from the server: {0} dataMiningComponentsHaveBeenUpdated=Die Data-Mining Komponenten wurden aktualisiert dataMiningComponentsNeedReloadDialogMessage=Klicke auf Neu Laden, um die Komponenten jetzt neu zu laden. Die aktuell angezeigten Daten gehen dabei verloren und eine Standard-Query wird ausgeführt.\nKlicke auf Schließen, um nichts zu tun. Das Data-Mining wird nicht korrekt funktionieren, bis die Komponenten neu geladen wurden. -noDimensionToGroupBySelectedError=Keine Gruppierungs-Dimension ausgewählt ist -noDataRetrieverChainDefinitonSelectedError=Kein Datenbeschaffer ausgewählt ist -noStatisticSelectedError=Keine zu berechnende Kennzahl ausgewählt ist +noDimensionToGroupBySelectedError=Keine Gruppierungs-Dimension wurde ausgewählt +noDataRetrieverChainDefinitonSelectedError=Kein Datenbeschaffer wurde ausgewählt +noStatisticSelectedError=Keine zu berechnende Kennzahl wurde ausgewählt +noAggregatorSelectedError=Kein Aggregator wurde ausgewählt calculateThe=Berechne die statisticProvider=Statistik-Versorger groupingProvider=Gruppierungs-Versorger @@ -48,4 +51,30 @@ search=Suche csvCopiedToClipboard=Der Inhalt wurde als CSV in die Zwischenablage kopiert. filter=Filter filterShownDimensions=Angezeigte Dimensionen filtern -selectDimensionsToFilterBy=Dimensionen der Filterung auswählen \ No newline at end of file +selectDimensionsToFilterBy=Dimensionen der Filterung auswählen +clear=Löschen +changesWillBeLost=Änderungen können verloren gehen +confirmQueryDefinitionChangeLoss=Eine andere Query soll angezeigt werden und es gibt Änderungen die verloren gehen würden.\nKlicke auf Änderungen Verwerfen, um die Query anzuzeigen, wodurch die aktuelle Selektion überschrieben wird.\nKlicke auf Änderungen Behalten, um die aktuelle Selektion zu behalten, wodurch die Query nicht angezeigt werden würde. +discardChanges=Änderungen Verwerfen +keepChanges=Änderungen Behalten +anErrorOccurredWhileApplyingTheQuery=Beim Anzeigen der Query ist ein Fehler aufgetreten +queryBasedOnRetrieverChainCanNotBeApplied=Die Query basierend auf {0} kann nicht angezeigt werden +statisticNotAvailable=Die Statistik {0} ist nicht verfügbar +aggregatorNotAvailable=Der Aggregator {0} ist nicht verfügbar +filterDimensionsAreNotAvailable=Die folgenden Filter-Dimensionen sind nicht verfügbar: {0} +filterValuesOfDimensionAreNotAvailable=Der folgenden Filter-Werte der Dimension {0} sind nicht verfügbar: {1} +groupingDimensionsAreNotAvailable=Die folgenden Gruppierungs-Dimensionen sind nicht verfügbar: {0} +rememberDecisionCanBeChangedInSettings=Entscheidung speichern (kann in den Einstellungen geändert werden) +changeLossStrategy=Verlust von Änderungen +changeLossStrageyTooltip=Verhalten beim möglichen Verlust von Änderungen, wenn eine andere Query angezeigt werden soll +askChangeLossStrategyName=Nachfragen +askChangeLossStrategyTooltip=Erfragen der zu verwendenden Strategie mit einem Dialog +discardChangesChangeLossStrategyName=Änderungen Verwerfen +discardChangesChangeLossStrategyTooltip=Anzeigen der Query, wodurch die aktuelle Selektion überschrieben wird +keepChangesChangeLossStrategyName=Änderungen Behalten +keepChangesChangeLossStrategyTooltip=Aktuelle Selektion zu behalten, wodurch die Query nicht angezeigt wird +applyingQuery=Query wird angezeigt +searchAvailableStatistics=Verfügbare Statistiken durchsuchen +reloadComponents=Kompnenten neu laden +and=und +followingStatisticsAreNotSupportedByAggregatorWarning=Die nachfolgenden Statistiken werden von dem aktuellen Aggregator nicht unterstützt.\nDieser wird zurückgesetzt, wenn eine davon ausgewählt wird. \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_es.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_es.properties index 2ba0e57b335..f78a0255fc9 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_es.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_es.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=Más resistente frente a modificaciones en la base de có useClassGetName=Utilizar Class.getName() para nombres de tipo useStringLiteralsTooltip=El fragmento de código puede utilizarse en todas las ubicaciones, pero se romperá si se modifica la base de código. queryDefinitionViewer=Visor de definición de consulta -runAsSubstantive=Ejecutar +run=Ejecutar queryNotValidBecause=No se puede hacer ninguna consulta porque queryRunner=Ejecutor de la consulta runAQuery=Realice una consulta @@ -41,4 +41,4 @@ queryDefinitionProvider=Proveedor de definición de consultas dataMiningRetrieval=Recuperación de datos tabbedResultsPresenter=Presentador de resultados por pestañas useStringLiterals=Utilizar literales de cadena para los nombres de tipo -numberPairResultsPresenter=Diagrama de dispersión +numberPairResultsPresenter=Diagrama de dispersi�n diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_fr.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_fr.properties index f07d1ec786c..ac6c097da31 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_fr.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_fr.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=Plus stable face aux modifications dans la base de code, useClassGetName=Utilisez Class.getName() pour les noms des types. useStringLiteralsTooltip=Le fichier de script peut être utilisé partout, mais sera corrompu si la base de code change. queryDefinitionViewer=Visualiseur de définitions de requête -runAsSubstantive=Exécuter +run=Exécuter queryNotValidBecause=Aucune requête possible dû à queryRunner=Outil d''exécution de requêtes runAQuery=Exécuter une requête diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ja.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ja.properties index 0834032cbd8..58136f0bfdb 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ja.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ja.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=コードベースでの変更に対してより堅牢で useClassGetName=タイプ名に class.getName() を使用 useStringLiteralsTooltip=このコードスニペットはどこでも使用できますが、コードベースが変更される場合は中断します。 queryDefinitionViewer=クエリ定義ビューア -runAsSubstantive=実行 +run=実行 queryNotValidBecause=クエリが可能でありません。理由: queryRunner=クエリランナー runningQuery=クエリ実行中 diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_pt.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_pt.properties index 78e3dc9bbdf..09a6e9fa375 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_pt.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_pt.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=Mais robusto em relação às modificações na base do c useClassGetName=Utilizar Class.getName() para nomes de tipo useStringLiteralsTooltip=O trecho do código pode ser utilizado em qualquer local, mas será quebrado se a base do código for modificada. queryDefinitionViewer=Visualizador de definição da consulta -runAsSubstantive=Competir +run=Competir queryNotValidBecause=Nenhuma consulta possível, pois queryRunner=Executor de consulta runAQuery=Executar uma consulta @@ -41,4 +41,4 @@ queryDefinitionProvider=Fornecedor da definição da consulta dataMiningRetrieval=Obtenção de dados tabbedResultsPresenter=Apresentador de resultados por fichas useStringLiterals=Utilizar literais de cadeia para nomes de tipo -numberPairResultsPresenter=Diagrama de dispersão +numberPairResultsPresenter=Diagrama de dispers�o diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ru.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ru.properties index f04a28fcbee..2912fb25851 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_ru.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=Более надежно по сравнению с из useClassGetName=Использовать Class.getName() для имен типов useStringLiteralsTooltip=Фрагмент кода может использоваться где угодно, но будет нарушен в случае изменения основания кода. queryDefinitionViewer=Средство просмотра определения запроса -runAsSubstantive=Выполнить +run=Выполнить queryNotValidBecause=Запрос невозможен, так как queryRunner=Выполнение запроса runAQuery=Выполнить запрос diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_zh.properties b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_zh.properties index 0d098962718..64d0ea34c61 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_zh.properties +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/StringMessages_zh.properties @@ -8,7 +8,7 @@ useClassGetNameTooltip=更灵活应对代码库的更改,但是代码片段只 useClassGetName=将 Class.getName() 用于类型名称 useStringLiteralsTooltip=代码片段可在任意位置使用,当如果基本代码更改,其将中断。 queryDefinitionViewer=查询定义查看器 -runAsSubstantive=运行 +run=运行 queryNotValidBecause=无法查询,原因: queryRunner=查询执行器 runAQuery=运行查询 diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/PredefinedQueryRunner.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/PredefinedQueryRunner.java index 9c3cb8247a0..bf6a7b7ddcb 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/PredefinedQueryRunner.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/PredefinedQueryRunner.java @@ -1,9 +1,9 @@ package com.sap.sse.datamining.ui.client.developer; -import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.function.Consumer; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -17,11 +17,10 @@ import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.Util; -import com.sap.sse.datamining.shared.DataMiningSession; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier; -import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; -import com.sap.sse.datamining.ui.client.ResultsPresenter; +import com.sap.sse.datamining.ui.client.QueryDefinitionProvider; import com.sap.sse.datamining.ui.client.StringMessages; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.controls.busyindicator.SimpleBusyIndicator; @@ -32,11 +31,11 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class PredefinedQueryRunner extends ComponentWithoutSettings { - private final DataMiningSession session; private final StringMessages stringMessages; private final DataMiningServiceAsync dataMiningService; private final ErrorReporter errorReporter; - private final ResultsPresenter resultsPresenter; + private final QueryDefinitionProvider queryDefinitionProvider; + private final Consumer queryRunner; private final Button showDialogButton; private final DialogBox dialogBox; @@ -44,15 +43,15 @@ public class PredefinedQueryRunner extends ComponentWithoutSettings { private final ValueListBox selectionListBox; private final Button runButton; - public PredefinedQueryRunner(Component parent, ComponentContext context, DataMiningSession session, - StringMessages stringMessages, DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, - ResultsPresenter resultsPresenter) { + public PredefinedQueryRunner(Component parent, ComponentContext context, StringMessages stringMessages, + DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, + QueryDefinitionProvider queryDefinitionProvider, Consumer queryRunner) { super(parent, context); - this.session = session; this.stringMessages = stringMessages; this.dataMiningService = dataMiningService; this.errorReporter = errorReporter; - this.resultsPresenter = resultsPresenter; + this.queryDefinitionProvider = queryDefinitionProvider; + this.queryRunner = queryRunner; showDialogButton = new Button(stringMessages.runPredefinedQuery(), new ClickHandler() { @Override @@ -62,7 +61,7 @@ public class PredefinedQueryRunner extends ComponentWithoutSettings { }); dialogBox = new DialogBox(false, true); - dialogBox.setText(stringMessages.viewQueryDefinition()); + dialogBox.setText(stringMessages.runPredefinedQuery()); dialogBox.setAnimationEnabled(true); Button closeButton = new Button(stringMessages.close(), new ClickHandler() { @@ -140,18 +139,16 @@ public class PredefinedQueryRunner extends ComponentWithoutSettings { protected void runSelectedPredefinedQuery() { PredefinedQueryIdentifier predefinedQueryIdentifier = selectionListBox.getValue(); - resultsPresenter.showBusyIndicator(); - dataMiningService.runPredefinedQuery(session, predefinedQueryIdentifier, - LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback>() { + dataMiningService.getPredefinedQueryDefinition(predefinedQueryIdentifier, + LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback() { @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error running the query: " + caught.getMessage()); - resultsPresenter.showError(stringMessages.errorRunningDataMiningQuery() + "."); + public void onSuccess(StatisticQueryDefinitionDTO queryDefinition) { + queryDefinitionProvider.applyQueryDefinition(queryDefinition); + queryRunner.accept(queryDefinition); } - @Override - public void onSuccess(QueryResultDTO result) { - resultsPresenter.showResult(result); + public void onFailure(Throwable error) { + errorReporter.reportError("Error running the query: " + error.getMessage()); } }); dialogBox.hide(); diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionParser.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionParser.java index 078a23db696..f06199e6189 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionParser.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionParser.java @@ -23,6 +23,9 @@ import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinition public class QueryDefinitionParser { + private static final String NullComponentText = "None selected"; + private static final String NullComponentCode = "UNDEFINED"; + public SafeHtml parseToDetailsAsSafeHtml(StatisticQueryDefinitionDTO queryDefinition) { String safeHtml = parseToDetails(queryDefinition, new HtmlBuilder()); return new OnlyToBeUsedInGeneratedCodeStringBlessedAsSafeHtml(safeHtml); @@ -33,24 +36,34 @@ public class QueryDefinitionParser { } private String parseToDetails(StatisticQueryDefinitionDTO queryDefinition, Builder builder) { - builder.appendText("Locale: " + queryDefinition.getLocaleInfoName()).appendLineBreak(); + if (queryDefinition == null) { + return ""; + } + + builder.appendText("Locale: " + queryDefinition.getLocaleInfoName()) + .appendLineBreak().appendLineBreak(); DataRetrieverChainDefinitionDTO chainDefinition = queryDefinition.getDataRetrieverChainDefinition(); - builder.appendLineBreak() - .appendText("Retrieval: " + chainDefinition).appendLineBreak() - .appendTab(1).appendText("Levels: ").appendLineBreak(); - for (int levelIndex = 0; levelIndex < chainDefinition.getLevelAmount(); levelIndex++) { - builder.appendTab(2).appendText(levelIndex + ": " + chainDefinition.getRetrieverLevel(levelIndex)).appendLineBreak(); + builder.appendText("Retrieval: "); + if (chainDefinition != null) { + builder.appendText(chainDefinition.toString()).appendLineBreak() + .appendTab(1).appendText("Levels: ").appendLineBreak(); + for (int levelIndex = 0; levelIndex < chainDefinition.getLevelAmount(); levelIndex++) { + builder.appendTab(2).appendText(levelIndex + ": " + chainDefinition.getRetrieverLevel(levelIndex)).appendLineBreak(); + } + } else { + builder.appendText(NullComponentText).appendLineBreak(); } + builder.appendLineBreak(); HashMap retrieverSettings = queryDefinition.getRetrieverSettings(); - if (!retrieverSettings.isEmpty()) { + if (retrieverSettings != null && !retrieverSettings.isEmpty()) { // TODO Display retriever settings } HashMap>> filterSelection = queryDefinition.getFilterSelection(); - if (!filterSelection.isEmpty()) { - builder.appendLineBreak().appendText("Filter Selection:").appendLineBreak(); + if (filterSelection != null && !filterSelection.isEmpty()) { + builder.appendText("Filter Selection:").appendLineBreak(); List retrieverLevels = new ArrayList<>(filterSelection.keySet()); Collections.sort(retrieverLevels); for (DataRetrieverLevelDTO retrieveLevel : retrieverLevels) { @@ -70,18 +83,25 @@ public class QueryDefinitionParser { builder.appendLineBreak(); } } + builder.appendLineBreak(); } - builder.appendLineBreak() - .appendText("Group By:").appendLineBreak(); ArrayList dimensionsToGroupBy = queryDefinition.getDimensionsToGroupBy(); - for (int index = 0; index < dimensionsToGroupBy.size(); index++) { - builder.appendTab(1).appendText(index + ": " + dimensionsToGroupBy.get(index)).appendLineBreak(); + builder.appendText("Group By: "); + if (dimensionsToGroupBy != null && !dimensionsToGroupBy.isEmpty()) { + builder.appendLineBreak(); + for (int index = 0; index < dimensionsToGroupBy.size(); index++) { + builder.appendTab(1).appendText(index + ": " + dimensionsToGroupBy.get(index)).appendLineBreak(); + } + } else { + builder.appendText(NullComponentText).appendLineBreak(); } + builder.appendLineBreak(); - builder.appendLineBreak() - .appendText("Statistic: " + queryDefinition.getStatisticToCalculate()).appendLineBreak(); - builder.appendText("Aggregator: " + queryDefinition.getAggregatorDefinition()); + FunctionDTO statistic = queryDefinition.getStatisticToCalculate(); + builder.appendText("Statistic: " + (statistic == null ? NullComponentText : statistic)).appendLineBreak(); + AggregationProcessorDefinitionDTO aggregator = queryDefinition.getAggregatorDefinition(); + builder.appendText("Aggregator: " + (aggregator == null ? NullComponentText : aggregator)); return builder.toString(); } @@ -96,6 +116,10 @@ public class QueryDefinitionParser { } private String parseToCode(StatisticQueryDefinitionDTO queryDefinition, TypeToCodeStrategy typeStrategy, Builder builder) { + if (queryDefinition == null) { + return ""; + } + String functionClassName = FunctionDTO.class.getSimpleName(); String aggregatorClassName = AggregationProcessorDefinitionDTO.class.getSimpleName(); String retrieverLevelClassName = DataRetrieverLevelDTO.class.getSimpleName(); @@ -106,96 +130,115 @@ public class QueryDefinitionParser { String hashMapClassName = HashMap.class.getSimpleName(); String hashSetClassName = HashSet.class.getSimpleName(); String serializableClassName = Serializable.class.getSimpleName(); - - DataRetrieverChainDefinitionDTO retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); // Extraction Function String statisticVariable = "statistic"; - if (statisticVariable != null && queryDefinition.getStatisticToCalculate() != null) { - builder.appendText(functionToCode(statisticVariable, queryDefinition.getStatisticToCalculate(), typeStrategy)); - } else { - builder.appendText("null"); - } + builder.appendText(functionToCode(statisticVariable, queryDefinition.getStatisticToCalculate(), typeStrategy)); builder.appendLineBreak(); - // Aggregator Definition String aggregatorVariable = "aggregator"; AggregationProcessorDefinitionDTO aggregator = queryDefinition.getAggregatorDefinition(); + builder.appendText(aggregatorClassName + " " + aggregatorVariable + " = "); if (aggregator != null) { - builder.appendText(aggregatorClassName + " " + aggregatorVariable + " = new " + aggregatorClassName + "(" + literal(aggregator.getMessageKey()) + ", " + - typeStrategy.toCode(aggregator.getExtractedTypeName()) + ", " + typeStrategy.toCode(aggregator.getAggregatedTypeName()) + ", \"\");"); + builder.appendText("new " + aggregatorClassName + "(" + literal(aggregator.getMessageKey()) + ", " + + typeStrategy.toCode(aggregator.getExtractedTypeName()) + ", " + + typeStrategy.toCode(aggregator.getAggregatedTypeName()) + ", \"\")"); } else { - builder.appendText("null"); + builder.appendText(NullComponentCode); + } + builder.appendText(";").appendLineBreak().appendLineBreak(); + + // Retriever Levels and Retriever Chain Definition + DataRetrieverChainDefinitionDTO retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); + String retrieverChainVariable = "retrieverChain"; + + if (retrieverChain != null) { + String retrieverLevelsVariable = "retrieverLevels"; + builder.appendText(arrayListClassName + "<" + retrieverLevelClassName + "> " + retrieverLevelsVariable + + " = new " + arrayListClassName + "<>();").appendLineBreak(); + for (DataRetrieverLevelDTO retrieverLevel : retrieverChain.getRetrieverLevels()) { + LocalizedTypeDTO retrievedType = retrieverLevel.getRetrievedDataType(); + String retrievedTypeAsCode = "new " + localizedTypeClassName + "(" + + typeStrategy.toCode(retrievedType.getTypeName()) + ", \"\")"; + String retrieverSettingsAsCode = "null"; + // TODO Handle the retriever level settings + builder.appendText(retrieverLevelsVariable + ".add(new " + retrieverLevelClassName + "(" + + retrieverLevel.getLevel() + ", " + typeStrategy.toCode(retrieverLevel.getRetrieverTypeName()) + + ", " + retrievedTypeAsCode + ", " + retrieverSettingsAsCode + "));").appendLineBreak(); + } + builder.appendText(retrieverChainClassName + " " + retrieverChainVariable + " = new " + + retrieverChainClassName + "(\"\", " + typeStrategy.toCode(retrieverChain.getDataSourceTypeName()) + + ", " + retrieverLevelsVariable + ");"); + } else { + builder.appendText(retrieverChainClassName + " " + retrieverChainVariable + " = " + NullComponentCode + ";"); } builder.appendLineBreak().appendLineBreak(); - // Retriever Levels and Retriever Chain Definition - String retrieverLevelsVariable = "retrieverLevels"; - builder.appendText(arrayListClassName + "<" + retrieverLevelClassName + "> " + retrieverLevelsVariable + " = new " + arrayListClassName + "<>();").appendLineBreak(); - for (DataRetrieverLevelDTO retrieverLevel : retrieverChain.getRetrieverLevels()) { - LocalizedTypeDTO retrievedType = retrieverLevel.getRetrievedDataType(); - String retrievedTypeAsCode = "new " + localizedTypeClassName + "(" + typeStrategy.toCode(retrievedType.getTypeName()) + ", " + literal(retrievedType.getDisplayName()) + ")"; - String retrieverSettingsAsCode = "null"; - // TODO Handle the retriever level settings - builder.appendText(retrieverLevelsVariable + ".add(new " + retrieverLevelClassName + "(" + retrieverLevel.getLevel() + ", " + - typeStrategy.toCode(retrieverLevel.getRetrieverTypeName()) + ", " + retrievedTypeAsCode + ", " + retrieverSettingsAsCode + "));").appendLineBreak(); - } - String retrieverChainVariable = "retrieverChain"; - builder.appendText(retrieverChainClassName + " " + retrieverChainVariable + " = new " + retrieverChainClassName + "(\"\", " + - typeStrategy.toCode(retrieverChain.getDataSourceTypeName()) + ", " + retrieverLevelsVariable + ");").appendLineBreak() - .appendLineBreak(); - // Query Definition instantiation String queryDefinitionVariable = "queryDefinition"; - builder.appendText(queryDefinitionClassName + " " + queryDefinitionVariable + " = new " + queryDefinitionClassName + "(" + literal(queryDefinition.getLocaleInfoName()) + - ", " + statisticVariable + ", " + aggregatorVariable + ", " + retrieverChainVariable + ");").appendLineBreak() - .appendLineBreak(); + builder.appendText(queryDefinitionClassName + " " + queryDefinitionVariable + " = new " + + queryDefinitionClassName + "(" + literal(queryDefinition.getLocaleInfoName()) + ", " + + statisticVariable + ", " + aggregatorVariable + ", " + retrieverChainVariable + ");") + .appendLineBreak().appendLineBreak(); // Filter Selection per Retriever Level separated by two line breaks - for (DataRetrieverLevelDTO retrieverLevel : retrieverChain.getRetrieverLevels()) { - HashMap> levelFilterSelection = queryDefinition.getFilterSelection().get(retrieverLevel); - if (levelFilterSelection != null && !levelFilterSelection.isEmpty()) { - String levelFilterSelectionVariable = "retrieverlevel" + retrieverLevel.getLevel() + "_FilterSelection"; - builder.appendText(hashMapClassName + "<" + functionClassName + ", " + hashSetClassName + "> " + levelFilterSelectionVariable + " = new " + hashMapClassName + "<>();").appendLineBreak(); - - int filterDimensionCounter = 0; - for (FunctionDTO filterDimension : levelFilterSelection.keySet()) { - String filterDimensionVariable = "filterDimension" + filterDimensionCounter; - builder.appendText(functionToCode(filterDimensionVariable, filterDimension, typeStrategy)).appendLineBreak(); - - String dimensionFilterSelectionVariable = filterDimensionVariable + "_Selection"; - builder.appendText(hashSetClassName + "<" + serializableClassName + "> " + dimensionFilterSelectionVariable + - " = new " + hashSetClassName + "<>();").appendLineBreak(); - for (Serializable filterValue : levelFilterSelection.get(filterDimension)) { - builder.appendText(dimensionFilterSelectionVariable + ".add(" + literal(filterValue) + ");").appendLineBreak(); + HashMap>> filterSelection = queryDefinition.getFilterSelection(); + if (filterSelection != null && retrieverChain != null) { + for (DataRetrieverLevelDTO retrieverLevel : retrieverChain.getRetrieverLevels()) { + HashMap> levelFilterSelection = filterSelection.get(retrieverLevel); + if (levelFilterSelection != null && !levelFilterSelection.isEmpty()) { + String levelFilterSelectionVariable = "retrieverlevel" + retrieverLevel.getLevel() + "_FilterSelection"; + builder.appendText(hashMapClassName + "<" + functionClassName + ", " + hashSetClassName + + "> " + levelFilterSelectionVariable + " = new " + + hashMapClassName + "<>();").appendLineBreak(); + + int filterDimensionCounter = 0; + for (FunctionDTO filterDimension : levelFilterSelection.keySet()) { + String filterDimensionVariable = "filterDimension" + filterDimensionCounter; + builder.appendText(functionToCode(filterDimensionVariable, filterDimension, typeStrategy)) + .appendLineBreak(); + + String dimensionFilterSelectionVariable = filterDimensionVariable + "_Selection"; + builder.appendText(hashSetClassName + "<" + serializableClassName + "> " + + dimensionFilterSelectionVariable + " = new " + hashSetClassName + "<>();") + .appendLineBreak(); + for (Serializable filterValue : levelFilterSelection.get(filterDimension)) { + builder.appendText(dimensionFilterSelectionVariable + ".add(" + literal(filterValue) + ");") + .appendLineBreak(); + } + + builder.appendText(levelFilterSelectionVariable + ".put(" + filterDimensionVariable + ", " + + dimensionFilterSelectionVariable + ");").appendLineBreak(); + filterDimensionCounter++; } - - builder.appendText(levelFilterSelectionVariable + ".put(" + filterDimensionVariable + ", " + dimensionFilterSelectionVariable + ");").appendLineBreak(); - filterDimensionCounter++; + + builder.appendText(queryDefinitionVariable + ".setFilterSelectionFor(" + retrieverChainVariable + + ".getRetrieverLevel(" + retrieverLevel.getLevel() + "), " + levelFilterSelectionVariable + + ");").appendLineBreak().appendLineBreak(); } - - builder.appendText(queryDefinitionVariable + ".setFilterSelectionFor(" + retrieverChainVariable + ".getRetrieverLevel(" + - retrieverLevel.getLevel() + "), " + levelFilterSelectionVariable + ");").appendLineBreak() - .appendLineBreak(); - } - - } - // Dimensions to Group By separated by two line breaks - int dimensionToGroupByCounter = 0; - boolean first = true; - for (FunctionDTO dimensionToGroupBy : queryDefinition.getDimensionsToGroupBy()) { - if (!first) { - builder.appendLineBreak().appendLineBreak(); } - - String dimensionToGroupByVariable = "dimensionToGroupBy" + dimensionToGroupByCounter; - builder.appendText(functionToCode(dimensionToGroupByVariable, dimensionToGroupBy, typeStrategy)).appendLineBreak(); - builder.appendText(queryDefinitionVariable + ".appendDimensionToGroupBy(" + dimensionToGroupByVariable + ");"); - - first = false; - dimensionToGroupByCounter++; + } + + // Dimensions to Group By separated by two line breaks + ArrayList dimensionsToGroupBy = queryDefinition.getDimensionsToGroupBy(); + if (dimensionsToGroupBy != null) { + int dimensionToGroupByCounter = 0; + boolean first = true; + for (FunctionDTO dimensionToGroupBy : dimensionsToGroupBy) { + if (!first) { + builder.appendLineBreak().appendLineBreak(); + } + + String dimensionToGroupByVariable = "dimensionToGroupBy" + dimensionToGroupByCounter; + builder.appendText(functionToCode(dimensionToGroupByVariable, dimensionToGroupBy, typeStrategy)) + .appendLineBreak(); + builder.appendText( + queryDefinitionVariable + ".appendDimensionToGroupBy(" + dimensionToGroupByVariable + ");"); + + first = false; + dimensionToGroupByCounter++; + } } return builder.toString(); @@ -203,9 +246,15 @@ public class QueryDefinitionParser { private String functionToCode(String functionVariable, FunctionDTO function, TypeToCodeStrategy typeStrategy) { String functionClassName = FunctionDTO.class.getSimpleName(); - return functionClassName + " " + functionVariable + " = new " + functionClassName + "(" + function.isDimension() + ", " + literal(function.getFunctionName()) + ", " + - typeStrategy.toCode(function.getSourceTypeName()) + ", " + typeStrategy.toCode(function.getReturnTypeName()) + ", " + - functionParametersToCode(function, typeStrategy) + ", \"\", 0);"; + String functionConstruction; + if (function != null) { + functionConstruction = "new " + functionClassName + "(" + function.isDimension() + ", " + literal(function.getFunctionName()) + ", " + + typeStrategy.toCode(function.getSourceTypeName()) + ", " + typeStrategy.toCode(function.getReturnTypeName()) + ", " + + functionParametersToCode(function, typeStrategy) + ", \"\", 0)"; + } else { + functionConstruction = NullComponentCode; + } + return functionClassName + " " + functionVariable + " = " + functionConstruction + ";"; } private String functionParametersToCode(FunctionDTO function, TypeToCodeStrategy typeStrategy) { @@ -214,7 +263,6 @@ public class QueryDefinitionParser { return "new " + ArrayList.class.getSimpleName() + "<" + String.class.getSimpleName() + ">()"; } - Arrays.asList("Test", "test"); StringBuilder builder = new StringBuilder(Arrays.class.getSimpleName() + ".asList("); boolean first = true; for (String parameterTypeName : parameterTypeNames) { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionViewer.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionViewer.java index 3b27225bf42..17949fbbffe 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionViewer.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/developer/QueryDefinitionViewer.java @@ -37,6 +37,7 @@ public class QueryDefinitionViewer extends ComponentWithoutSettings implements Q private final HTML codeHtml; private final RadioButton useClassGetNameRadioButton; private final RadioButton useStringLiteralsRadioButton; + private final Button copyToClipboardButton; private StatisticQueryDefinitionDTO currentDefinition; private boolean active; @@ -74,7 +75,7 @@ public class QueryDefinitionViewer extends ComponentWithoutSettings implements Q codeScrollPanel.getElement().addClassName("queryDefinitionViewerContent"); codeDockPanel.add(codeScrollPanel); - Button copyToClipboardButton = new Button(stringMessages.copyToClipboard(), new ClickHandler() { + copyToClipboardButton = new Button(stringMessages.copyToClipboard(), new ClickHandler() { @Override public void onClick(ClickEvent event) { switch (contentPanel.getSelectedIndex()) { @@ -89,6 +90,7 @@ public class QueryDefinitionViewer extends ComponentWithoutSettings implements Q } } }); + copyToClipboardButton.setEnabled(false); HorizontalPanel controlsPanel = new HorizontalPanel(); controlsPanel.setSpacing(5); @@ -109,6 +111,7 @@ public class QueryDefinitionViewer extends ComponentWithoutSettings implements Q @Override public void queryDefinitionChanged(StatisticQueryDefinitionDTO newQueryDefinition) { currentDefinition = newQueryDefinition; + copyToClipboardButton.setEnabled(currentDefinition != null); if (active) { updateDetails(); updateCode(); @@ -138,11 +141,11 @@ public class QueryDefinitionViewer extends ComponentWithoutSettings implements Q } public void setActive(boolean active) { - if (active && this.active != active) { - updateDetails(); - updateCode(); - } + boolean needsUpdate = !this.active && active; this.active = active; + if (needsUpdate) { + queryDefinitionChanged(currentDefinition); + } } @Override diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/execution/SimpleQueryRunner.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/execution/SimpleQueryRunner.java index a2c4f64a0e0..01f485201c7 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/execution/SimpleQueryRunner.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/execution/SimpleQueryRunner.java @@ -1,22 +1,24 @@ package com.sap.sse.datamining.ui.client.execution; import java.io.Serializable; +import java.util.Iterator; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.Widget; +import com.sap.sse.common.Util; import com.sap.sse.datamining.shared.DataMiningSession; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; +import com.sap.sse.datamining.ui.client.CompositeResultsPresenter; import com.sap.sse.datamining.ui.client.DataMiningService; import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; import com.sap.sse.datamining.ui.client.ManagedDataMiningQueriesCounter; import com.sap.sse.datamining.ui.client.QueryDefinitionProvider; import com.sap.sse.datamining.ui.client.QueryRunner; -import com.sap.sse.datamining.ui.client.ResultsPresenter; import com.sap.sse.datamining.ui.client.settings.QueryRunnerSettings; import com.sap.sse.datamining.ui.client.settings.QueryRunnerSettingsDialogComponent; import com.sap.sse.gwt.client.ErrorReporter; @@ -48,67 +50,64 @@ public class SimpleQueryRunner extends AbstractDataMiningComponent queryDefinitionProvider; - private final ResultsPresenter resultsPresenter; + private final CompositeResultsPresenter resultsPresenter; private final Button runButton; public SimpleQueryRunner(Component parent, ComponentContext context, DataMiningSession session, - DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, - QueryDefinitionProvider queryDefinitionProvider, ResultsPresenter resultsPresenter) { + DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, + QueryDefinitionProvider queryDefinitionProvider, CompositeResultsPresenter resultsPresenter) { super(parent, context); this.session = session; this.dataMiningService = dataMiningService; this.errorReporter = errorReporter; counter = new SimpleManagedDataMiningQueriesCounter(); + queryDefinitionProvider.addQueryDefinitionChangedListener(this); this.settings = new QueryRunnerSettings(); this.queryDefinitionProvider = queryDefinitionProvider; this.resultsPresenter = resultsPresenter; - runButton = new Button(getDataMiningStringMessages().runAsSubstantive()); + runButton = new Button(getDataMiningStringMessages().run()); runButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - runQuery(); + run(queryDefinitionProvider.getQueryDefinition()); } }); queryReleaseTimer = new Timer() { @Override public void run() { - runQuery(); + SimpleQueryRunner.this.run(queryDefinitionProvider.getQueryDefinition()); } }; - if (this.settings.isRunAutomatically()) { - queryDefinitionProvider.addQueryDefinitionChangedListener(this); - } - } - - @Override - public void runQuery() { - run(queryDefinitionProvider.getQueryDefinition()); + + queryDefinitionChanged(queryDefinitionProvider.getQueryDefinition()); } @Override public void run(StatisticQueryDefinitionDTO queryDefinition) { Iterable errorMessages = queryDefinitionProvider.validateQueryDefinition(queryDefinition); + String presenterId = resultsPresenter.getCurrentPresenterId(); if (errorMessages == null || !errorMessages.iterator().hasNext()) { counter.increase(); - resultsPresenter.showBusyIndicator(); - dataMiningService.runQuery(session, queryDefinition, new ManagedDataMiningQueryCallback(counter) { + resultsPresenter.showBusyIndicator(presenterId); + dataMiningService.runQuery(session, queryDefinition, + new ManagedDataMiningQueryCallback(counter) { + @Override + protected void handleSuccess(QueryResultDTO result) { + resultsPresenter.showResult(presenterId, queryDefinition, result); + queryDefinitionProvider.queryDefinitionChangesHaveBeenStored(); + } @Override protected void handleFailure(Throwable caught) { errorReporter.reportError("Error running the query: " + caught.getMessage()); - resultsPresenter - .showError(getDataMiningStringMessages().errorRunningDataMiningQuery() + "."); - } - - @Override - protected void handleSuccess(QueryResultDTO result) { - resultsPresenter.showResult(result); + resultsPresenter.showError(presenterId, + getDataMiningStringMessages().errorRunningDataMiningQuery() + "."); } }); } else { - resultsPresenter.showError(getDataMiningStringMessages().queryNotValidBecause(), errorMessages); + resultsPresenter.showError(presenterId, getDataMiningStringMessages().queryNotValidBecause(), errorMessages); } } @@ -116,18 +115,25 @@ public class SimpleQueryRunner extends AbstractDataMiningComponent errors = queryDefinitionProvider.validateQueryDefinition(newQueryDefinition); + boolean isValid = Util.isEmpty(errors); + runButton.setEnabled(isValid); + if (isValid) { + runButton.setTitle(null); + if (settings.isRunAutomatically()) { + queryReleaseTimer.schedule(queryBufferTimeInMillis); + } + } else { + Iterator errorsIterator = errors.iterator(); + StringBuilder tooltipBuilder = new StringBuilder(errorsIterator.next()); + errorsIterator.forEachRemaining(e -> tooltipBuilder.append("\n").append(e)); + runButton.setTitle(tooltipBuilder.toString()); + } } @Override diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractResultsPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractResultsPresenter.java index 5e354433def..90a692ca2ee 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractResultsPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/AbstractResultsPresenter.java @@ -3,23 +3,27 @@ package com.sap.sse.datamining.ui.client.presentation; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DeckLayoutPanel; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.settings.Settings; import com.sap.sse.datamining.shared.GroupKey; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; -import com.sap.sse.datamining.ui.client.ResultsPresenterWithControls; +import com.sap.sse.datamining.ui.client.ResultsPresenter; +import com.sap.sse.datamining.ui.client.WithControls; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.settings.ComponentContext; public abstract class AbstractResultsPresenter - extends AbstractDataMiningComponent implements ResultsPresenterWithControls { + extends AbstractDataMiningComponent implements ResultsPresenter, WithControls { private enum ResultsPresenterState { BUSY, ERROR, RESULT @@ -34,6 +38,7 @@ public abstract class AbstractResultsPresenter private final HTML errorLabel; private final HTML labeledBusyIndicator; + private StatisticQueryDefinitionDTO currentQueryDefinition; private QueryResultDTO currentResult; private boolean isCurrentResultSimple; @@ -42,6 +47,7 @@ public abstract class AbstractResultsPresenter mainPanel = new DockLayoutPanel(Unit.PX); controlsPanel = new HorizontalPanel(); controlsPanel.setSpacing(5); + controlsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); mainPanel.addNorth(controlsPanel, 40); Button exportButton = new Button("Export"); exportButton.setEnabled(false); @@ -74,19 +80,18 @@ public abstract class AbstractResultsPresenter } @Override - public void showResult(QueryResultDTO result) { + public void showResult(StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result) { if (result != null && !result.isEmpty()) { if (state != ResultsPresenterState.RESULT) { - mainPanel.setWidgetHidden(controlsPanel, false); + mainPanel.setWidgetHidden(controlsPanel, controlsPanel.getWidgetCount() == 0); presentationPanel.setWidget(getPresentationWidget()); state = ResultsPresenterState.RESULT; } this.currentResult = result; + this.currentQueryDefinition = queryDefinition; updateCurrentResultInfo(); internalShowResults(getCurrentResult()); } else { - this.currentResult = null; - updateCurrentResultInfo(); showError(getDataMiningStringMessages().noDataFound() + "."); } } @@ -97,14 +102,7 @@ public abstract class AbstractResultsPresenter @Override public void showError(String error) { - if (state != ResultsPresenterState.ERROR) { - mainPanel.setWidgetHidden(controlsPanel, true); - errorLabel.setHTML(SafeHtmlUtils.fromString(error).asString()); - state = ResultsPresenterState.ERROR; - } - currentResult = null; - updateCurrentResultInfo(); - presentationPanel.setWidget(errorLabel); + showError(SafeHtmlUtils.fromString(error)); } @Override @@ -114,16 +112,30 @@ public abstract class AbstractResultsPresenter errorBuilder.append("

  • " + detailedError + "
  • "); } errorBuilder.append(""); - showError(errorBuilder.toString()); + showError(SafeHtmlUtils.fromTrustedString(errorBuilder.toString())); + } + + private void showError(SafeHtml error) { + if (state != ResultsPresenterState.ERROR) { + mainPanel.setWidgetHidden(controlsPanel, true); + presentationPanel.setWidget(errorLabel); + state = ResultsPresenterState.ERROR; + } + errorLabel.setHTML(error); + currentResult = null; + currentQueryDefinition = null; + updateCurrentResultInfo(); } @Override public void showBusyIndicator() { if (state != ResultsPresenterState.BUSY) { + mainPanel.setWidgetHidden(controlsPanel, true); presentationPanel.setWidget(labeledBusyIndicator); state = ResultsPresenterState.BUSY; } currentResult = null; + currentQueryDefinition = null; updateCurrentResultInfo(); } @@ -150,6 +162,11 @@ public abstract class AbstractResultsPresenter public QueryResultDTO getCurrentResult() { return currentResult; } + + @Override + public StatisticQueryDefinitionDTO getCurrentQueryDefinition() { + return currentQueryDefinition; + } @Override public Widget getEntryWidget() { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/MultiResultsPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/MultiResultsPresenter.java index 8d8ed7ec120..16e7e87abf1 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/MultiResultsPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/MultiResultsPresenter.java @@ -8,11 +8,13 @@ import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.user.client.ui.DeckLayoutPanel; +import com.google.gwt.user.client.ui.HasVerticalAlignment; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; import com.sap.sse.datamining.ui.client.ResultsPresenter; @@ -43,6 +45,7 @@ public class MultiResultsPresenter extends AbstractDataMiningComponent controlsPanel = new HorizontalPanel(); controlsPanel.setSpacing(5); + controlsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); controlsPanel.add(new Label(getDataMiningStringMessages().choosePresentation() + ":")); presentersListBox = new ValueListBox<>(new AbstractObjectRenderer>() { @Override @@ -84,14 +87,19 @@ public class MultiResultsPresenter extends AbstractDataMiningComponent } @Override - public QueryResultDTO getCurrentResult() { + public QueryResultDTO getCurrentResult() { return currentPresenter.getCurrentResult(); } + + @Override + public StatisticQueryDefinitionDTO getCurrentQueryDefinition() { + return currentPresenter.getCurrentQueryDefinition(); + } @Override - public void showResult(QueryResultDTO result) { + public void showResult(StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result) { for (PresenterDescriptor descriptor : availablePresenters) { - descriptor.getPresenter().showResult(result); + descriptor.getPresenter().showResult(queryDefinition, result); } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/PlainFilterSelectionPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/PlainFilterSelectionPresenter.java index f0e5ff2a043..5cb68dd08a8 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/PlainFilterSelectionPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/PlainFilterSelectionPresenter.java @@ -8,21 +8,15 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; -import com.google.gwt.dom.client.Style.FontWeight; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.settings.AbstractSettings; -import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; -import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionChangedListener; -import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionProvider; -import com.sap.sse.datamining.ui.client.FilterSelectionChangedListener; import com.sap.sse.datamining.ui.client.FilterSelectionPresenter; import com.sap.sse.datamining.ui.client.FilterSelectionProvider; import com.sap.sse.datamining.ui.client.StringMessages; @@ -31,43 +25,30 @@ import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class PlainFilterSelectionPresenter extends AbstractComponent implements - FilterSelectionPresenter, FilterSelectionChangedListener, DataRetrieverChainDefinitionChangedListener { +public class PlainFilterSelectionPresenter extends AbstractComponent implements FilterSelectionPresenter { private final FilterSelectionProvider filterSelectionProvider; - private DataRetrieverChainDefinitionDTO retrieverChain; private final HorizontalPanel mainPanel; private final VerticalPanel presentationPanel; public PlainFilterSelectionPresenter(Component parent, ComponentContext context, - StringMessages stringMessages, DataRetrieverChainDefinitionProvider retrieverChainProvider, - FilterSelectionProvider filterSelectionProvider) { + StringMessages stringMessages, FilterSelectionProvider filterSelectionProvider) { super(parent, context); this.filterSelectionProvider = filterSelectionProvider; this.filterSelectionProvider.addSelectionChangedListener(this); - // TODO Is listening to the retriever chain really necessary? Shouldn't the filter selection change - // if the filter selection provider is affected by a changing retriever chain? - retrieverChainProvider.addDataRetrieverChainDefinitionChangedListener(this); Label currentSelectionLabel = new Label(stringMessages.currentFilterSelection()); currentSelectionLabel.setWidth("75px"); - currentSelectionLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); + currentSelectionLabel.addStyleName("emphasizedLabel"); presentationPanel = new VerticalPanel(); mainPanel = new HorizontalPanel(); + mainPanel.setStyleName("filterSelectionPresenter"); mainPanel.add(currentSelectionLabel); mainPanel.add(presentationPanel); } - @Override - public void dataRetrieverChainDefinitionChanged(DataRetrieverChainDefinitionDTO newDataRetrieverChainDefinition) { - if (!Objects.equals(retrieverChain, newDataRetrieverChainDefinition)) { - retrieverChain = newDataRetrieverChainDefinition; - presentationPanel.clear(); - } - } - @Override public void selectionChanged() { presentationPanel.clear(); @@ -76,16 +57,12 @@ public class PlainFilterSelectionPresenter extends AbstractComponent sortedLevels = new ArrayList<>(selection.keySet()); Collections.sort(sortedLevels); - boolean first = true; for (DataRetrieverLevelDTO retrieverLevel : sortedLevels) { Map> levelSelection = selection.get(retrieverLevel); RetrieverLevelFilterSelectionPresenter levelSelectionPresenter = new RetrieverLevelFilterSelectionPresenter( retrieverLevel, levelSelection); - if (!first) { - levelSelectionPresenter.getEntryWidget().getElement().getStyle().setMarginTop(5, Unit.PX); - } + levelSelectionPresenter.getEntryWidget().addStyleName("dataMiningMarginBottom"); presentationPanel.add(levelSelectionPresenter.getEntryWidget()); - first = false; } } @@ -97,17 +74,22 @@ public class PlainFilterSelectionPresenter extends AbstractComponent> levelSelection) { Label levelLabel = new Label(retrieverLevel.getRetrievedDataType().getDisplayName()); - levelLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD); levelLabel.setWidth("100px"); + levelLabel.addStyleName("emphasizedLabel"); VerticalPanel presentationPanel = new VerticalPanel(); List sortedDimensions = new ArrayList<>(levelSelection.keySet()); Collections.sort(sortedDimensions); + boolean first = true; for (FunctionDTO dimension : sortedDimensions) { Collection dimensionSelection = levelSelection.get(dimension); DimensionFilterSelectionPresenter dimensionSelectionPresenter = new DimensionFilterSelectionPresenter( dimension, dimensionSelection); + if (!first) { + dimensionSelectionPresenter.getEntryWidget().getElement().getStyle().setMarginTop(2, Unit.PX); + } presentationPanel.add(dimensionSelectionPresenter.getEntryWidget()); + first = false; } mainPanel = new HorizontalPanel(); @@ -129,7 +111,7 @@ public class PlainFilterSelectionPresenter extends AbstractComponent dimensionSelection) { mainPanel = new HorizontalPanel(); Label dimensionLabel = new Label(dimension.getDisplayName() + ":"); - dimensionLabel.getElement().getStyle().setMarginRight(2, Unit.PX); + dimensionLabel.addStyleName("dimensionLabel"); mainPanel.add(dimensionLabel); StringBuilder selectionStringBuilder = new StringBuilder(); @@ -172,7 +154,7 @@ public class PlainFilterSelectionPresenter extends AbstractComponent { sortByPanel = new HorizontalPanel(); sortByPanel.setSpacing(5); + sortByPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); sortByPanel.add(new Label(getDataMiningStringMessages().sortBy() + ":")); keyComparatorListBox = new ValueListBox<>(new AbstractRenderer>() { @Override @@ -202,6 +204,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { HorizontalPanel decimalsPanel = new HorizontalPanel(); decimalsPanel.setSpacing(5); + decimalsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); decimalsPanel.add(new Label(getDataMiningStringMessages().shownDecimals() + ":")); decimalsListBox = new ValueListBox(new SimpleObjectRenderer()); decimalsPanel.add(decimalsListBox); @@ -216,6 +219,7 @@ public class ResultsChart extends AbstractNumericResultsPresenter { HorizontalPanel showDataLabelsPanel = new HorizontalPanel(); showDataLabelsPanel.setSpacing(5); + showDataLabelsPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); showDataLabelsPanel.add(new Label(getDataMiningStringMessages().showDataLabels() + ":")); showDataLabelsCheckBox = new CheckBox(); showDataLabelsPanel.add(showDataLabelsCheckBox); @@ -256,7 +260,6 @@ public class ResultsChart extends AbstractNumericResultsPresenter { updateKeyComparatorListBox(); resetChartSeries(); updateChartLabels(); - updateChartSubtitle(); showResultData(); } @@ -293,11 +296,6 @@ public class ResultsChart extends AbstractNumericResultsPresenter { chart.setToolTip(new ToolTip().setValueDecimals(decimalsListBox.getValue())); } - private void updateChartSubtitle() { - chart.setChartSubtitle(new ChartSubtitle().setText(getDataMiningStringMessages().queryResultsChartSubtitle( - getCurrentResult().getRetrievedDataAmount(), getCurrentResult().getCalculationTimeInSeconds()))); - } - private void showResultData() { buildMainKeyMapAndSetXAxisCategories(); createAndAddSeriesToChart(); @@ -435,8 +433,8 @@ public class ResultsChart extends AbstractNumericResultsPresenter { private Chart createChart() { Chart chart = new Chart().setType(Series.Type.COLUMN).setMarginLeft(100).setMarginRight(45).setWidth100() .setHeight100().setBorderColor(new Color("#F0AB00")).setPlotBorderWidth(0) - .setCredits(new Credits().setEnabled(false)) - .setChartTitle(new ChartTitle().setText(getDataMiningStringMessages().dataMiningResult())); + .setTitle(new ChartTitle().setText(""), new ChartSubtitle().setText("")) + .setCredits(new Credits().setEnabled(false)); chart.setExporting(new Exporting().setEnabled(false)); chart.getXAxis().setAllowDecimals(false); chart.getYAxis().setAxisTitleText("Result").setLabels(new YAxisLabels().setFormatter(new AxisLabelsFormatter() { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java index bc6bbc13d2c..1dcb2eabef6 100755 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/presentation/TabbedResultsPresenter.java @@ -1,21 +1,25 @@ package com.sap.sse.datamining.ui.client.presentation; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import com.google.gwt.core.shared.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.logical.shared.BeforeSelectionEvent; -import com.google.gwt.event.logical.shared.BeforeSelectionHandler; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; +import com.sap.sse.datamining.ui.client.CompositeResultsPresenter; import com.sap.sse.datamining.ui.client.ResultsPresenter; import com.sap.sse.datamining.ui.client.presentation.ResultsChart.DrillDownCallback; import com.sap.sse.datamining.ui.client.resources.DataMiningResources; @@ -25,25 +29,40 @@ import com.sap.sse.gwt.client.shared.controls.ScrolledTabLayoutPanel; import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class TabbedResultsPresenter extends AbstractDataMiningComponent - implements ResultsPresenter { + implements CompositeResultsPresenter { protected static final DataMiningResources resources = GWT.create(DataMiningResources.class); - protected final ScrolledTabLayoutPanel tabPanel; - protected final Map> presentersMappedByHeader; - protected final DrillDownCallback drillDownCallback; - protected final Map> registeredResultPresenterMap; - - public TabbedResultsPresenter(Component parent, ComponentContext context, - DrillDownCallback drillDownCallback) { + private static final String IdPrefix = "Tab"; + + private final AtomicInteger idCounter; + private final ScrolledTabLayoutPanel tabPanel; + private final Map tabsMappedById; + private final DrillDownCallback drillDownCallback; + private final Map> registeredPresenterFactories; + private final ResultsPresenterFactory defaultFactory; + private final Set listeners; + + public TabbedResultsPresenter(Component parent, ComponentContext context, DrillDownCallback drillDownCallback) { super(parent, context); - this.drillDownCallback = drillDownCallback; + idCounter = new AtomicInteger(); tabPanel = new ScrolledTabLayoutPanel(30, Unit.PX, resources.arrowLeftIcon(), resources.arrowRightIcon()); tabPanel.setAnimationDuration(0); - presentersMappedByHeader = new HashMap<>(); - registeredResultPresenterMap = new HashMap<>(); + tabsMappedById = new HashMap<>(); + this.drillDownCallback = drillDownCallback; + registeredPresenterFactories = new HashMap<>(); + defaultFactory = new ResultsPresenterFactory<>(MultiResultsPresenter.class, + () -> new MultiResultsPresenter(this, getComponentContext(), drillDownCallback)); + listeners = new HashSet<>(); addNewTabTab(); addTabAndFocus(new MultiResultsPresenter(this, context, drillDownCallback)); + + tabPanel.addSelectionHandler(event -> { + String presenterId = ((CloseablePresenterTab) tabPanel.getTabWidget(event.getSelectedItem())).getId(); + for (CurrentPresenterChangedListener listener : listeners) { + listener.currentPresenterChanged(presenterId); + } + }); } private void addNewTabTab() { @@ -53,62 +72,156 @@ public class TabbedResultsPresenter extends AbstractDataMiningComponent() { - @Override - public void onBeforeSelection(BeforeSelectionEvent event) { - if (event.getItem() == tabPanel.getWidgetCount() - 1) { - event.cancel(); - addTabAndFocus(new MultiResultsPresenter(TabbedResultsPresenter.this, getComponentContext(), - drillDownCallback)); - } + tabPanel.addBeforeSelectionHandler(event -> { + if (event.getItem() == tabPanel.getWidgetCount() - 1) { + event.cancel(); + addTabAndFocus(new MultiResultsPresenter(TabbedResultsPresenter.this, getComponentContext(), + drillDownCallback)); } }); } @Override - public void showError(String error) { - getSelectedHeader().setText(getDataMiningStringMessages().error()); - getSelectedPresenter().showError(error); + public String getCurrentPresenterId() { + return getSelectedTab().getId(); } @Override - public void showError(String mainError, Iterable detailedErrors) { - getSelectedHeader().setText(getDataMiningStringMessages().error()); - getSelectedPresenter().showError(mainError, detailedErrors); + public Iterable getPresenterIds() { + Set idSet = new HashSet<>(tabsMappedById.keySet()); + return Collections.unmodifiableSet(idSet); + } + + @Override + public boolean containsPresenter(String presenterId) { + return tabsMappedById.containsKey(presenterId); + } + + @Override + public QueryResultDTO getResult(String presenterId) { + CloseablePresenterTab tab = getTab(presenterId); + return tab != null ? tab.getPresenter().getCurrentResult() : null; + } + + @Override + public StatisticQueryDefinitionDTO getQueryDefinition(String presenterId) { + CloseablePresenterTab tab = getTab(presenterId); + return tab != null ? tab.getPresenter().getCurrentQueryDefinition() : null; } @Override - public void showBusyIndicator() { - getSelectedHeader().setText(getDataMiningStringMessages().runningQuery()); - getSelectedPresenter().showBusyIndicator(); + public void showResult(String presenterId, StatisticQueryDefinitionDTO queryDefinition, QueryResultDTO result) { + CloseablePresenterTab presenterTab = getTab(presenterId); + if (presenterTab == null) { + return; + } + + if (result != null) { + ResultsPresenter presenter = presenterTab.getPresenter(); + ResultsPresenterFactory factory = registeredPresenterFactories.getOrDefault(result.getResultType(), defaultFactory); + if (presenter.getClass() != factory.getProducedType()) { + CloseablePresenterTab newPresenterTab = addTabAndFocus(factory.createPresenter()); + removeTab(presenterTab); + presenterTab = newPresenterTab; + } + presenterTab.setText(result.getResultSignifier()); + } else { + presenterTab.setText(getDataMiningStringMessages().empty()); + } + presenterTab.getPresenter().showResult(queryDefinition, result); } @Override - public QueryResultDTO getCurrentResult() { - return getSelectedPresenter().getCurrentResult(); + public void showError(String presenterId, String error) { + CloseablePresenterTab tab = getTab(presenterId); + if (tab == null) { + return; + } + + tab.setText(getDataMiningStringMessages().error()); + tab.getPresenter().showError(error); } - protected CloseableTabHeader getSelectedHeader() { - return (CloseableTabHeader) tabPanel.getTabWidget(tabPanel.getSelectedIndex()); + @Override + public void showError(String presenterId, String mainError, Iterable detailedErrors) { + CloseablePresenterTab tab = getTab(presenterId); + if (tab == null) { + return; + } + + tab.setText(getDataMiningStringMessages().error()); + tab.getPresenter().showError(mainError, detailedErrors); } - protected ResultsPresenter getSelectedPresenter() { - return presentersMappedByHeader.get(getSelectedHeader()); + @Override + public void showBusyIndicator(String presenterId) { + CloseablePresenterTab tab = getTab(presenterId); + if (tab == null) { + return; + } + + tab.setText(getDataMiningStringMessages().runningQuery()); + tab.getPresenter().showBusyIndicator(); } - protected void addTabAndFocus(ResultsPresenter tabPresenter) { - CloseableTabHeader tabHeader = new CloseableTabHeader(); - presentersMappedByHeader.put(tabHeader, tabPresenter); + @Override + public void addCurrentPresenterChangedListener(CurrentPresenterChangedListener listener) { + listeners.add(listener); + } - tabPanel.insert(tabPresenter.getEntryWidget(), tabHeader, tabPanel.getWidgetCount() - 1); - int presenterIndex = tabPanel.getWidgetIndex(tabPresenter.getEntryWidget()); + @Override + public void removeCurrentPresenterChangedListener(CurrentPresenterChangedListener listener) { + listeners.remove(listener); + } + + /** + * Register a {@link ResultsPresenterFactory} that creates {@link ResultsPresenter} to handle the retrieved + * {@link resultType}. Each {@link resultType} can only be registered once. Multiple registrations will cause an + * {@link IllegalStateException}. When {@link #showResult(QueryResultDTO)} executes it checks whether the + * {@link resultType} was registered before and triggers the corresponding {@link ResultsPresenterFactory}. + * + * @param resultType + * of the data mining query. + * @param presenterFactory + * creates {@link ResultsPresenter} which shall be used to handle the data mining query result. + * + * @throws IllegalStateException + * if the {@link resultType} is already registered. + */ + public void registerResultsPresenter(Class resultType, ResultsPresenterFactory presenterFactory) + throws IllegalStateException { + String className = resultType.getName(); + if (!registeredPresenterFactories.containsKey(className)) { + registeredPresenterFactories.put(className, presenterFactory); + } else { + throw new IllegalStateException( + "Multiple registration for result type key: " + resultType.toString() + "not allowed."); + } + } + + protected CloseablePresenterTab getSelectedTab() { + return (CloseablePresenterTab) tabPanel.getTabWidget(tabPanel.getSelectedIndex()); + } + + protected CloseablePresenterTab getTab(String id) { + return tabsMappedById.get(id); + } + + protected CloseablePresenterTab addTabAndFocus(ResultsPresenter presenter) { + String tabId = IdPrefix + idCounter.getAndIncrement(); + CloseablePresenterTab presenterTab = new CloseablePresenterTab(tabId, presenter); + tabsMappedById.put(tabId, presenterTab); + + tabPanel.insert(presenter.getEntryWidget(), presenterTab, tabPanel.getWidgetCount() - 1); + int presenterIndex = tabPanel.getWidgetIndex(presenter.getEntryWidget()); tabPanel.selectTab(presenterIndex); tabPanel.scrollToTab(presenterIndex); + return presenterTab; } - protected void removeTab(CloseableTabHeader header) { - header.removeFromParent(); - presentersMappedByHeader.remove(header); + protected void removeTab(CloseablePresenterTab tab) { + tab.removeFromParent(); + tabsMappedById.remove(tab.getId()); } @Override @@ -151,89 +264,78 @@ public class TabbedResultsPresenter extends AbstractDataMiningComponent> { + T create(); + } + + public static class ResultsPresenterFactory> { + + private final Class presenterType; + private final ResultsPresenterSupplier presenterSupplier; + + public ResultsPresenterFactory(Class presenterType, ResultsPresenterSupplier presenterSupplier) { + this.presenterType = presenterType; + this.presenterSupplier = presenterSupplier; + } - public class CloseableTabHeader extends FlowPanel { + public Class getProducedType() { + return presenterType; + } + + public T createPresenter() { + return presenterSupplier.create(); + } + + } - private final Label label; + private class CloseablePresenterTab extends FlowPanel { - public CloseableTabHeader() { + private final String id; + private final Label headerLabel; + private final ResultsPresenter presenter; + + public CloseablePresenterTab(String id, ResultsPresenter presenter) { + this.id = id; + this.presenter = presenter; this.addStyleName("resultsPresenterTabHeader"); - label = new Label(getDataMiningStringMessages().empty()); - this.add(label); + headerLabel = new Label(getDataMiningStringMessages().empty()); + this.add(headerLabel); Image closeImage = new Image(resources.closeIcon()); closeImage.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { if (tabPanel.getWidgetCount() > 2) { - removeTab(CloseableTabHeader.this); + removeTab(CloseablePresenterTab.this); } } }); this.add(closeImage); } + + public String getId() { + return id; + } + + public ResultsPresenter getPresenter() { + return presenter; + } public void setText(String text) { - label.setText(text); + headerLabel.setText(text); tabPanel.checkIfScrollButtonsNecessary(); } } - - @Override - public String getId() { - return "TabbedResultsPresenter"; - } - - /** - * Register a {@link ResultsPresenter} to handle the retrieved {@link resultType}. Each {@link resultType} can only - * be registered once. Multiple registrations will cause an {@link IllegalStateException}. When - * {@link #showResult(QueryResultDTO)} executes it checks whether the {@link resultType} was registered before and - * triggers the corresponding {@link ResultsPresenter}. - * - * @param resultType - * of the datamining query. - * @param resultPresenter - * which shall be used to handle the datamaining query result. - * - * @throws IllegalStateException - * if the {@link resultType} is already registered. - */ - public void registerResultsPresenter(Class resultType, ResultsPresenter resultPresenter) - throws IllegalStateException { - String className = resultType.getName(); - if (!registeredResultPresenterMap.containsKey(className)) { - registeredResultPresenterMap.put(className, resultPresenter); - } else { - throw new IllegalStateException( - "Multiple registration for result type key: " + resultType.toString() + "not allowed."); - } - } - - @Override - public void showResult(QueryResultDTO result) { - try { - if (result != null) { - if (registeredResultPresenterMap.containsKey(result.getResultType())) { - CloseableTabHeader oldHeader = getSelectedHeader(); - addTabAndFocus(registeredResultPresenterMap.get(result.getResultType())); - removeTab(oldHeader); - } else { - if (!(getSelectedPresenter() instanceof MultiResultsPresenter)) { - CloseableTabHeader oldHeader = getSelectedHeader(); - addTabAndFocus(new MultiResultsPresenter(this, getComponentContext(), drillDownCallback)); - removeTab(oldHeader); - } - getSelectedHeader().setText(result.getResultSignifier()); - } - } - - } finally { - getSelectedPresenter().showResult(result); - } - } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css index 9f3ff36a969..7e7fc8240c7 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/resources/DataMiningDataGrid.css @@ -9,6 +9,12 @@ .dataGridHoveredRow { cursor: default; + background: #d5e2ff; +} + +.dataGridSelectedRow { + background: #628cd5; + color: white; } .dataGridSubHeader { @@ -20,6 +26,8 @@ .dataGridSubHeaderLabel { position: relative; left: -20px; + font-weight: 600; + text-shadow: none; } .dataGridSpacedSubHeader { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/AbstractQueryDefinitionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/AbstractQueryDefinitionProvider.java index efbd8892979..128278bf854 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/AbstractQueryDefinitionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/AbstractQueryDefinitionProvider.java @@ -13,16 +13,16 @@ import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sse.common.settings.Settings; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; import com.sap.sse.datamining.ui.client.QueryDefinitionChangedListener; import com.sap.sse.datamining.ui.client.QueryDefinitionProvider; +import com.sap.sse.datamining.ui.client.StringMessages; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.settings.ComponentContext; @@ -101,9 +101,8 @@ public abstract class AbstractQueryDefinitionProvider errorMessages = new ArrayList(); if (queryDefinition != null) { - String grouperError = validateGrouper(queryDefinition); - if (grouperError != null && !grouperError.isEmpty()) { - errorMessages.add(grouperError); - } - String statisticError = validateStatisticAndAggregator(queryDefinition); - if (statisticError != null && !statisticError.isEmpty()) { - errorMessages.add(statisticError); - } - String retrieverChainError = validateDataRetrieverChain(queryDefinition); - if (retrieverChainError != null && !retrieverChainError.isEmpty()) { - errorMessages.add(retrieverChainError); + StringMessages stringMessages = getDataMiningStringMessages(); + if (queryDefinition.getStatisticToCalculate() == null || + queryDefinition.getDataRetrieverChainDefinition() == null) { + errorMessages.add(stringMessages.noStatisticSelectedError()); + } else { + // Other components aren't available if no statistic has been selected + // Reporting an error for something that isn't there would be misleading + if (queryDefinition.getAggregatorDefinition() == null) { + errorMessages.add(stringMessages.noAggregatorSelectedError()); + } + if (queryDefinition.getDimensionsToGroupBy().isEmpty()) { + errorMessages.add(stringMessages.noDimensionToGroupBySelectedError()); + } } } return errorMessages; } - private String validateGrouper(StatisticQueryDefinitionDTO queryDefinition) { - for (FunctionDTO dimension : queryDefinition.getDimensionsToGroupBy()) { - if (dimension != null) { - return null; - } - } - return getDataMiningStringMessages().noDimensionToGroupBySelectedError(); - } - - private String validateStatisticAndAggregator(StatisticQueryDefinitionDTO queryDefinition) { - return queryDefinition.getStatisticToCalculate() == null || queryDefinition.getAggregatorDefinition() == null - ? getDataMiningStringMessages().noStatisticSelectedError() : null; - } - - private String validateDataRetrieverChain(StatisticQueryDefinitionDTO queryDefinition) { - return queryDefinition.getDataRetrieverChainDefinition() == null - ? getDataMiningStringMessages().noDataRetrieverChainDefinitonSelectedError() : null; - } - @Override public void addQueryDefinitionChangedListener(QueryDefinitionChangedListener listener) { listeners.add(listener); @@ -187,34 +171,12 @@ public abstract class AbstractQueryDefinitionProvider { +public class DimensionFilterSelectionProvider extends AbstractDataMiningComponent { private static final DataMiningResources resources = GWT.create(DataMiningResources.class); private static final NaturalComparator NaturalComparator = new NaturalComparator(); @@ -78,17 +80,20 @@ public class DimensionFilterSelectionProvider extends AbstractComponent selectionToBeApplied; private final DockLayoutPanel mainPanel; private final AbstractFilterablePanel filterPanel; private final LayoutPanel contentContainer; private final SimpleBusyIndicator busyIndicator; + private final Set availableData; private final ListDataProvider filteredData; private final MultiSelectionModel selectionModel; private final DataGrid dataGrid; private final Column checkboxColumn; + + private Iterable selectionToBeApplied; + private Consumer> selectionCallback; public DimensionFilterSelectionProvider(Component parent, ComponentContext componentContext, DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, DataMiningSession session, DataRetrieverChainDefinitionProvider retrieverChainProvider, @@ -104,7 +109,6 @@ public class DimensionFilterSelectionProvider extends AbstractComponent(); - selectionToBeApplied = new HashSet<>(); DataMiningDataGridResources dataGridResources = GWT.create(DataMiningDataGridResources.class); dataGrid = new DataGrid<>(Integer.MAX_VALUE, dataGridResources); @@ -112,6 +116,7 @@ public class DimensionFilterSelectionProvider extends AbstractComponent(); filteredData = new ListDataProvider(this::elementAsString); filterPanel = new AbstractFilterablePanel(null, dataGrid, filteredData) { @Override @@ -170,6 +175,7 @@ public class DimensionFilterSelectionProvider extends AbstractComponent dimensions = new HashSet<>(); dimensions.add(dimension); + availableData.clear(); counter.increase(); contentContainer.remove(dataGrid); busyIndicator.setBusy(true); @@ -214,20 +221,23 @@ public class DimensionFilterSelectionProvider extends AbstractComponent> result) { Map> results = result.getResults(); - List content = new ArrayList<>(); + List sortedData = new ArrayList<>(); if (!results.isEmpty()) { GroupKey contentKey = new GenericGroupKey(dimension); - content.addAll((Collection) results.get(contentKey)); - content.sort((o1, o2) -> NaturalComparator.compare(o1.toString(), o2.toString())); + availableData.addAll((Collection) results.get(contentKey)); + sortedData.addAll(availableData); + sortedData.sort((o1, o2) -> NaturalComparator.compare(o1.toString(), o2.toString())); } busyIndicator.setBusy(false); - filterPanel.updateAll(content); + filterPanel.updateAll(sortedData); contentContainer.add(dataGrid); - internalSetSelection(!selectionToBeApplied.isEmpty() ? selectionToBeApplied : selectionModel.getSelectedSet()); - selectionToBeApplied.clear(); + internalSetSelection(selectionToBeApplied != null ? selectionToBeApplied : selectionModel.getSelectedSet(), + selectionCallback != null ? selectionCallback : m -> { }); + selectionToBeApplied = null; + selectionCallback = null; if (callback != null) { callback.run(); @@ -236,6 +246,8 @@ public class DimensionFilterSelectionProvider extends AbstractComponent(selectionModel.getSelectedSet()); } - public void setSelection(Iterable items) { - selectionToBeApplied.clear(); - for (Serializable item : items) { - selectionToBeApplied.add(item); - } + public void setSelection(Iterable selection, Consumer> callback) { + selectionToBeApplied = selection; + selectionCallback = callback; if (!busyIndicator.isBusy()) { - internalSetSelection(selectionToBeApplied); - selectionToBeApplied.clear(); + internalSetSelection(selectionToBeApplied, selectionCallback); + selectionToBeApplied = null;; + selectionCallback = null; } } - private void internalSetSelection(Set selection) { + private void internalSetSelection(Iterable selection, Consumer> callback) { clearSelection(); - if (!selection.isEmpty()) { - for (Serializable item : filterPanel.getAll()) { - if (selection.contains(item)) { - selectionModel.setSelected(item, true); - } + Collection missingValues = new ArrayList<>(); + for (Serializable value : selection) { + if (availableData.contains(value)) { + selectionModel.setSelected(value, true); + } else { + missingValues.add(value); } } + + if (!missingValues.isEmpty()) { + String listedValues = missingValues.stream().map(this::elementAsString).collect(Collectors.joining(", ")); + callback.accept(Collections.singleton(getDataMiningStringMessages() + .filterValuesOfDimensionAreNotAvailable(dimension.getDisplayName(), listedValues))); + } else { + callback.accept(Collections.emptySet()); + } } public void clearSelection() { diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java index da61d4672b7..21242950caf 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/HierarchicalDimensionListFilterSelectionProvider.java @@ -12,6 +12,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import com.google.gwt.cell.client.CheckboxCell; @@ -31,7 +32,9 @@ import com.google.gwt.user.cellview.client.DataGrid; import com.google.gwt.user.cellview.client.RowHoverEvent; import com.google.gwt.user.cellview.client.TextColumn; import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.Widget; @@ -42,6 +45,7 @@ import com.google.gwt.view.client.DefaultSelectionEventManager.SelectAction; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.MultiSelectionModel; import com.google.gwt.view.client.SelectionChangeEvent; +import com.sap.sse.common.Util; import com.sap.sse.common.settings.SerializableSettings; import com.sap.sse.datamining.shared.DataMiningSession; import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; @@ -49,6 +53,7 @@ import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; import com.sap.sse.datamining.shared.impl.dto.ReducedDimensionsDTO; +import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionProvider; import com.sap.sse.datamining.ui.client.FilterSelectionChangedListener; @@ -61,21 +66,20 @@ import com.sap.sse.datamining.ui.client.resources.DataMiningDataGridResources.Da import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.celltable.BaseCellTableBuilder; import com.sap.sse.gwt.client.panels.AbstractFilterablePanel; -import com.sap.sse.gwt.client.shared.components.AbstractComponent; import com.sap.sse.gwt.client.shared.components.Component; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class HierarchicalDimensionListFilterSelectionProvider extends AbstractComponent implements FilterSelectionProvider { - - private final static String DimensionListSubheaderAttribute = "subheader"; +public class HierarchicalDimensionListFilterSelectionProvider extends AbstractDataMiningComponent implements FilterSelectionProvider { + private static final String DimensionListSubheaderAttribute = "subheader"; private static final Unit LayoutUnit = Unit.PX; private static final double SelectionPresenterHeight = 100; - private static final double DimensionSelectionWidth = 300; - private static final double DimensionSelectionHeaderHeight = 40; + private static final double DimensionSelectionWidth = 350; + private static final double DimensionSelectionHeaderHeight = 30; + private static final double FilterFilterDimensionsHeight = 40; private static final double FilterSelectionTableWidth = 250; - + private final DataMiningSession session; private final DataMiningServiceAsync dataMiningService; private final ErrorReporter errorReporter; @@ -83,12 +87,15 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo private final Set listeners; private boolean isAwaitingReload; + private boolean isUpdatingFilterDimensions; + private boolean ignoreSelectionChangedNotifications; private DataRetrieverChainDefinitionDTO retrieverChain; private ReducedDimensionsDTO reducedDimensions; - private final List allFilterDimensions; + private final List availableFilterDimensions; private final ListDataProvider filteredFilterDimensions; private final DockLayoutPanel mainPanel; + private final Button clearSelectionButton; private final AbstractFilterablePanel filterFilterDimensionsPanel; private final MultiSelectionModel filterDimensionSelectionModel; @@ -97,14 +104,16 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo private final DockLayoutPanel dimensionFilterSelectionProvidersPanel; private final Map dimensionFilterSelectionProviders; - private boolean updateInProgress; + private boolean isUpdatingAvailableFilterValues; private final FilterSelectionPresenter filterSelectionPresenter; private final ScrollPanel filterSelectionPresenterContainer; + private HashMap>> selectionToBeApplied; + private Consumer> selectionCallback; + public HierarchicalDimensionListFilterSelectionProvider(Component parent, ComponentContext context, - DataMiningSession session, StringMessages stringMessages, - DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, + DataMiningSession session, DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, DataRetrieverChainDefinitionProvider retrieverChainProvider) { super(parent, context); this.session = session; @@ -116,14 +125,21 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo listeners = new HashSet<>(); isAwaitingReload = false; retrieverChain = null; - allFilterDimensions = new ArrayList<>(); + availableFilterDimensions = new ArrayList<>(); filteredFilterDimensions = new ListDataProvider<>(); + + StringMessages stringMessages = getDataMiningStringMessages(); Label filterDimensionsSelectionTitleLabel = new Label(stringMessages.selectDimensionsToFilterBy()); filterDimensionsSelectionTitleLabel.addStyleName("emphasizedLabel"); filterDimensionsSelectionTitleLabel.addStyleName("dataMiningMarginLeft"); filterDimensionsSelectionTitleLabel.addStyleName("filterDimensionsTitleLabel"); + clearSelectionButton = new Button(stringMessages.clear()); + clearSelectionButton.addStyleName("floatRight"); + clearSelectionButton.addStyleName("dataMiningMarginRight"); + clearSelectionButton.addClickHandler(e -> clearSelection()); + DataMiningDataGridResources resources = GWT.create(DataMiningDataGridResources.class); filterDimensionsList = new DataGrid<>(Integer.MAX_VALUE, resources); filterDimensionsList.setAutoHeaderRefreshDisabled(true); @@ -165,9 +181,14 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo }; filterDimensionsList.addColumn(dimensionColumn); + FlowPanel headerPanel = new FlowPanel(); + headerPanel.addStyleName("dataMiningMarginTop"); + headerPanel.add(filterDimensionsSelectionTitleLabel); + headerPanel.add(clearSelectionButton); + DockLayoutPanel filterDimensionsSelectionPanel = new DockLayoutPanel(LayoutUnit); - filterDimensionsSelectionPanel.addNorth(filterDimensionsSelectionTitleLabel, 20); - filterDimensionsSelectionPanel.addNorth(filterFilterDimensionsPanel, DimensionSelectionHeaderHeight); + filterDimensionsSelectionPanel.addNorth(headerPanel, DimensionSelectionHeaderHeight); + filterDimensionsSelectionPanel.addNorth(filterFilterDimensionsPanel, FilterFilterDimensionsHeight); filterDimensionsSelectionPanel.add(filterDimensionsList); dimensionFilterSelectionProviders = new HashMap<>(); @@ -175,8 +196,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo dimensionFilterSelectionProvidersPanel.addStyleName("dimensionFilterSelectionTablesContainer"); dimensionFilterSelectionProvidersPanel.addStyleName("dataMiningBorderLeft"); - filterSelectionPresenter = new PlainFilterSelectionPresenter(this, context, stringMessages, retrieverChainProvider, this); - filterSelectionPresenter.getEntryWidget().addStyleName("dataMiningMarginLeft"); + filterSelectionPresenter = new PlainFilterSelectionPresenter(this, context, stringMessages, this); filterSelectionPresenterContainer = new ScrollPanel(filterSelectionPresenter.getEntryWidget()); filterSelectionPresenterContainer.addStyleName("dataMiningBorderTop"); @@ -185,6 +205,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo mainPanel.setWidgetHidden(filterSelectionPresenterContainer, true); mainPanel.addWest(filterDimensionsSelectionPanel, DimensionSelectionWidth); mainPanel.add(dimensionFilterSelectionProvidersPanel); + updateControls(); } @Override @@ -199,79 +220,200 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo @Override public void reloadComponents() { - updateFilterDimensions(); isAwaitingReload = false; - notifyListeners(); + updateFilterDimensions(); } @Override public void dataRetrieverChainDefinitionChanged(DataRetrieverChainDefinitionDTO newDataRetrieverChainDefinition) { if (!Objects.equals(retrieverChain, newDataRetrieverChainDefinition)) { retrieverChain = newDataRetrieverChainDefinition; - if (!isAwaitingReload && retrieverChain != null) { + if (!isAwaitingReload) { updateFilterDimensions(); - } else if (!isAwaitingReload) { - clearContent(); } } } private void updateFilterDimensions() { - clearContent(); - dataMiningService.getReducedDimensionsMappedByLevelFor(retrieverChain, LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback() { - @Override - public void onSuccess(ReducedDimensionsDTO result) { - reducedDimensions = result; - for (Entry> entry : reducedDimensions.getReducedDimensions().entrySet()) { - DataRetrieverLevelDTO retrieverlevel = entry.getKey(); - for (FunctionDTO dimension : entry.getValue()) { - allFilterDimensions.add(new DimensionWithContext(dimension, retrieverlevel)); + // Try to retain the selection even if the retriever chain has been null in between + HashMap>> currentSelection = getSelection(); + if (!currentSelection.isEmpty()) { + selectionToBeApplied = currentSelection; + selectionCallback = EmptyApplyCallback; + } + + if (retrieverChain != null) { + isUpdatingFilterDimensions = true; + clearContent(); + dataMiningService.getReducedDimensionsMappedByLevelFor(retrieverChain, LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback() { + @Override + public void onSuccess(ReducedDimensionsDTO result) { + reducedDimensions = result; + for (Entry> entry : reducedDimensions.getReducedDimensions().entrySet()) { + DataRetrieverLevelDTO retrieverlevel = entry.getKey(); + for (FunctionDTO dimension : entry.getValue()) { + availableFilterDimensions.add(new DimensionWithContext(dimension, retrieverlevel)); + } } + availableFilterDimensions.sort(null); + filterFilterDimensionsPanel.updateAll(availableFilterDimensions); + + updateControls(); + if (selectionToBeApplied != null) { + ignoreSelectionChangedNotifications = true; + setSelection(selectionToBeApplied, selectionCallback); + selectionToBeApplied = null; + selectionCallback = null; + } + isUpdatingFilterDimensions = false; } - allFilterDimensions.sort(null); - filterFilterDimensionsPanel.updateAll(allFilterDimensions); - } - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error fetching the dimensions of the retriever chain from the server: " + caught.getMessage()); - } - }); + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error fetching the dimensions of the retriever chain from the server: " + caught.getMessage()); + updateControls(); + selectionToBeApplied = null; + selectionCallback = null; + isUpdatingFilterDimensions = false; + } + }); + } else { + clearContent(); + updateControls(); + } } private void clearContent() { + boolean notifyListeners = !getSelection().isEmpty(); reducedDimensions = null; - allFilterDimensions.clear(); + availableFilterDimensions.clear(); filterDimensionSelectionModel.clear(); filterFilterDimensionsPanel.removeAll(); + if (notifyListeners) { + notifyListeners(); + } + } + + private void updateControls() { + boolean controlsEnabled = !availableFilterDimensions.isEmpty(); + clearSelectionButton.setEnabled(controlsEnabled); + filterFilterDimensionsPanel.getTextBox().setEnabled(controlsEnabled); } @Override - public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { - HashMap>> filterSelection = queryDefinition.getFilterSelection(); - for (DimensionWithContext dimensionWithContext : allFilterDimensions) { - HashMap> retrieverLevelSelection = filterSelection.get(dimensionWithContext.getRetrieverLevel()); - if (retrieverLevelSelection != null) { - HashSet items = retrieverLevelSelection.get(dimensionWithContext.getDimension()); - if (items != null) { - setSelection(dimensionWithContext, items); + public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition, Consumer> callback) { + DataRetrieverChainDefinitionDTO retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); + selectionToBeApplied = queryDefinition.getFilterSelection(); + selectionCallback = callback; + if (!isUpdatingFilterDimensions && !isAwaitingReload && retrieverChain.equals(this.retrieverChain)) { + ignoreSelectionChangedNotifications = true; + filterDimensionSelectionModel.clear(); + setSelection(selectionToBeApplied, selectionCallback); + selectionToBeApplied = null; + selectionCallback = null; + } + } + + private void setSelection(HashMap>> filterSelection, Consumer> callback) { + Set innerCallbacks = new HashSet<>(); + Collection callbackMessages = new ArrayList<>(); + Collection missingDimensions = new ArrayList<>(); + for (DataRetrieverLevelDTO retrieverLevel : filterSelection.keySet()) { + HashMap> levelSelection = filterSelection.get(retrieverLevel); + for (FunctionDTO dimension : levelSelection.keySet()) { + DimensionWithContext dimensionWithContext = new DimensionWithContext(dimension, retrieverLevel); + int index = availableFilterDimensions.indexOf(dimensionWithContext); + if (index != -1) { + InnerSelectionCallback innerCallback = new InnerSelectionCallback(callbackMessages, innerCallbacks, callback); + innerCallbacks.add(innerCallback); + setDimensionSelection(availableFilterDimensions.get(index), levelSelection.get(dimension), innerCallback); + } else { + missingDimensions.add(dimensionWithContext); } } } + + if (!missingDimensions.isEmpty()) { + String listedDimensions = missingDimensions.stream().map(d -> d.getDimension().getDisplayName()) + .collect(Collectors.joining(", ")); + callbackMessages.add(getDataMiningStringMessages().filterDimensionsAreNotAvailable(listedDimensions)); + } + + if (!innerCallbacks.isEmpty()) { + for (InnerSelectionCallback innerCallback : innerCallbacks) { + innerCallback.canPublishMessages = true; + } + } else { + setSelectionCompleted(callback, callbackMessages); + } + } + + private void setSelectionCompleted(Consumer> callback, Iterable messages) { + HashMap>> selection = getSelection(); + ignoreSelectionChangedNotifications = false; + Runnable finalizeSelection = () -> { + mainPanel.setWidgetHidden(filterSelectionPresenterContainer, selection.isEmpty()); + filterSelectionPresenter.selectionChanged(); + callback.accept(messages); + }; + if (dimensionFilterSelectionProviders.size() > 1) { + isUpdatingAvailableFilterValues = false; + updateAvailableFilterValues(retrieverChain.getRetrieverLevel(0), null, finalizeSelection); + } else { + finalizeSelection.run(); + } + } + + private class InnerSelectionCallback implements Consumer> { + + private final Collection allMessages; + private final Set innerCallbacks; + private final Consumer> outerCallback; + private boolean canPublishMessages; + + public InnerSelectionCallback(Collection allMessages, Set innerCallbacks, + Consumer> outerCallback) { + this.allMessages = allMessages; + this.innerCallbacks = innerCallbacks; + this.outerCallback = outerCallback; + } + + @Override + public void accept(Iterable messages) { + Util.addAll(messages, allMessages); + innerCallbacks.remove(this); + if (canPublishMessages && innerCallbacks.isEmpty()) { + setSelectionCompleted(outerCallback, allMessages); + } + } + } @Override - public void setHighestRetrieverLevelWithFilterDimension(FunctionDTO dimension, Serializable groupKey) { - FunctionDTO reducedDimension = reducedDimensions == null ? dimension : reducedDimensions.getReducedDimension(dimension); - Collection items = Collections.singleton(groupKey); - for (DimensionWithContext dimensionWithContext : allFilterDimensions) { + public void setFilterSelectionForUnreducedDimension(FunctionDTO unreducedDimension, Serializable filterValue, Runnable onCompletion) { + FunctionDTO reducedDimension = reducedDimensions == null ? unreducedDimension : reducedDimensions.getReducedDimension(unreducedDimension); + DimensionWithContext dimensionToChange = null; + for (DimensionWithContext dimensionWithContext : availableFilterDimensions) { if (dimensionWithContext.getDimension().equals(reducedDimension)) { - setSelection(dimensionWithContext, items); + dimensionToChange = dimensionWithContext; break; } } + + if (dimensionToChange != null) { + ignoreSelectionChangedNotifications = true; + final DimensionWithContext changedDimension = dimensionToChange; + setDimensionSelection(dimensionToChange, Collections.singleton(filterValue), m -> { + ignoreSelectionChangedNotifications = false; + if (dimensionFilterSelectionProviders.size() > 1) { + updateAvailableFilterValues(changedDimension.getRetrieverLevel(), changedDimension, onCompletion); + } else { + onCompletion.run(); + } + }); + } } - private void setSelection(DimensionWithContext dimension, Collection items) { + private void setDimensionSelection(DimensionWithContext dimension, Collection items, Consumer> callback) { DimensionFilterSelectionProvider selectionProvider; if (filterDimensionSelectionModel.isSelected(dimension)) { selectionProvider = dimensionFilterSelectionProviders.get(dimension); @@ -279,7 +421,7 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo selectionProvider = addDimensionFilterSelectionProvider(dimension); filterDimensionSelectionModel.setSelected(dimension, true); } - selectionProvider.setSelection(items); + selectionProvider.setSelection(items, callback); } private void selectedFilterDimensionsChanged(SelectionChangeEvent event) { @@ -327,57 +469,66 @@ public class HierarchicalDimensionListFilterSelectionProvider extends AbstractCo } private void filterSelectionChanged(DimensionWithContext changedDimension) { - if (!updateInProgress) { - updateInProgress = true; - int levelToStartWith = changedDimension.getRetrieverLevel().getLevel(); - List dimensionsToUpdate = dimensionFilterSelectionProviders.keySet() - .stream().filter(d -> d.getRetrieverLevel().getLevel() >= levelToStartWith) - .collect(Collectors.toList()); - updateDimensionFilterSelectionProviders(dimensionsToUpdate.iterator(), changedDimension); + if (!isUpdatingAvailableFilterValues && !ignoreSelectionChangedNotifications) { + updateAvailableFilterValues(changedDimension.getRetrieverLevel(), changedDimension, this::notifyListeners); } } + + private void updateAvailableFilterValues(DataRetrieverLevelDTO fromRetrieverLevel, DimensionWithContext exceptDimension, Runnable onCompletion) { + if (isUpdatingAvailableFilterValues) { + throw new IllegalStateException("Update of availabe filter values already in progress"); + } - private void updateDimensionFilterSelectionProviders(Iterator dimensionIterator, DimensionWithContext exceptDimension) { + List dimensionsToUpdate = new ArrayList<>(); + for (DimensionWithContext dimension : dimensionFilterSelectionProviders.keySet()) { + if (dimension.getRetrieverLevel().getLevel() >= fromRetrieverLevel.getLevel()) { + dimensionsToUpdate.add(dimension); + } + } + dimensionsToUpdate.sort(null); + + isUpdatingAvailableFilterValues = true; + updateDimensionFilterSelectionProviders(dimensionsToUpdate.iterator(), exceptDimension, onCompletion); + } + + private void updateDimensionFilterSelectionProviders(Iterator dimensionIterator, DimensionWithContext exceptDimension, Runnable onCompletion) { if (dimensionIterator.hasNext()) { DimensionWithContext dimension = dimensionIterator.next(); if (dimension.equals(exceptDimension)) { - updateDimensionFilterSelectionProviders(dimensionIterator, exceptDimension); + updateDimensionFilterSelectionProviders(dimensionIterator, exceptDimension, onCompletion); } else { DimensionFilterSelectionProvider selectionProvider = dimensionFilterSelectionProviders.get(dimension); HashSet selectionBefore = selectionProvider.getSelection(); selectionProvider.updateContent(() -> { boolean selectionChanged = !selectionBefore.equals(selectionProvider.getSelection()); if (selectionChanged) { - updateInProgress = false; - filterSelectionChanged(dimension); + isUpdatingAvailableFilterValues = false; + updateAvailableFilterValues(dimension.getRetrieverLevel(), dimension, onCompletion); } else { - updateDimensionFilterSelectionProviders(dimensionIterator, exceptDimension); + updateDimensionFilterSelectionProviders(dimensionIterator, exceptDimension, onCompletion); } }); } } else { - updateInProgress = false; - notifyListeners(); + isUpdatingAvailableFilterValues = false; + onCompletion.run(); } } @Override public HashMap>> getSelection() { HashMap>> filterSelection = new HashMap<>(); - for (DimensionWithContext dimensionWithContext : allFilterDimensions) { + for (DimensionWithContext dimensionWithContext : dimensionFilterSelectionProviders.keySet()) { DataRetrieverLevelDTO retrieverLevel = dimensionWithContext.getRetrieverLevel(); FunctionDTO dimension = dimensionWithContext.getDimension(); - DimensionFilterSelectionProvider selectionProvider = dimensionFilterSelectionProviders.get(dimensionWithContext); - if (selectionProvider != null) { - HashSet dimensionFilterSelection = selectionProvider.getSelection(); - if (!dimensionFilterSelection.isEmpty()) { - HashMap> retrieverFilterSelection = filterSelection.get(retrieverLevel); - if (retrieverFilterSelection == null) { - retrieverFilterSelection = new HashMap<>(); - filterSelection.put(retrieverLevel, retrieverFilterSelection); - } - retrieverFilterSelection.put(dimension, dimensionFilterSelection); + HashSet dimensionFilterSelection = dimensionFilterSelectionProviders.get(dimensionWithContext).getSelection(); + if (!dimensionFilterSelection.isEmpty()) { + HashMap> retrieverFilterSelection = filterSelection.get(retrieverLevel); + if (retrieverFilterSelection == null) { + retrieverFilterSelection = new HashMap<>(); + filterSelection.put(retrieverLevel, retrieverFilterSelection); } + retrieverFilterSelection.put(dimension, dimensionFilterSelection); } } return filterSelection; diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java index 68bb72469a8..5b51514bc21 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/MultiDimensionalGroupingProvider.java @@ -8,7 +8,11 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.stream.Collectors; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; @@ -38,7 +42,10 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponent implements GroupingProvider { - private static final String GROUPING_PROVIDER_ELEMENT_STYLE = "groupingProviderElement"; + private static final String GroupingProviderStyle = "groupingProvider"; + private static final String GroupingProviderLabelStyle = "groupingProviderLabel"; + private static final String GroupingProviderElementStyle = "groupingProviderElement"; + private static final NaturalComparator NaturalComparator = new NaturalComparator(); private static final Comparator DimensionComparator = (d1, d2) -> { // Null values (to deselect the dimension) on top @@ -49,7 +56,10 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen } return NaturalComparator.compare(d1.getDisplayName(), d2.getDisplayName()); }; - + + @FunctionalInterface + private static interface DimensionMatcher extends BiPredicate { } + private final DataMiningServiceAsync dataMiningService; private final ErrorReporter errorReporter; private final Set listeners; @@ -58,8 +68,13 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen private final List> dimensionToGroupByBoxes; private boolean isAwaitingReload; + private boolean isUpdating; private DataRetrieverChainDefinitionDTO currentRetrieverChainDefinition; private final List availableDimensions; + + private Iterable dimensionsToSelect; + private DimensionMatcher dimensionMatcher; + private Consumer> selectionCallback; public MultiDimensionalGroupingProvider(Component parent, ComponentContext context, DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, @@ -74,10 +89,10 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen dimensionToGroupByBoxes = new ArrayList>(); mainPanel = new FlowPanel(); - mainPanel.addStyleName("groupingProvider"); + mainPanel.addStyleName(GroupingProviderStyle); + Label groupByLabel = new Label(this.getDataMiningStringMessages().groupBy()); - groupByLabel.addStyleName(GROUPING_PROVIDER_ELEMENT_STYLE); - groupByLabel.addStyleName("queryProviderElementLabel"); + groupByLabel.addStyleName(GroupingProviderLabelStyle); groupByLabel.addStyleName("emphasizedLabel"); mainPanel.add(groupByLabel); @@ -113,12 +128,27 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen } private void updateAvailableDimensions() { + // Try to recover the selection even if the retriever chain has been null in between + Collection currentDimensions = getDimensionsToGroupBy(); + if (!currentDimensions.isEmpty()) { + dimensionsToSelect = currentDimensions; + // Match dimensions by display name to retain dimensions of different retriever chains that appear to be equal + dimensionMatcher = (d1, d2) -> { + if (d1 == d2) return true; + if (d1 == null || d2 == null) return false; + + return d1.getDisplayName().equals(d2.getDisplayName()); + }; + selectionCallback = EmptyApplyCallback; + } + if (currentRetrieverChainDefinition != null) { + isUpdating = true; dataMiningService.getDimensionsFor(currentRetrieverChainDefinition, LocaleInfo.getCurrentLocale().getLocaleName(), new AsyncCallback>() { @Override public void onSuccess(HashSet dimensions) { - clearAvailableDimensionsAndGroupByBoxes(); + clear(); for (FunctionDTO dimension : dimensions) { availableDimensions.add(dimension); } @@ -126,31 +156,50 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen addDimensionToGroupByBoxAndUpdateAcceptableValues(firstDimensionToGroupByBox); if (!availableDimensions.isEmpty()) { Collections.sort(availableDimensions, DimensionComparator); - firstDimensionToGroupByBox.setValue(availableDimensions.iterator().next(), true); + if (dimensionsToSelect != null) { + setSelectedDimensions(dimensionsToSelect, dimensionMatcher, selectionCallback); + } else { + Optional dimensionWithLowestOrdinal = availableDimensions.stream().min((d1, d2) -> { + int ordinal1 = d1 == null ? Integer.MAX_VALUE : d1.getOrdinal(); + int ordinal2 = d2 == null ? Integer.MAX_VALUE : d2.getOrdinal(); + return Integer.compare(ordinal1, ordinal2); + }); + dimensionWithLowestOrdinal.ifPresent(d -> firstDimensionToGroupByBox.setValue(d, true)); + } + dimensionsToSelect = null; + dimensionMatcher = null; + selectionCallback = null; } else { notifyListeners(); } + isUpdating = false; } - @Override public void onFailure(Throwable caught) { - errorReporter.reportError( - "Error fetching the dimensions from the server: " + caught.getMessage()); + errorReporter.reportError("Error fetching the dimensions from the server: " + caught.getMessage()); + dimensionsToSelect = null; + dimensionMatcher = null; + selectionCallback = null; + isUpdating = false; } }); } else { - clearAvailableDimensionsAndGroupByBoxes(); + clear(); ValueListBox firstDimensionToGroupByBox = createDimensionToGroupByBox(); addDimensionToGroupByBoxAndUpdateAcceptableValues(firstDimensionToGroupByBox); } } - private void clearAvailableDimensionsAndGroupByBoxes() { - for (ValueListBox dimensionToGroupByBox : dimensionToGroupByBoxes) { - mainPanel.remove(dimensionToGroupByBox); + private void clear() { + clearDimensionBoxes(); + availableDimensions.clear(); + } + + private void clearDimensionBoxes() { + for (ValueListBox dimensionBox : dimensionToGroupByBoxes) { + dimensionBox.removeFromParent(); } dimensionToGroupByBoxes.clear(); - availableDimensions.clear(); } @Override @@ -165,6 +214,7 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen private ValueListBox createDimensionToGroupByBox() { ValueListBox dimensionToGroupByBox = createDimensionToGroupByBoxWithoutEventHandler(); + dimensionToGroupByBox.addStyleName("dataMiningListBox"); dimensionToGroupByBox.addValueChangeHandler(new ValueChangeHandler() { private boolean firstChange = true; @@ -175,9 +225,8 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen addDimensionToGroupByBox(newDimensionToGroupByBox); firstChange = false; } else if (event.getValue() == null) { - Widget changedDimensionToGroupByBox = (Widget) event.getSource(); - mainPanel.remove(changedDimensionToGroupByBox); - dimensionToGroupByBoxes.remove(changedDimensionToGroupByBox); + mainPanel.remove(dimensionToGroupByBox); + dimensionToGroupByBoxes.remove(dimensionToGroupByBox); } updateAcceptableValues(); notifyListeners(); @@ -196,7 +245,7 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen } }); - dimensionToGroupByBox.addStyleName(GROUPING_PROVIDER_ELEMENT_STYLE); + dimensionToGroupByBox.addStyleName(GroupingProviderElementStyle); return dimensionToGroupByBox; } @@ -217,8 +266,12 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen if (dimensionToGroupByBox.getValue() != null) { acceptableValues.add(dimensionToGroupByBox.getValue()); } - acceptableValues.add(null); - Collections.sort(acceptableValues, DimensionComparator); + if (!acceptableValues.isEmpty()) { + acceptableValues.add(null); + Collections.sort(acceptableValues, DimensionComparator); + } else { + dimensionToGroupByBox.setEnabled(false); + } dimensionToGroupByBox.setAcceptableValues(acceptableValues); } } @@ -240,7 +293,7 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen final ValueListBox dimensionListBox = i.next(); if (Util.equalsWithNull(dimension, dimensionListBox.getValue())) { i.remove(); - mainPanel.remove(dimensionListBox); + dimensionListBox.removeFromParent(); updateAcceptableValues(); notifyListeners(); } @@ -248,16 +301,50 @@ public class MultiDimensionalGroupingProvider extends AbstractDataMiningComponen } @Override - public String getCustomGrouperScriptText() { - return ""; + public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition, Consumer> callback) { + DataRetrieverChainDefinitionDTO newRetrieverChain = queryDefinition.getDataRetrieverChainDefinition(); + dimensionsToSelect = queryDefinition.getDimensionsToGroupBy(); + dimensionMatcher = Objects::equals; + selectionCallback = callback; + if (!isAwaitingReload && !isUpdating && currentRetrieverChainDefinition.equals(newRetrieverChain)) { + clearDimensionBoxes(); + ValueListBox firstDimensionToGroupByBox = createDimensionToGroupByBox(); + addDimensionToGroupByBoxAndUpdateAcceptableValues(firstDimensionToGroupByBox); + + setSelectedDimensions(dimensionsToSelect, dimensionMatcher, selectionCallback); + dimensionsToSelect = null; + dimensionMatcher = null; + selectionCallback = null; + } } - @Override - public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { - int index = 0; - for (FunctionDTO dimension : queryDefinition.getDimensionsToGroupBy()) { - dimensionToGroupByBoxes.get(index).setValue(dimension, true); - index++; + private void setSelectedDimensions(Iterable dimensions, DimensionMatcher matcher, Consumer> callback) { + Collection missingDimensions = new ArrayList<>(); + int boxIndex = 0; + for (FunctionDTO dimension : dimensions) { + FunctionDTO dimensionToSelect = null; + for (int i = 0; i < availableDimensions.size(); i++) { + FunctionDTO availableDimension = availableDimensions.get(i); + if (matcher.test(dimension, availableDimension)) { + dimensionToSelect = availableDimension; + break; + } + } + if (dimensionToSelect != null) { + setDimensionToGroupBy(boxIndex, dimensionToSelect); + boxIndex++; + } else { + missingDimensions.add(dimension); + } + } + + if (!missingDimensions.isEmpty()) { + String listedDimensions = missingDimensions.stream().map(FunctionDTO::getDisplayName) + .collect(Collectors.joining(", ")); + callback.accept(Collections + .singleton(getDataMiningStringMessages().groupingDimensionsAreNotAvailable(listedDimensions))); + } else { + callback.accept(Collections.emptySet()); } } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java index 3b020589f63..f6cf540a18c 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/QueryDefinitionProviderWithControls.java @@ -6,17 +6,30 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Consumer; +import com.google.gwt.dom.builder.shared.HtmlBuilderFactory; +import com.google.gwt.dom.builder.shared.HtmlUListBuilder; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.i18n.client.LocaleInfo; +import com.google.gwt.safehtml.shared.SafeHtmlBuilder; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.DialogBox; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.LayoutPanel; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; import com.google.gwt.user.client.ui.ToggleButton; import com.google.gwt.user.client.ui.ValueListBox; +import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.common.Util; import com.sap.sse.common.settings.SerializableSettings; @@ -37,13 +50,15 @@ import com.sap.sse.datamining.ui.client.FilterSelectionChangedListener; import com.sap.sse.datamining.ui.client.FilterSelectionProvider; import com.sap.sse.datamining.ui.client.GroupingChangedListener; import com.sap.sse.datamining.ui.client.GroupingProvider; -import com.sap.sse.datamining.ui.client.ResultsPresenter; import com.sap.sse.datamining.ui.client.StatisticChangedListener; import com.sap.sse.datamining.ui.client.StatisticProvider; +import com.sap.sse.datamining.ui.client.StringMessages; import com.sap.sse.datamining.ui.client.WithControls; import com.sap.sse.datamining.ui.client.developer.PredefinedQueryRunner; import com.sap.sse.datamining.ui.client.developer.QueryDefinitionViewer; +import com.sap.sse.datamining.ui.client.selection.statistic.SuggestBoxStatisticProvider; import com.sap.sse.datamining.ui.client.settings.AdvancedDataMiningSettings; +import com.sap.sse.datamining.ui.client.settings.AdvancedDataMiningSettings.ChangeLossStrategy; import com.sap.sse.datamining.ui.client.settings.AdvancedDataMiningSettingsDialogComponent; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.dialog.DataEntryDialog; @@ -55,17 +70,20 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class QueryDefinitionProviderWithControls extends AbstractQueryDefinitionProvider implements WithControls { - private static final double HeaderPanelHeight = 45; - private static final double FooterPanelHeight = 50; + private static final double HeaderPanelHeight = 41; + private static final double InitialQueryDefinitionViewerWidth = 600; + private static final double InitialStatisticProviderWidth = 800; + private static final double FooterPanelHeight = 60; private static final int SplitterSize = 10; - private final DockLayoutPanel mainPanel; + private final Panel mainPanel; private final FlowPanel controlsPanel; + private final DataMiningSettingsControl settingsControl; + private final AdvancedDataMiningSettings settings; + private final Button reloadComponentsButton; private final ToggleButton queryDefinitionViewerToggleButton; private final QueryDefinitionViewer queryDefinitionViewer; private final PredefinedQueryRunner predefinedQueryRunner; - private final DataMiningSettingsControl settingsControl; - private final AdvancedDataMiningSettings settings; private final ProviderListener providerListener; private final Collection> providers; @@ -74,16 +92,22 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition private final GroupingProvider groupingProvider; private final SplitLayoutPanel filterSplitPanel; private final FilterSelectionProvider filterSelectionProvider; + + private final Panel applyQueryBusyIndicator; + + private final DialogBox confirmChangeLossDialog; + private StatisticQueryDefinitionDTO queryDefinitionToBeApplied; + private boolean queryDefinitionChanged; public QueryDefinitionProviderWithControls(Component parent, ComponentContext context, DataMiningSession session, DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, DataMiningSettingsControl settingsControl, DataMiningSettingsInfoManager settingsManager, - ResultsPresenter resultsPresenter) { + Consumer queryRunner) { super(parent, context, dataMiningService, errorReporter); providerListener = new ProviderListener(); // Creating the header panel, that contains the retriever chain provider and the controls controlsPanel = new FlowPanel(); - controlsPanel.addStyleName("definitionProviderControls"); + controlsPanel.addStyleName("dataMiningMarginBase"); this.settingsControl = settingsControl; addControl(this.settingsControl.getEntryWidget()); @@ -94,44 +118,38 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition new ClickHandler() { @Override public void onClick(ClickEvent event) { - boolean active = queryDefinitionViewerToggleButton.isDown(); - filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), !active); - queryDefinitionViewer.setActive(active); + boolean active = queryDefinitionViewerToggleButton.isDown(); + filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), !active); + queryDefinitionViewer.setActive(active); } }); queryDefinitionViewer = new QueryDefinitionViewer(parent, context, getDataMiningStringMessages()); queryDefinitionViewer.getEntryWidget().addStyleName("dataMiningMarginRight"); queryDefinitionViewer.setActive(false); addQueryDefinitionChangedListener(queryDefinitionViewer); - predefinedQueryRunner = new PredefinedQueryRunner(parent, context, session, getDataMiningStringMessages(), - dataMiningService, errorReporter, resultsPresenter); + predefinedQueryRunner = new PredefinedQueryRunner(parent, context, getDataMiningStringMessages(), + dataMiningService, errorReporter, this, queryRunner); - Button clearSelectionButton = new Button(getDataMiningStringMessages().clearSelection()); - clearSelectionButton.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - filterSelectionProvider.clearSelection(); - } - }); - addControl(clearSelectionButton); - - Button reloadButton = new Button(getDataMiningStringMessages().reload()); - reloadButton.addClickHandler(new ClickHandler() { + reloadComponentsButton = new Button(getDataMiningStringMessages().reloadComponents()); + reloadComponentsButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { reloadComponents(); } }); - addControl(reloadButton); if (settings.isDeveloperOptions()) { + addControl(reloadComponentsButton); addControl(queryDefinitionViewerToggleButton); addControl(predefinedQueryRunner.getEntryWidget()); } + + confirmChangeLossDialog = createConfirmChangeLossDialog(); statisticProvider = new SuggestBoxStatisticProvider(parent, context, dataMiningService, errorReporter, settingsControl, settingsManager); - statisticProvider.getEntryWidget().addStyleName("statisticProvider"); + Widget statisticProviderWidget = statisticProvider.getEntryWidget(); + statisticProviderWidget.addStyleName("dataMiningMarginBase"); statisticProvider.addStatisticChangedListener(providerListener); groupingProvider = new MultiDimensionalGroupingProvider(parent, context, dataMiningService, @@ -139,25 +157,35 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition groupingProvider.addGroupingChangedListener(providerListener); groupingProvider.getEntryWidget().addStyleName("dataMiningMarginBase"); - filterSelectionProvider = new HierarchicalDimensionListFilterSelectionProvider( - parent, context, session, getDataMiningStringMessages(), dataMiningService, errorReporter, statisticProvider); + filterSelectionProvider = new HierarchicalDimensionListFilterSelectionProvider(parent, context, session, + dataMiningService, errorReporter, statisticProvider); filterSelectionProvider.addSelectionChangedListener(providerListener); filterSelectionProvider.getEntryWidget().addStyleName("dataMiningBorderTop"); filterSplitPanel = new SplitLayoutPanel(SplitterSize); filterSplitPanel.addSouth(groupingProvider.getEntryWidget(), FooterPanelHeight); - filterSplitPanel.addEast(queryDefinitionViewer.getEntryWidget(), 600); + filterSplitPanel.addEast(queryDefinitionViewer.getEntryWidget(), InitialQueryDefinitionViewerWidth); filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), true); filterSplitPanel.add(filterSelectionProvider.getEntryWidget()); + + Widget glass = new SimplePanel(); + glass.addStyleName("whiteGlass"); + HTML labeledBusyIndicator = new HTML(SafeHtmlUtils.fromString(getDataMiningStringMessages().applyingQuery())); + labeledBusyIndicator.setStyleName("applyQueryBusyMessage"); + applyQueryBusyIndicator = new LayoutPanel(); + applyQueryBusyIndicator.add(glass); + applyQueryBusyIndicator.add(labeledBusyIndicator); SplitLayoutPanel headerPanel = new SplitLayoutPanel(SplitterSize); - headerPanel.addStyleName("dataMiningMarginBase"); - headerPanel.addWest(statisticProvider.getEntryWidget(), 800); + headerPanel.addWest(statisticProviderWidget, InitialStatisticProviderWidth); headerPanel.add(controlsPanel); - mainPanel = new DockLayoutPanel(Unit.PX); - mainPanel.addNorth(headerPanel, HeaderPanelHeight); - mainPanel.add(filterSplitPanel); + DockLayoutPanel contentPanel = new DockLayoutPanel(Unit.PX); + contentPanel.addNorth(headerPanel, HeaderPanelHeight); + contentPanel.add(filterSplitPanel); + + mainPanel = new LayoutPanel(); + mainPanel.add(contentPanel); // Storing the different component providers in a list providers = new ArrayList<>(); @@ -167,6 +195,54 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition reloadComponents(); } + private DialogBox createConfirmChangeLossDialog() { + StringMessages stringMessages = getDataMiningStringMessages(); + + DialogBox dialog = new DialogBox(false, true); + dialog.setAnimationEnabled(true); + dialog.setText(stringMessages.changesWillBeLost()); + dialog.setGlassEnabled(true); + + VerticalPanel contentPanel = new VerticalPanel(); + contentPanel.setSpacing(5); + contentPanel.add(new HTML(new SafeHtmlBuilder() + .appendEscapedLines(stringMessages.confirmQueryDefinitionChangeLoss()).toSafeHtml())); + + CheckBox rememberDecisionCheckBox = new CheckBox(stringMessages.rememberDecisionCanBeChangedInSettings()); + contentPanel.add(rememberDecisionCheckBox); + + FlowPanel buttonPanel = new FlowPanel(); + buttonPanel.addStyleName("floatRight"); + contentPanel.add(buttonPanel); + + Button discardChanges = new Button(stringMessages.discardChanges()); + discardChanges.addClickHandler(e -> { + dialog.hide(); + setQueryDefinition(queryDefinitionToBeApplied); + queryDefinitionToBeApplied = null; + if (rememberDecisionCheckBox.getValue()) { + settings.setChangeLossStrategy(ChangeLossStrategy.DISCARD_CHANGES); + rememberDecisionCheckBox.setValue(false); + } + }); + discardChanges.addStyleName("dataMiningMarginLeft"); + buttonPanel.add(discardChanges); + + Button keepChanges = new Button(stringMessages.keepChanges()); + keepChanges.addClickHandler(e -> { + dialog.hide(); + if (rememberDecisionCheckBox.getValue()) { + settings.setChangeLossStrategy(ChangeLossStrategy.KEEP_CHANGES); + rememberDecisionCheckBox.setValue(false); + } + }); + keepChanges.addStyleName("dataMiningMarginLeft"); + buttonPanel.add(keepChanges); + + dialog.setWidget(contentPanel); + return dialog; + } + /** * The first {@link FunctionDTO dimension} of which the {@code groupKey} is assumed to be an instance will be * removed from the {@link #groupingProvider}. A filter criterion for that dimension will be added to the @@ -184,61 +260,22 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition */ public void drillDown(GroupKey groupKey, Runnable onSuccessCallback) { assert groupKey instanceof GenericGroupKey; - final GenericGroupKey groupKeyForSingleDimension = (GenericGroupKey) groupKey; + final Serializable filterValue = (Serializable) ((GenericGroupKey) groupKey).getValue(); final Collection dimensionsToGroupBy = groupingProvider.getDimensionsToGroupBy(); if (!dimensionsToGroupBy.isEmpty()) { - final FunctionDTO firstDimension = dimensionsToGroupBy.iterator().next(); + final FunctionDTO firstDimension = Util.first(dimensionsToGroupBy); if (dimensionsToGroupBy.size() == 1) { - letUserSelectADifferentFirstDimension(() -> { - filterSelectionProvider.setHighestRetrieverLevelWithFilterDimension(firstDimension, - (Serializable) groupKeyForSingleDimension.getValue()); - onSuccessCallback.run(); + showSelectNewFirstDimensionDialog(() -> { + filterSelectionProvider.setFilterSelectionForUnreducedDimension(firstDimension, filterValue, onSuccessCallback); }); } else { - filterSelectionProvider.setHighestRetrieverLevelWithFilterDimension(firstDimension, - (Serializable) groupKeyForSingleDimension.getValue()); groupingProvider.removeDimensionToGroupBy(firstDimension); - onSuccessCallback.run(); + filterSelectionProvider.setFilterSelectionForUnreducedDimension(firstDimension, filterValue, onSuccessCallback); } } } - private class FirstDimensionSelectionDialog extends DataEntryDialog { - private ValueListBox dimensionChooser; - - public FirstDimensionSelectionDialog(DialogCallback callback) { - super(getDataMiningStringMessages().chooseDifferentDimensionTitle(), - getDataMiningStringMessages().chooseDifferentDimensionMessage(), getDataMiningStringMessages().ok(), - getDataMiningStringMessages().cancel(), new DataEntryDialog.Validator() { - @Override - public String getErrorMessage(FunctionDTO valueToValidate) { - if (valueToValidate == null) { - return getDataMiningStringMessages().pleaseSelectADimension(); - } else { - return null; - } - } - }, callback); - dimensionChooser = groupingProvider.createDimensionToGroupByBoxWithoutEventHandler(); - Collection acceptableValues = new ArrayList<>(); - Util.addAll(groupingProvider.getAvailableDimensions(), acceptableValues); - acceptableValues.add(null); - dimensionChooser.setAcceptableValues(acceptableValues); - dimensionChooser.addValueChangeHandler(e -> validateAndUpdate()); - } - - @Override - protected Widget getAdditionalWidget() { - return dimensionChooser; - } - - @Override - protected FunctionDTO getResult() { - return dimensionChooser.getValue(); - } - } - - private void letUserSelectADifferentFirstDimension(final Runnable onSuccessCallback) { + private void showSelectNewFirstDimensionDialog(final Runnable onSuccessCallback) { final FirstDimensionSelectionDialog dialog = new FirstDimensionSelectionDialog( new DialogCallback() { @Override @@ -254,13 +291,7 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition dialog.show(); } - @Override - public void awaitReloadComponents() { - // Nothing to do here - } - - @Override - public boolean isAwaitingReload() { + public boolean providersAwaitingReload() { for (DataMiningComponentProvider provider : providers) { if (provider.isAwaitingReload()) { return true; @@ -298,12 +329,109 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition @Override public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { + applyQueryDefinition(queryDefinition, settings.getChangeLossStrategy()); + } + + /** + * Displays the given query definition, using the given {@link ChangeLossStrategy} to handle a possible loss of changes. + * @param queryDefinition The query to be displayed + * @param strategy The strategy to use if the current query definition has been changed + */ + public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition, ChangeLossStrategy strategy) { + if (queryDefinitionChanged) { + switch (strategy) { + case ASK: + queryDefinitionToBeApplied = queryDefinition; + confirmChangeLossDialog.center(); + break; + case DISCARD_CHANGES: + setQueryDefinition(queryDefinition); + break; + case KEEP_CHANGES: + // Do nothing + break; + } + } else { + setQueryDefinition(queryDefinition); + } + + } + + private void setQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { + Set callbacks = new HashSet<>(); + Collection errorMessages = new ArrayList<>(); + String retrieverChainName = queryDefinition.getDataRetrieverChainDefinition().getName(); + setBlockChangeNotification(true); - statisticProvider.applyQueryDefinition(queryDefinition); - groupingProvider.applyQueryDefinition(queryDefinition); - filterSelectionProvider.applyQueryDefinition(queryDefinition); + + ApplyCallback statisticCallback = new ApplyCallback(errorMessages, callbacks, retrieverChainName); + callbacks.add(statisticCallback); + statisticProvider.applyQueryDefinition(queryDefinition, statisticCallback); + // The statistic wasn't available, if the callback was called immediately and an error occurred + // Applying the query to the remaining component providers can be skipped + if (!callbacks.isEmpty() || errorMessages.isEmpty()) { + ApplyCallback groupingCallback = new ApplyCallback(errorMessages, callbacks, retrieverChainName); + callbacks.add(groupingCallback); + groupingProvider.applyQueryDefinition(queryDefinition, groupingCallback); + + ApplyCallback filterCallback = new ApplyCallback(errorMessages, callbacks, retrieverChainName); + callbacks.add(filterCallback); + filterSelectionProvider.applyQueryDefinition(queryDefinition, filterCallback); + } + + if (!callbacks.isEmpty()) { + for (ApplyCallback callback : callbacks) { + callback.isArmed = true; + } + if (applyQueryBusyIndicator.getParent() == null) { + mainPanel.add(applyQueryBusyIndicator); + } + } else { + applyQueryDefinitionCompleted(errorMessages, retrieverChainName); + } + } + + private void applyQueryDefinitionCompleted(Collection errorMessages, String retrieverChainName) { + if (!errorMessages.isEmpty()) { + showErrorWhileApplyingQueryDialog(errorMessages, retrieverChainName); + } + applyQueryBusyIndicator.removeFromParent(); setBlockChangeNotification(false); - notifyQueryDefinitionChanged(); + queryDefinitionChanged = false; + } + + private void showErrorWhileApplyingQueryDialog(Iterable errorMessages, String retrieverChainName) { + StringMessages stringMessages = getDataMiningStringMessages(); + + DialogBox dialog = new DialogBox(false, true); + dialog.setText(stringMessages.anErrorOccurredWhileApplyingTheQuery()); + dialog.setAnimationEnabled(true); + dialog.setGlassEnabled(true); + + VerticalPanel contentPanel = new VerticalPanel(); + contentPanel.add(new HTML(SafeHtmlUtils.fromString(stringMessages.queryBasedOnRetrieverChainCanNotBeApplied(retrieverChainName)))); + + HtmlUListBuilder messagesBuilder = HtmlBuilderFactory.get().createUListBuilder(); + for (String errorMessage : errorMessages) { + messagesBuilder.startLI().text(errorMessage).end(); + } + contentPanel.add(new HTML(messagesBuilder.asSafeHtml())); + + FlowPanel buttonPanel = new FlowPanel(); + buttonPanel.addStyleName("floatRight"); + contentPanel.add(buttonPanel); + + Button okButton = new Button(stringMessages.ok()); + okButton.addClickHandler(e -> dialog.hide()); + buttonPanel.add(okButton); + + dialog.setWidget(contentPanel); + dialog.center(); + } + + @Override + public void queryDefinitionChangesHaveBeenStored() { + queryDefinitionChanged = false; } @Override @@ -358,18 +486,21 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition if (settings.isDeveloperOptions() != newSettings.isDeveloperOptions()) { settings.setDeveloperOptions(newSettings.isDeveloperOptions()); if (settings.isDeveloperOptions()) { + addControl(reloadComponentsButton); addControl(queryDefinitionViewerToggleButton); boolean queryDefinitionViewerActive = queryDefinitionViewerToggleButton.isDown(); filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), !queryDefinitionViewerActive); queryDefinitionViewer.setActive(queryDefinitionViewerActive); addControl(predefinedQueryRunner.getEntryWidget()); } else { + removeControl(reloadComponentsButton); removeControl(queryDefinitionViewerToggleButton); filterSplitPanel.setWidgetHidden(queryDefinitionViewer.getEntryWidget(), true); queryDefinitionViewer.setActive(false); removeControl(predefinedQueryRunner.getEntryWidget()); } } + settings.setChangeLossStrategy(newSettings.getChangeLossStrategy()); } @Override @@ -382,49 +513,107 @@ public class QueryDefinitionProviderWithControls extends AbstractQueryDefinition return "QueryDefinitionProviderWithControls"; } + private class ApplyCallback implements Consumer> { + + private final Collection allMessages; + private final Set callbacks; + private final String retrieverChainName; + private boolean isArmed; + + public ApplyCallback(Collection allMessages, Set callbacks, String retrieverChainName) { + this.allMessages = allMessages; + this.callbacks = callbacks; + this.retrieverChainName = retrieverChainName; + } + + @Override + public void accept(Iterable messages) { + Util.addAll(messages, allMessages); + callbacks.remove(this); + if (isArmed && callbacks.isEmpty()) { + applyQueryDefinitionCompleted(allMessages, retrieverChainName); + } + } + + } + private class ProviderListener implements StatisticChangedListener, FilterSelectionChangedListener, GroupingChangedListener { @Override public void dataRetrieverChainDefinitionChanged(DataRetrieverChainDefinitionDTO newDataRetrieverChainDefinition) { - if (isAwaitingReload()) { + if (providersAwaitingReload()) { groupingProvider.dataRetrieverChainDefinitionChanged(newDataRetrieverChainDefinition); groupingProvider.reloadComponents(); filterSelectionProvider.dataRetrieverChainDefinitionChanged(newDataRetrieverChainDefinition); filterSelectionProvider.reloadComponents(); } else { - notifyQueryDefinitionChanged(); + queryDefinitionChanged(); } } @Override public void aggregatorDefinitionChanged(AggregationProcessorDefinitionDTO newAggregatorDefinition) { - if (!isAwaitingReload()) { - notifyQueryDefinitionChanged(); - } + queryDefinitionChanged(); } @Override public void extractionFunctionChanged(FunctionDTO extractionFunction) { - if (!isAwaitingReload()) { - notifyQueryDefinitionChanged(); - } + queryDefinitionChanged(); } @Override public void groupingChanged() { - if (!isAwaitingReload()) { - notifyQueryDefinitionChanged(); - } + queryDefinitionChanged(); } @Override public void selectionChanged() { - if (!isAwaitingReload()) { + queryDefinitionChanged(); + } + + private void queryDefinitionChanged() { + if (!providersAwaitingReload()) { + queryDefinitionChanged = true; notifyQueryDefinitionChanged(); } } } + private class FirstDimensionSelectionDialog extends DataEntryDialog { + private ValueListBox dimensionChooser; + + public FirstDimensionSelectionDialog(DialogCallback callback) { + super(getDataMiningStringMessages().chooseDifferentDimensionTitle(), + getDataMiningStringMessages().chooseDifferentDimensionMessage(), getDataMiningStringMessages().ok(), + getDataMiningStringMessages().cancel(), new DataEntryDialog.Validator() { + @Override + public String getErrorMessage(FunctionDTO valueToValidate) { + if (valueToValidate == null) { + return getDataMiningStringMessages().pleaseSelectADimension(); + } else { + return null; + } + } + }, callback); + dimensionChooser = groupingProvider.createDimensionToGroupByBoxWithoutEventHandler(); + Collection acceptableValues = new ArrayList<>(); + Util.addAll(groupingProvider.getAvailableDimensions(), acceptableValues); + acceptableValues.add(null); + dimensionChooser.setAcceptableValues(acceptableValues); + dimensionChooser.addValueChangeHandler(e -> validateAndUpdate()); + } + + @Override + protected Widget getAdditionalWidget() { + return dimensionChooser; + } + + @Override + protected FunctionDTO getResult() { + return dimensionChooser.getValue(); + } + } + } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SimpleDataRetrieverChainDefinitionProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SimpleDataRetrieverChainDefinitionProvider.java deleted file mode 100644 index 2e426dac9b3..00000000000 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SimpleDataRetrieverChainDefinitionProvider.java +++ /dev/null @@ -1,307 +0,0 @@ -package com.sap.sse.datamining.ui.client.selection; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.text.shared.AbstractRenderer; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.ValueListBox; -import com.google.gwt.user.client.ui.Widget; -import com.sap.sse.common.settings.SerializableSettings; -import com.sap.sse.common.settings.Settings; -import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; -import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; -import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; -import com.sap.sse.datamining.ui.client.DataMiningSettingsControl; -import com.sap.sse.datamining.ui.client.DataMiningSettingsInfo; -import com.sap.sse.datamining.ui.client.DataMiningSettingsInfoManager; -import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionChangedListener; -import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionProvider; -import com.sap.sse.gwt.client.ErrorReporter; -import com.sap.sse.gwt.client.shared.components.Component; -import com.sap.sse.gwt.client.shared.components.CompositeSettings; -import com.sap.sse.gwt.client.shared.components.CompositeTabbedSettingsDialogComponent; -import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; -import com.sap.sse.gwt.client.shared.settings.ComponentContext; - -public class SimpleDataRetrieverChainDefinitionProvider extends AbstractDataMiningComponent - implements DataRetrieverChainDefinitionProvider { - - private final DataMiningServiceAsync dataMiningService; - private final ErrorReporter errorReporter; - private final Set listeners; - private boolean isAwaitingReload; - - private final HorizontalPanel mainPanel; - private final ValueListBox retrieverChainsListBox; - - private final DataMiningSettingsInfoManager settingsManager; - private final DataMiningSettingsControl settingsControl; - private final Map> settingsMap; - - private final List> retrieverLevelSettingsComponents; - - public SimpleDataRetrieverChainDefinitionProvider(Component parent, ComponentContext context, - DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, - DataMiningSettingsControl settingsControl, DataMiningSettingsInfoManager settingsManager) { - super(parent, context); - this.dataMiningService = dataMiningService; - this.errorReporter = errorReporter; - this.retrieverLevelSettingsComponents = new ArrayList<>(); - listeners = new HashSet<>(); - isAwaitingReload = false; - this.settingsManager = settingsManager; - this.settingsControl = settingsControl; - this.settingsControl.addSettingsComponent(this); - settingsMap = new HashMap<>(); - - mainPanel = new HorizontalPanel(); - mainPanel.setSpacing(5); - mainPanel.add(new Label(getDataMiningStringMessages().analyze())); - - retrieverChainsListBox = createRetrieverChainsListBox(); - mainPanel.add(retrieverChainsListBox); - - updateRetrieverChains(); - } - - private ValueListBox createRetrieverChainsListBox() { - ValueListBox retrieverChainsListBox = new ValueListBox<>( - new AbstractRenderer() { - @Override - public String render(DataRetrieverChainDefinitionDTO retrieverChain) { - return retrieverChain == null ? "" : retrieverChain.getName(); - } - - }); - retrieverChainsListBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - notifyListeners(); - } - }); - return retrieverChainsListBox; - } - - @Override - public void awaitReloadComponents() { - isAwaitingReload = true; - } - - @Override - public boolean isAwaitingReload() { - return isAwaitingReload; - } - - @Override - public void reloadComponents() { - updateRetrieverChains(); - } - - private void updateRetrieverChains() { - dataMiningService.getDataRetrieverChainDefinitions(LocaleInfo.getCurrentLocale().getLocaleName(), - new AsyncCallback>() { - @Override - public void onSuccess(ArrayList dataRetrieverChainDefinitions) { - settingsMap.clear(); - if (!dataRetrieverChainDefinitions.isEmpty()) { - fillSettingsMap(dataRetrieverChainDefinitions); - Collections.sort(dataRetrieverChainDefinitions); - - DataRetrieverChainDefinitionDTO currentRetrieverChain = getDataRetrieverChainDefinition(); - DataRetrieverChainDefinitionDTO retrieverChainToBeSelected = dataRetrieverChainDefinitions - .contains(currentRetrieverChain) ? currentRetrieverChain - : dataRetrieverChainDefinitions.get(0); - - retrieverChainsListBox.setValue(retrieverChainToBeSelected); - retrieverChainsListBox.setAcceptableValues(dataRetrieverChainDefinitions); - - if (isAwaitingReload || !retrieverChainToBeSelected.equals(currentRetrieverChain)) { - isAwaitingReload = false; - notifyListeners(); - } - } else { - retrieverChainsListBox.setValue(null); - retrieverChainsListBox - .setAcceptableValues(new ArrayList()); - notifyListeners(); - } - } - - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError( - "Error retrieving the available DataRetrieverChainDefinitions: " + caught.getMessage()); - } - }); - } - - private void fillSettingsMap(ArrayList retrieverChains) { - for (DataRetrieverChainDefinitionDTO retrieverChain : retrieverChains) { - if (retrieverChain.hasSettings()) { - settingsMap.put(retrieverChain, retrieverChain.getDefaultSettings()); - } - } - } - - @Override - public DataRetrieverChainDefinitionDTO getDataRetrieverChainDefinition() { - return retrieverChainsListBox.getValue(); - } - - @Override - public void addDataRetrieverChainDefinitionChangedListener(DataRetrieverChainDefinitionChangedListener listener) { - listeners.add(listener); - } - - private void notifyListeners() { - DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition = getDataRetrieverChainDefinition(); - for (DataRetrieverChainDefinitionChangedListener listener : listeners) { - listener.dataRetrieverChainDefinitionChanged(dataRetrieverChainDefinition); - } - } - - @Override - public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { - retrieverChainsListBox.setValue(queryDefinition.getDataRetrieverChainDefinition(), false); - } - - @Override - public String getLocalizedShortName() { - return getDataMiningStringMessages().dataMiningRetrieval(); - } - - @Override - public Widget getEntryWidget() { - return mainPanel; - } - - @Override - public boolean isVisible() { - return mainPanel.isVisible(); - } - - @Override - public void setVisible(boolean visibility) { - mainPanel.setVisible(visibility); - } - - @Override - public String getDependentCssClassName() { - return "simpleDataRetrieverChainDefinition"; - } - - @Override - public HashMap getRetrieverSettings() { - if (settingsMap.containsKey(getDataRetrieverChainDefinition())) { - return settingsMap.get(getDataRetrieverChainDefinition()); - } else { - return new HashMap<>(); - } - } - - @Override - public boolean hasSettings() { - return getDataRetrieverChainDefinition().hasSettings(); - } - - @Override - public SettingsDialogComponent getSettingsDialogComponent(CompositeSettings settings) { - retrieverLevelSettingsComponents.clear(); - retrieverLevelSettingsComponents.addAll(createSettingsComponentsFor(getDataRetrieverChainDefinition())); - return new CompositeTabbedSettingsDialogComponent(retrieverLevelSettingsComponents); - } - - private List> createSettingsComponentsFor(final DataRetrieverChainDefinitionDTO retrieverChain) { - List> settingsComponents = new ArrayList<>(); - for (Entry retrieverLevelSettings : settingsMap.get(retrieverChain) - .entrySet()) { - final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); - final Class settingsType = retrieverLevelSettings.getValue().getClass(); - DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); - settingsComponents.add(new RetrieverLevelSettingsComponent(SimpleDataRetrieverChainDefinitionProvider.this, - getComponentContext(), retrieverLevel, settingsInfo.getId(), - settingsInfo.getLocalizedName()) { - @Override - public SettingsDialogComponent getSettingsDialogComponent( - SerializableSettings settings) { - return settingsManager.getSettingsInfo(settingsType) - .createSettingsDialogComponent(settingsMap.get(retrieverChain).get(retrieverLevel)); - } - - @Override - public void updateSettings(SerializableSettings newSettings) { - settingsMap.get(retrieverChain).put(retrieverLevel, newSettings); - } - }); - } - return settingsComponents; - } - - @Override - public void updateSettings(CompositeSettings newSettings) { - Map chainSettings = settingsMap - .get(getDataRetrieverChainDefinition()); - for (Entry settingsPerComponent : newSettings.getSettingsPerComponentId().entrySet()) { - RetrieverLevelSettingsComponent component = (RetrieverLevelSettingsComponent) findComponentById( - settingsPerComponent.getKey()); - SerializableSettings settings = (SerializableSettings) settingsPerComponent.getValue(); - chainSettings.put(component.getRetrieverLevel(), settings); - } - notifyListeners(); - } - - private Component findComponentById(String componentId) { - for (Component component : retrieverLevelSettingsComponents) { - if (component.getId().equals(componentId)) { - return component; - } - } - return null; - } - - @Override - public CompositeSettings getSettings() { - Map settings = new HashMap<>(); - for (Entry retrieverLevelSettings : settingsMap - .get(getDataRetrieverChainDefinition()).entrySet()) { - final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); - final Class settingsType = retrieverLevelSettings.getValue().getClass(); - DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); - RetrieverLevelSettingsComponent c = new RetrieverLevelSettingsComponent( - SimpleDataRetrieverChainDefinitionProvider.this, getComponentContext(), retrieverLevel, - settingsInfo.getId(), settingsInfo.getLocalizedName()) { - @Override - public SettingsDialogComponent getSettingsDialogComponent( - SerializableSettings settings) { - return null; - } - - @Override - public void updateSettings(SerializableSettings newSettings) { - } - }; - settings.put(c.getId(), c.hasSettings() ? c.getSettings() : null); - } - - return new CompositeSettings(settings); - } - - @Override - public String getId() { - return "SimpleDataRetrieverChainDefinitionProvider"; - } -} diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java deleted file mode 100644 index 9e489e8bda5..00000000000 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/SuggestBoxStatisticProvider.java +++ /dev/null @@ -1,630 +0,0 @@ -package com.sap.sse.datamining.ui.client.selection; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; - -import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.i18n.client.LocaleInfo; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.MenuItem; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay; -import com.google.gwt.user.client.ui.ValueListBox; -import com.google.gwt.user.client.ui.Widget; -import com.sap.sse.common.Util; -import com.sap.sse.common.settings.SerializableSettings; -import com.sap.sse.common.settings.Settings; -import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; -import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; -import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; -import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; -import com.sap.sse.datamining.ui.client.AggregatorDefinitionChangedListener; -import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; -import com.sap.sse.datamining.ui.client.DataMiningSettingsControl; -import com.sap.sse.datamining.ui.client.DataMiningSettingsInfo; -import com.sap.sse.datamining.ui.client.DataMiningSettingsInfoManager; -import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionChangedListener; -import com.sap.sse.datamining.ui.client.ExtractionFunctionChangedListener; -import com.sap.sse.datamining.ui.client.StatisticChangedListener; -import com.sap.sse.datamining.ui.client.StatisticProvider; -import com.sap.sse.gwt.client.ErrorReporter; -import com.sap.sse.gwt.client.shared.components.Component; -import com.sap.sse.gwt.client.shared.components.CompositeSettings; -import com.sap.sse.gwt.client.shared.components.CompositeTabbedSettingsDialogComponent; -import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; -import com.sap.sse.gwt.client.shared.controls.AbstractObjectRenderer; -import com.sap.sse.gwt.client.shared.settings.ComponentContext; -import com.sap.sse.gwt.client.suggestion.AbstractListSuggestOracle; -import com.sap.sse.gwt.client.suggestion.CustomSuggestBox; - -/** - * A {@link StatisticProvider} that contains all statistics registered in the server. Each statistic is paired with the - * corresponding {@link DataRetrieverChainDefinitionDTO}. This provides a more comfortable access to the available - * statistics, without the need to specify the domain to analyze. - * - * @author Lennart Hensler - */ -public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent - implements StatisticProvider { - - private static final String STATISTIC_PROVIDER_ELEMENT_STYLE = "statisticProviderElement"; - - private final DataMiningServiceAsync dataMiningService; - private final ErrorReporter errorReporter; - private final Set retrieverChainListeners; - private final Set extractionFunctionListeners; - private final Set aggregatorDefinitionListeners; - private boolean isAwaitingReload; - private int awaitingRetrieverChainStatistics; - - private final DataMiningSettingsInfoManager settingsManager; - private final DataMiningSettingsControl settingsControl; - private final Map> settingsMap; - private final List> retrieverLevelSettingsComponents; - - private final FlowPanel mainPanel; - private final List availableExtractionFunctions; - private final ExtractionFunctionSuggestBox extractionFunctionSuggestBox; - private final ValueListBox aggregatorListBox; - - public SuggestBoxStatisticProvider(Component parent, ComponentContext componentContext, - DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, - DataMiningSettingsControl settingsControl, DataMiningSettingsInfoManager settingsManager) { - super(parent, componentContext); - this.dataMiningService = dataMiningService; - this.errorReporter = errorReporter; - retrieverChainListeners = new HashSet<>(); - extractionFunctionListeners = new HashSet<>(); - aggregatorDefinitionListeners = new HashSet<>(); - isAwaitingReload = false; - this.settingsManager = settingsManager; - this.settingsControl = settingsControl; - this.settingsControl.addSettingsComponent(this); - settingsMap = new HashMap<>(); - retrieverLevelSettingsComponents = new ArrayList<>(); - - mainPanel = new FlowPanel(); - Label label = new Label(getDataMiningStringMessages().calculateThe()); - label.addStyleName(STATISTIC_PROVIDER_ELEMENT_STYLE); - label.addStyleName("queryProviderElementLabel"); - label.addStyleName("emphasizedLabel"); - mainPanel.add(label); - - availableExtractionFunctions = new ArrayList<>(); - extractionFunctionSuggestBox = new ExtractionFunctionSuggestBox() { - @Override - protected void onValueChange() { - notifyRetrieverChainListeners(); - notifyExtractionFunctionListeners(); - updateAggregators(); - } - }; - extractionFunctionSuggestBox.getValueBox().addFocusHandler(e -> { - extractionFunctionSuggestBox.getValueBox().selectAll(); - extractionFunctionSuggestBox.showSuggestionList(); - }); - extractionFunctionSuggestBox.getValueBox().addKeyUpHandler(e -> { - int keyCode = e.getNativeEvent().getKeyCode(); - if (keyCode == KeyCodes.KEY_ESCAPE) { - extractionFunctionSuggestBox.hideSuggestionList(); - extractionFunctionSuggestBox.setFocus(false); - } - }); - extractionFunctionSuggestBox.setLimit(Integer.MAX_VALUE); - extractionFunctionSuggestBox.addStyleName(STATISTIC_PROVIDER_ELEMENT_STYLE); - extractionFunctionSuggestBox.setWidth("540px"); - mainPanel.add(extractionFunctionSuggestBox); - - aggregatorListBox = createAggregatorListBox(); - aggregatorListBox.addStyleName(STATISTIC_PROVIDER_ELEMENT_STYLE); - mainPanel.add(aggregatorListBox); - } - - private ValueListBox createAggregatorListBox() { - ValueListBox aggregatorListBox = new ValueListBox( - new AbstractObjectRenderer() { - @Override - protected String convertObjectToString(AggregationProcessorDefinitionDTO nonNullObject) { - return nonNullObject.getDisplayName(); - } - }); - aggregatorListBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - notifyAggregatorDefinitionListeners(); - } - }); - return aggregatorListBox; - } - - @Override - public void awaitReloadComponents() { - isAwaitingReload = true; - } - - @Override - public boolean isAwaitingReload() { - return isAwaitingReload; - } - - @Override - public void reloadComponents() { - isAwaitingReload = false; - updateContent(); - } - - private void updateContent() { - final String localeName = LocaleInfo.getCurrentLocale().getLocaleName(); - dataMiningService.getDataRetrieverChainDefinitions(localeName, - new AsyncCallback>() { - @Override - public void onSuccess(ArrayList dataRetrieverChainDefinitions) { - settingsMap.clear(); - awaitingRetrieverChainStatistics = dataRetrieverChainDefinitions.size(); - availableExtractionFunctions.clear(); - if (awaitingRetrieverChainStatistics == 0) { - extractionFunctionSuggestBox.setSelectableValues(Collections.emptyList()); - } else { - for (DataRetrieverChainDefinitionDTO retrieverChain : dataRetrieverChainDefinitions) { - if (retrieverChain.hasSettings()) { - settingsMap.put(retrieverChain, retrieverChain.getDefaultSettings()); - } - dataMiningService.getStatisticsFor(retrieverChain, localeName, - new AsyncCallback>() { - @Override - public void onSuccess(HashSet statistics) { - collectStatistics(retrieverChain, statistics); - } - - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError( - "Error fetching the statistics for the retriever chain '" - + retrieverChain + "': " + caught.getMessage()); - collectStatistics(retrieverChain, Collections.emptySet()); - } - }); - } - } - } - - @Override - public void onFailure(Throwable caught) { - errorReporter - .reportError("Error fetching the retriever chain definitions: " + caught.getMessage()); - } - }); - } - - private void collectStatistics(DataRetrieverChainDefinitionDTO retrieverChain, - Iterable extractionFunctions) { - for (FunctionDTO extractionFunction : extractionFunctions) { - availableExtractionFunctions.add(new ExtractionFunctionWithContext(retrieverChain, extractionFunction)); - } - - awaitingRetrieverChainStatistics--; - if (awaitingRetrieverChainStatistics == 0) { - Collections.sort(availableExtractionFunctions); - extractionFunctionSuggestBox.setSelectableValues(availableExtractionFunctions); - - // TODO Do not pre-select the first element. The other UI components have to be able to handle "empty content" - ExtractionFunctionWithContext currentValue = extractionFunctionSuggestBox.getExtractionFunction(); - ExtractionFunctionWithContext valueToBeSelected = availableExtractionFunctions.contains(currentValue) - ? currentValue : Util.first(availableExtractionFunctions); - extractionFunctionSuggestBox.setExtractionFunction(valueToBeSelected); - } - } - - private void updateAggregators() { - FunctionDTO extractionFunction = getExtractionFunction(); - if (extractionFunction == null) { - updateListBox(aggregatorListBox, Collections.emptyList()); - } else { - dataMiningService.getAggregatorDefinitionsFor(extractionFunction, - LocaleInfo.getCurrentLocale().getLocaleName(), - new AsyncCallback>() { - @Override - public void onSuccess(HashSet aggregators) { - List aggregatorsList = new ArrayList<>(aggregators); - Collections.sort(aggregatorsList); - updateListBox(aggregatorListBox, aggregatorsList); - } - - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error fetching the aggregators for the extraction function'" - + extractionFunction + "': " + caught.getMessage()); - } - }); - } - } - - private void updateListBox(ValueListBox listBox, Collection acceptableValues) { - T currentValue = listBox.getValue(); - T valueToBeSelected = acceptableValues.contains(currentValue) ? currentValue : Util.first(acceptableValues); - - listBox.setValue(valueToBeSelected, true); - listBox.setAcceptableValues(acceptableValues); - } - - @Override - public HashMap getRetrieverSettings() { - if (settingsMap.containsKey(getDataRetrieverChainDefinition())) { - return settingsMap.get(getDataRetrieverChainDefinition()); - } else { - return new HashMap<>(); - } - } - - @Override - public boolean hasSettings() { - return getDataRetrieverChainDefinition().hasSettings(); - } - - @Override - public SettingsDialogComponent getSettingsDialogComponent(CompositeSettings settings) { - retrieverLevelSettingsComponents.clear(); - retrieverLevelSettingsComponents.addAll(createSettingsComponentsFor(getDataRetrieverChainDefinition())); - return new CompositeTabbedSettingsDialogComponent(retrieverLevelSettingsComponents); - } - - private List> createSettingsComponentsFor(final DataRetrieverChainDefinitionDTO retrieverChain) { - List> settingsComponents = new ArrayList<>(); - for (Entry retrieverLevelSettings : settingsMap.get(retrieverChain) - .entrySet()) { - final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); - final Class settingsType = retrieverLevelSettings.getValue().getClass(); - DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); - settingsComponents.add(new RetrieverLevelSettingsComponent(this, getComponentContext(), retrieverLevel, - settingsInfo.getId(), settingsInfo.getLocalizedName()) { - @Override - public SettingsDialogComponent getSettingsDialogComponent( - SerializableSettings settings) { - return settingsManager.getSettingsInfo(settingsType) - .createSettingsDialogComponent(settingsMap.get(retrieverChain).get(retrieverLevel)); - } - - @Override - public void updateSettings(SerializableSettings newSettings) { - settingsMap.get(retrieverChain).put(retrieverLevel, newSettings); - } - }); - } - return settingsComponents; - } - - @Override - public void updateSettings(CompositeSettings newSettings) { - Map chainSettings = settingsMap - .get(getDataRetrieverChainDefinition()); - for (Entry settingsPerComponent : newSettings.getSettingsPerComponentId().entrySet()) { - RetrieverLevelSettingsComponent component = (RetrieverLevelSettingsComponent) findComponentById( - settingsPerComponent.getKey()); - SerializableSettings settings = (SerializableSettings) settingsPerComponent.getValue(); - chainSettings.put(component.getRetrieverLevel(), settings); - } - notifyRetrieverChainListeners(); - } - - private Component findComponentById(String componentId) { - for (Component component : retrieverLevelSettingsComponents) { - if (component.getId().equals(componentId)) { - return component; - } - } - return null; - } - - @Override - public CompositeSettings getSettings() { - Map settings = new HashMap<>(); - for (Entry retrieverLevelSettings : settingsMap - .get(getDataRetrieverChainDefinition()).entrySet()) { - final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); - final Class settingsType = retrieverLevelSettings.getValue().getClass(); - DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); - RetrieverLevelSettingsComponent c = new RetrieverLevelSettingsComponent(this, getComponentContext(), - retrieverLevel, settingsInfo.getId(), - settingsInfo.getLocalizedName()) { - @Override - public SettingsDialogComponent getSettingsDialogComponent( - SerializableSettings settings) { - return null; - } - - @Override - public void updateSettings(SerializableSettings newSettings) { - } - }; - settings.put(c.getId(), c.hasSettings() ? c.getSettings() : null); - } - - return new CompositeSettings(settings); - } - - @Override - public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition) { - DataRetrieverChainDefinitionDTO retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); - FunctionDTO extractionFunction = queryDefinition.getStatisticToCalculate(); - ExtractionFunctionWithContext statistic = new ExtractionFunctionWithContext(retrieverChain, extractionFunction); - int index = availableExtractionFunctions.indexOf(statistic); - if (index != -1) { - statistic = availableExtractionFunctions.get(index); - } else { - availableExtractionFunctions.add(statistic); - } - - extractionFunctionSuggestBox.setExtractionFunction(statistic); - aggregatorListBox.setValue(queryDefinition.getAggregatorDefinition()); - } - - @Override - public void addStatisticChangedListener(StatisticChangedListener listener) { - retrieverChainListeners.add(listener); - extractionFunctionListeners.add(listener); - aggregatorDefinitionListeners.add(listener); - } - - @Override - public void addDataRetrieverChainDefinitionChangedListener(DataRetrieverChainDefinitionChangedListener listener) { - retrieverChainListeners.add(listener); - } - - private void notifyRetrieverChainListeners() { - DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition = getDataRetrieverChainDefinition(); - for (DataRetrieverChainDefinitionChangedListener listener : retrieverChainListeners) { - listener.dataRetrieverChainDefinitionChanged(dataRetrieverChainDefinition); - } - } - - @Override - public void addExtractionFunctionChangedListener(ExtractionFunctionChangedListener listener) { - extractionFunctionListeners.add(listener); - } - - private void notifyExtractionFunctionListeners() { - FunctionDTO extractionFunction = getExtractionFunction(); - for (ExtractionFunctionChangedListener listener : extractionFunctionListeners) { - listener.extractionFunctionChanged(extractionFunction); - } - } - - @Override - public void addAggregatorDefinitionChangedListener(AggregatorDefinitionChangedListener listener) { - aggregatorDefinitionListeners.add(listener); - } - - private void notifyAggregatorDefinitionListeners() { - AggregationProcessorDefinitionDTO aggregatorDefinition = getAggregatorDefinition(); - for (AggregatorDefinitionChangedListener listener : aggregatorDefinitionListeners) { - listener.aggregatorDefinitionChanged(aggregatorDefinition); - } - } - - @Override - public DataRetrieverChainDefinitionDTO getDataRetrieverChainDefinition() { - ExtractionFunctionWithContext extractionFunction = extractionFunctionSuggestBox.getExtractionFunction(); - return extractionFunction == null ? null : extractionFunction.getRetrieverChain(); - } - - @Override - public FunctionDTO getExtractionFunction() { - ExtractionFunctionWithContext extractionFunction = extractionFunctionSuggestBox.getExtractionFunction(); - return extractionFunction == null ? null : extractionFunction.getExtractionFunction(); - } - - @Override - public AggregationProcessorDefinitionDTO getAggregatorDefinition() { - return aggregatorListBox.getValue(); - } - - @Override - public String getLocalizedShortName() { - return getDataMiningStringMessages().statisticProvider(); - } - - @Override - public Widget getEntryWidget() { - return mainPanel; - } - - @Override - public boolean isVisible() { - return mainPanel.isVisible(); - } - - @Override - public void setVisible(boolean visibility) { - mainPanel.setVisible(visibility); - } - - @Override - public String getDependentCssClassName() { - return "globalStatisticsProvider"; - } - - @Override - public String getId() { - return "GlobalStatisticProvider"; - } - - private class ExtractionFunctionWithContext implements Comparable { - - private final DataRetrieverChainDefinitionDTO retrieverChain; - private final FunctionDTO extractionFunction; - private final Collection matchingStrings; - - public ExtractionFunctionWithContext(DataRetrieverChainDefinitionDTO retrieverChain, - FunctionDTO extractionFunction) { - this.retrieverChain = retrieverChain; - this.extractionFunction = extractionFunction; - matchingStrings = new ArrayList<>(2); - matchingStrings.add(retrieverChain.getName()); - matchingStrings.add(extractionFunction.getDisplayName()); - } - - public DataRetrieverChainDefinitionDTO getRetrieverChain() { - return retrieverChain; - } - - public FunctionDTO getExtractionFunction() { - return extractionFunction; - } - - public Iterable getMatchingStrings() { - return matchingStrings; - } - - @Override - public int compareTo(ExtractionFunctionWithContext o) { - String otherDisplayName = o.getExtractionFunction().getDisplayName(); - int comparedDisplayName = extractionFunction.getDisplayName().compareToIgnoreCase(otherDisplayName); - if (comparedDisplayName != 0) { - return comparedDisplayName; - } - return retrieverChain.compareTo(o.getRetrieverChain()); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((extractionFunction == null) ? 0 : extractionFunction.hashCode()); - result = prime * result + ((retrieverChain == null) ? 0 : retrieverChain.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - ExtractionFunctionWithContext other = (ExtractionFunctionWithContext) obj; - if (extractionFunction == null) { - if (other.extractionFunction != null) - return false; - } else if (!extractionFunction.equals(other.extractionFunction)) - return false; - if (retrieverChain == null) { - if (other.retrieverChain != null) - return false; - } else if (!retrieverChain.equals(other.retrieverChain)) - return false; - return true; - } - - } - - private abstract class ExtractionFunctionSuggestBox extends CustomSuggestBox { - - private final AbstractListSuggestOracle suggestOracle; - private final ScrollableSuggestionDisplay display; - - private ExtractionFunctionWithContext extractionFunction; - - @SuppressWarnings("unchecked") - public ExtractionFunctionSuggestBox() { - super(new AbstractListSuggestOracle() { - @Override - protected Iterable getKeywordStrings(Iterable queryTokens) { - String filterText = Util.first(queryTokens); - if (filterText == null) { - return queryTokens; - } - return Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(filterText); - } - - @Override - protected Iterable getMatchingStrings(ExtractionFunctionWithContext value) { - return value.getMatchingStrings(); - } - - @Override - protected String createSuggestionKeyString(ExtractionFunctionWithContext value) { - return value.getExtractionFunction().getDisplayName(); - } - - @Override - protected String createSuggestionAdditionalDisplayString(ExtractionFunctionWithContext value) { - return value.getRetrieverChain().getName(); - } - }, new ScrollableSuggestionDisplay()); - suggestOracle = (AbstractListSuggestOracle) getSuggestOracle(); - display = (ScrollableSuggestionDisplay) getSuggestionDisplay(); - addSuggestionSelectionHandler(this::setExtractionFunction); - } - - @Override - public void hideSuggestionList() { - display.hideSuggestions(); - } - - public void setSelectableValues(Collection selectableValues) { - suggestOracle.setSelectableValues(selectableValues); - } - - public void setExtractionFunction(ExtractionFunctionWithContext extractionFunction) { - if (!Objects.equals(this.extractionFunction, extractionFunction)) { - this.extractionFunction = extractionFunction; - setValue(extractionFunction.getExtractionFunction().getDisplayName(), false); - onValueChange(); - } - setFocus(false); - } - - public ExtractionFunctionWithContext getExtractionFunction() { - return extractionFunction; - } - - protected abstract void onValueChange(); - - } - - private static class ScrollableSuggestionDisplay extends DefaultSuggestionDisplay { - - public ScrollableSuggestionDisplay() { - PopupPanel popupPanel = getPopupPanel(); - popupPanel.addStyleName("statisticSuggestBoxPopup"); - } - - @Override - protected void moveSelectionUp() { - super.moveSelectionUp(); - scrollSelectedItemIntoView(); - } - - @Override - protected void moveSelectionDown() { - super.moveSelectionDown(); - scrollSelectedItemIntoView(); - } - - private void scrollSelectedItemIntoView() { - getSelectedMenuItem().getElement().scrollIntoView(); - } - - private native MenuItem getSelectedMenuItem() /*-{ - var menu = this.@com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay::suggestionMenu; - return menu.@com.google.gwt.user.client.ui.MenuBar::selectedItem; - }-*/; - - } - -} diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorGroup.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorGroup.java new file mode 100644 index 00000000000..ba35dc25649 --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorGroup.java @@ -0,0 +1,92 @@ +package com.sap.sse.datamining.ui.client.selection.statistic; + +import java.util.HashMap; +import java.util.Map; + +import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; + +class AggregatorGroup implements Comparable { + + private final String key; + private final Map aggregatorsBySupportedTypeName; + + private String displayName; + + public AggregatorGroup(String key) { + this.key = key; + aggregatorsBySupportedTypeName = new HashMap<>(); + } + + public String getKey() { + return key; + } + + public boolean supportsFunction(ExtractionFunctionWithContext extractionFunction) { + return extractionFunction != null && supportsFunction(extractionFunction.getExtractionFunction()); + } + + public boolean supportsFunction(FunctionDTO function) { + return function != null && supportsType(function.getReturnTypeName()); + } + + public boolean supportsType(String typeName) { + return aggregatorsBySupportedTypeName.containsKey(typeName); + } + + public AggregationProcessorDefinitionDTO getForType(String typeName) { + return aggregatorsBySupportedTypeName.get(typeName); + } + + public void setForType(String typeName, AggregationProcessorDefinitionDTO aggregator) { + if (displayName == null) { + displayName = aggregator.getDisplayName(); + } + aggregatorsBySupportedTypeName.put(typeName, aggregator); + } + + public String getDisplayName() { + return displayName; + } + + @Override + public int compareTo(AggregatorGroup other) { + if (other == null) { + return 1; + } + return displayName.compareToIgnoreCase(other.displayName); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((aggregatorsBySupportedTypeName == null) ? 0 : aggregatorsBySupportedTypeName.hashCode()); + result = prime * result + ((key == null) ? 0 : key.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + AggregatorGroup other = (AggregatorGroup) obj; + if (aggregatorsBySupportedTypeName == null) { + if (other.aggregatorsBySupportedTypeName != null) + return false; + } else if (!aggregatorsBySupportedTypeName.equals(other.aggregatorsBySupportedTypeName)) + return false; + if (key == null) { + if (other.key != null) + return false; + } else if (!key.equals(other.key)) + return false; + return true; + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorListBox.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorListBox.java new file mode 100644 index 00000000000..46c7dc6aaaa --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/AggregatorListBox.java @@ -0,0 +1,83 @@ +package com.sap.sse.datamining.ui.client.selection.statistic; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.OptionElement; +import com.google.gwt.dom.client.SelectElement; +import com.google.gwt.text.shared.AbstractRenderer; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.ValueListBox; + +public class AggregatorListBox extends ValueListBox { + + @FunctionalInterface + public static interface ValueChangeHandler { + void valueChanged(AggregatorGroup oldValue, AggregatorGroup newValue); + } + + private static final String UnsupportedItemStyle = "unsupportedAggregatorListItem"; + + private final Map aggregatorsByDisplayName; + private AggregatorGroup value; + private ValueChangeHandler valueChangeHandler; + + public AggregatorListBox(String nullDisplayString) { + super(new AbstractRenderer() { + @Override + public String render(AggregatorGroup aggregator) { + return aggregator == null ? nullDisplayString : aggregator.getDisplayName(); + } + }); + aggregatorsByDisplayName = new HashMap<>(); + + addValueChangeHandler(event -> { + AggregatorGroup newValue = event.getValue(); + if (!Objects.equals(value, newValue)) { + AggregatorGroup oldValue = value; + value = newValue; + if (valueChangeHandler != null) { + valueChangeHandler.valueChanged(oldValue, value); + } + } + }); + } + + public void setValueChangeHandler(ValueChangeHandler valueChangeHandler) { + this.valueChangeHandler = valueChangeHandler; + } + + public void updateItemStyles(ExtractionFunctionWithContext extractionFunction) { + ListBox listBox = (ListBox) getWidget(); + SelectElement selectElement = SelectElement.as(listBox.getElement()); + NodeList options = selectElement.getOptions(); + for (int i = 0; i < options.getLength(); i++) { + OptionElement option = options.getItem(i); + AggregatorGroup aggregator = aggregatorsByDisplayName.get(option.getText()); + String className = aggregator == null || extractionFunction == null + || aggregator.supportsFunction(extractionFunction) ? "" : UnsupportedItemStyle; + option.setClassName(className); + } + } + + @Override + public void setAcceptableValues(Collection newValues) { + Collection newValuesWithNull = new ArrayList<>(); + newValuesWithNull.add(null); + + aggregatorsByDisplayName.clear(); + for (AggregatorGroup aggregator : newValues) { + if (aggregator != null) { + aggregatorsByDisplayName.put(aggregator.getDisplayName(), aggregator); + newValuesWithNull.add(aggregator); + } + } + + super.setAcceptableValues(newValuesWithNull); + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionSuggestBox.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionSuggestBox.java new file mode 100644 index 00000000000..4be312b285c --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionSuggestBox.java @@ -0,0 +1,415 @@ +package com.sap.sse.datamining.ui.client.selection.statistic; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Predicate; + +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Element; +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.user.client.ui.HasAnimation; +import com.google.gwt.user.client.ui.MenuBar; +import com.google.gwt.user.client.ui.MenuItem; +import com.google.gwt.user.client.ui.MenuItemSeparator; +import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.PopupPanel.AnimationType; +import com.google.gwt.user.client.ui.SuggestBox; +import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; +import com.sap.sse.common.Util; +import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; +import com.sap.sse.datamining.ui.client.StringMessages; +import com.sap.sse.gwt.client.suggestion.AbstractListSuggestOracle; +import com.sap.sse.gwt.client.suggestion.CustomSuggestBox; + +public class ExtractionFunctionSuggestBox extends CustomSuggestBox { + + @FunctionalInterface + public interface ValueChangeHandler { + void valueChanged(ExtractionFunctionWithContext oldValue, ExtractionFunctionWithContext newValue); + } + + private final AbstractListSuggestOracle suggestOracle; + private final ExtractionFunctionSuggestionDisplay display; + private ValueChangeHandler valueChangeHandler; + + private ExtractionFunctionWithContext extractionFunction; + + @SuppressWarnings("unchecked") + public ExtractionFunctionSuggestBox(Predicate extractionFunctionSupportedPredicate) { + super(new ExtractionFunctionSuggestOracle(extractionFunctionSupportedPredicate), new ExtractionFunctionSuggestionDisplay()); + suggestOracle = (AbstractListSuggestOracle) getSuggestOracle(); + display = (ExtractionFunctionSuggestionDisplay) getSuggestionDisplay(); + addSuggestionSelectionHandler(this::setExtractionFunction); + getValueBox().addBlurHandler(this::onBlur); + } + + public void setValueChangeHandler(ValueChangeHandler valueChangeHandler) { + this.valueChangeHandler = valueChangeHandler; + } + + public Collection getSelectableValues() { + return suggestOracle.getSelectableValues(); + } + + public void setSelectableValues(Collection selectableValues) { + suggestOracle.setSelectableValues(selectableValues); + } + + public void setExtractionFunction(ExtractionFunctionWithContext extractionFunction) { + if (!Objects.equals(this.extractionFunction, extractionFunction)) { + ExtractionFunctionWithContext oldValue = this.extractionFunction; + this.extractionFunction = extractionFunction; + setValue(extractionFunction == null ? null : extractionFunction.getDisplayString(), false); + if (valueChangeHandler != null) { + valueChangeHandler.valueChanged(oldValue, extractionFunction); + } + } + setFocus(false); + } + + public ExtractionFunctionWithContext getExtractionFunction() { + return extractionFunction; + } + + private void onBlur(BlurEvent event) { + setValue(extractionFunction == null ? null : extractionFunction.getDisplayString(), false); + } + + @Override + public void hideSuggestionList() { + display.hideSuggestions(); + } + + public boolean isGroupingSuggestionsByRetrieverChain() { + return display.isGroupingSuggestionsByRetrieverChain(); + } + + public void setGroupingSuggestionsByRetrieverChain(boolean groupingSuggestionsByRetrieverChain) { + display.setGroupingSuggestionsByRetrieverChain(groupingSuggestionsByRetrieverChain); + } + + private static class ExtractionFunctionSuggestOracle extends AbstractListSuggestOracle { + + private final Predicate extractionFunctionSupportedPredicate; + + public ExtractionFunctionSuggestOracle(Predicate extractionFunctionSupportedPredicate) { + this.extractionFunctionSupportedPredicate = extractionFunctionSupportedPredicate; + } + + @Override + protected Iterable getKeywordStrings(Iterable queryTokens) { + String filterText = Util.first(queryTokens); + if (filterText == null) { + return queryTokens; + } + return Util.splitAlongWhitespaceRespectingDoubleQuotedPhrases(filterText); + } + + @Override + protected Iterable getMatchingStrings(ExtractionFunctionWithContext value) { + return value.getMatchingStrings(); + } + + @Override + protected String createSuggestionKeyString(ExtractionFunctionWithContext value) { + return value.getDisplayString(); + } + + @Override + protected String createSuggestionAdditionalDisplayString(ExtractionFunctionWithContext value) { + return null; + } + + @Override + protected SimpleSuggestion createSuggestion(ExtractionFunctionWithContext match, Iterable queryTokens) { + return new ExtractionFunctionSuggestion(match, queryTokens, extractionFunctionSupportedPredicate.test(match)); + } + + private class ExtractionFunctionSuggestion extends SimpleSuggestion implements Comparable { + + private final boolean supported; + + public ExtractionFunctionSuggestion(ExtractionFunctionWithContext suggestObject, + Iterable queryTokens, boolean supported) { + super(suggestObject, queryTokens); + this.supported = supported; + } + + public boolean isSupported() { + return supported; + } + + @Override + public int compareTo(ExtractionFunctionSuggestion other) { + if (other == null) { + return -1; + } + String displayName = getSuggestObject().getExtractionFunction().getDisplayName(); + String otherDisplayName = other.getSuggestObject().getExtractionFunction().getDisplayName(); + return displayName.compareToIgnoreCase(otherDisplayName); + } + + } + + } + + private static class ExtractionFunctionSuggestionDisplay extends SuggestionDisplay implements HasAnimation { + + private final StringMessages stringMessages = StringMessages.INSTANCE; + + private final ExtractionFunctionSuggestionMenu suggestionMenu; + private final PopupPanel suggestionPopup; + + private boolean groupingSuggestionsByRetrieverChain; + private Element autoHidePartner; + + public ExtractionFunctionSuggestionDisplay() { + suggestionMenu = new ExtractionFunctionSuggestionMenu(/*vertical*/ true); + suggestionPopup = new PopupPanel(/*autoHide*/ true, /*modal*/ false); + suggestionPopup.setStyleName("statisticSuggestBoxPopup"); + suggestionPopup.addStyleName("dataMiningBorderLeft"); + suggestionPopup.addStyleName("dataMiningBorderBottom"); + suggestionPopup.addStyleName("dataMiningBorderRight"); + suggestionPopup.setPreviewingAllNativeEvents(true); + suggestionPopup.setAnimationType(AnimationType.ROLL_DOWN); + suggestionPopup.setWidget(suggestionMenu); + } + + @Override + protected Suggestion getCurrentSelection() { + return suggestionMenu.getSelectedSuggestion(); + } + + @Override + public void hideSuggestions() { + suggestionPopup.hide(); + } + + @Override + public boolean isSuggestionListShowing() { + return suggestionPopup.isVisible(); + } + + @Override + protected void moveSelectionDown() { + if (isSuggestionListShowing()) { + suggestionMenu.selectItem(suggestionMenu.getSelectedIndex() + 1); + } + } + + @Override + protected void moveSelectionUp() { + if (isSuggestionListShowing()) { + int selectedIndex = suggestionMenu.getSelectedIndex(); + if (selectedIndex < 0) { + suggestionMenu.selectItem(suggestionMenu.size() - 1); + } else { + suggestionMenu.selectItem(selectedIndex - 1); + } + } + } + + public boolean isGroupingSuggestionsByRetrieverChain() { + return groupingSuggestionsByRetrieverChain; + } + + public void setGroupingSuggestionsByRetrieverChain(boolean groupingSuggestionsByRetrieverChain) { + this.groupingSuggestionsByRetrieverChain = groupingSuggestionsByRetrieverChain; + } + + @Override + protected void showSuggestions(SuggestBox suggestBox, Collection suggestions, + boolean isDisplayStringHTML, boolean isAutoSelectEnabled, SuggestionCallback callback) { + if (suggestionPopup.isAttached()) { + suggestionPopup.hide(); + } + + Collection supportedSuggestions = new ArrayList<>(); + Collection unsupportedSuggestions = new ArrayList<>(); + for (Suggestion suggestion : suggestions) { + ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion extractionFunctionSuggestion = + (ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion) suggestion; + if (extractionFunctionSuggestion.isSupported()) { + supportedSuggestions.add(extractionFunctionSuggestion); + } else { + unsupportedSuggestions.add(extractionFunctionSuggestion); + } + } + + suggestionMenu.clearItems(); + if (groupingSuggestionsByRetrieverChain) { + showSuggestionsGroupedByRetrieverChain(supportedSuggestions, isDisplayStringHTML, callback); + } else { + addSuggestionMenuItems(supportedSuggestions, isDisplayStringHTML, callback); + } + if (!unsupportedSuggestions.isEmpty()) { + suggestionMenu.addSeparator(new UnsupportedExtractionFunctionsSeparator(stringMessages.followingStatisticsAreNotSupportedByAggregatorWarning())); + if (groupingSuggestionsByRetrieverChain) { + showSuggestionsGroupedByRetrieverChain(unsupportedSuggestions, isDisplayStringHTML, callback); + } else { + addSuggestionMenuItems(unsupportedSuggestions, isDisplayStringHTML, callback); + } + } + + if (isAutoSelectEnabled && suggestionMenu.size() > 0) { + suggestionMenu.selectItem(0); + } + + updateAutoHidePartner(suggestBox.getElement()); + suggestionPopup.showRelativeTo(suggestBox); + } + + private void showSuggestionsGroupedByRetrieverChain(Collection suggestions, + boolean isDisplayStringHTML, SuggestionCallback callback) { + Map> groupedSuggestions = + groupSuggestionsByRetrieverChain(suggestions); + List orderedRetrieverChains = new ArrayList<>(groupedSuggestions.keySet()); + Collections.sort(orderedRetrieverChains); + for (DataRetrieverChainDefinitionDTO retrieverChain : orderedRetrieverChains) { + List suggestionsOfRetrieverChain = groupedSuggestions.get(retrieverChain); + suggestionsOfRetrieverChain.sort(null); + + ExtractionFunctionSeparator separator = new ExtractionFunctionSeparator(retrieverChain); + suggestionMenu.addSeparator(separator); + if (!suggestionsOfRetrieverChain.get(0).isSupported()) { + separator.addStyleName("separator-unsupported"); + } + addSuggestionMenuItems(suggestionsOfRetrieverChain, isDisplayStringHTML, callback); + } + } + + private Map> groupSuggestionsByRetrieverChain( + Collection suggestions) { + Map> groupedSuggestions = new HashMap<>(); + for (Suggestion suggestion : suggestions) { + ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion extractionFunctionSuggestion = + (ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion) suggestion; + ExtractionFunctionWithContext extractionFunction = extractionFunctionSuggestion.getSuggestObject(); + DataRetrieverChainDefinitionDTO retrieverChain = extractionFunction.getRetrieverChain(); + List suggestionsOfRetrieverChain = groupedSuggestions + .get(retrieverChain); + if (suggestionsOfRetrieverChain == null) { + suggestionsOfRetrieverChain = new ArrayList<>(); + groupedSuggestions.put(retrieverChain, suggestionsOfRetrieverChain); + } + suggestionsOfRetrieverChain.add(extractionFunctionSuggestion); + } + return groupedSuggestions; + } + + private void addSuggestionMenuItems( + Collection suggestions, + boolean isDisplayStringHTML, SuggestionCallback callback) { + int count = 0; + for (ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion suggestion : suggestions) { + boolean isEven = count % 2 == 0; + SuggestionMenuItem menuItem = createSuggestionMenuItem(suggestion, isDisplayStringHTML, isEven, callback); + suggestionMenu.addItem(menuItem); + count++; + } + } + + private SuggestionMenuItem createSuggestionMenuItem(ExtractionFunctionSuggestOracle.ExtractionFunctionSuggestion suggestion, + boolean isDisplayStringHTML, boolean isEven, SuggestionCallback callback) { + SuggestionMenuItem menuItem = new SuggestionMenuItem(suggestion, isDisplayStringHTML, + () -> callback.onSuggestionSelected(suggestion)); + menuItem.addStyleName("item-" + (isEven ? "even" : "odd")); + if (!suggestion.isSupported()) { + menuItem.addStyleName("item-unsupported"); + } + return menuItem; + } + + private void updateAutoHidePartner(Element newAutoHidePartner) { + if (autoHidePartner != newAutoHidePartner) { + if (autoHidePartner != null) { + suggestionPopup.removeAutoHidePartner(autoHidePartner); + } + suggestionPopup.addAutoHidePartner(newAutoHidePartner); + autoHidePartner = newAutoHidePartner; + } + } + + @Override + public boolean isAnimationEnabled() { + return suggestionPopup.isAnimationEnabled(); + } + + @Override + public void setAnimationEnabled(boolean enable) { + suggestionPopup.setAnimationEnabled(enable); + } + + } + + private static class ExtractionFunctionSuggestionMenu extends MenuBar { + + public ExtractionFunctionSuggestionMenu(boolean vertical) { + super(vertical); + setStyleName("statisticSuggestBoxPopupContent"); + setFocusOnHoverEnabled(false); + } + + public int size() { + return getItems().size(); + } + + public int getSelectedIndex() { + MenuItem selectedItem = getSelectedItem(); + return selectedItem == null ? -1 : getItems().indexOf(selectedItem); + } + + public void selectItem(int index) { + if (index >= 0 && index < size()) { + MenuItem itemToSelect = getItems().get(index); + selectItem(itemToSelect); + itemToSelect.getElement().scrollIntoView(); + } + } + + public Suggestion getSelectedSuggestion() { + return ((SuggestionMenuItem) getSelectedItem()).getSuggestion(); + } + + } + + private static class ExtractionFunctionSeparator extends MenuItemSeparator { + + public ExtractionFunctionSeparator(DataRetrieverChainDefinitionDTO retrieverChain) { + setStyleName("separator"); + getElement().getFirstChildElement().setInnerText(retrieverChain.getName()); + } + + } + + private static class UnsupportedExtractionFunctionsSeparator extends MenuItemSeparator { + + public UnsupportedExtractionFunctionsSeparator(String text) { + setStyleName("separator-message"); + getElement().getFirstChildElement().setInnerText(text); + } + + } + + private static class SuggestionMenuItem extends MenuItem { + + private final Suggestion suggestion; + + public SuggestionMenuItem(Suggestion suggestion, boolean asHTML, ScheduledCommand command) { + super(suggestion.getDisplayString(), asHTML, command); + this.suggestion = suggestion; + setStyleName("item"); + } + + public Suggestion getSuggestion() { + return suggestion; + } + + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionWithContext.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionWithContext.java new file mode 100644 index 00000000000..7b53a7c14c4 --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/ExtractionFunctionWithContext.java @@ -0,0 +1,111 @@ +package com.sap.sse.datamining.ui.client.selection.statistic; + +import java.util.ArrayList; +import java.util.Collection; + +import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; + +abstract class ExtractionFunctionWithContext implements Comparable { + + private final DataRetrieverChainDefinitionDTO retrieverChain; + private final FunctionDTO extractionFunction; + private final Collection matchingStrings; + + protected ExtractionFunctionWithContext(DataRetrieverChainDefinitionDTO retrieverChain, + FunctionDTO extractionFunction) { + this.retrieverChain = retrieverChain; + this.extractionFunction = extractionFunction; + matchingStrings = new ArrayList<>(4); + } + + public DataRetrieverChainDefinitionDTO getRetrieverChain() { + return retrieverChain; + } + + public FunctionDTO getExtractionFunction() { + return extractionFunction; + } + + public abstract String getDisplayString(); + + public Iterable getMatchingStrings() { + return matchingStrings; + } + + protected void addMatchingString(String matchingString) { + matchingStrings.add(matchingString); + } + + @Override + public int compareTo(ExtractionFunctionWithContext o) { + String otherDisplayName = o.getExtractionFunction().getDisplayName(); + int comparedDisplayName = extractionFunction.getDisplayName().compareToIgnoreCase(otherDisplayName); + if (comparedDisplayName != 0) { + return comparedDisplayName; + } + return retrieverChain.compareTo(o.getRetrieverChain()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((extractionFunction == null) ? 0 : extractionFunction.hashCode()); + result = prime * result + ((retrieverChain == null) ? 0 : retrieverChain.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ExtractionFunctionWithContext other = (ExtractionFunctionWithContext) obj; + if (extractionFunction == null) { + if (other.extractionFunction != null) + return false; + } else if (!extractionFunction.equals(other.extractionFunction)) + return false; + if (retrieverChain == null) { + if (other.retrieverChain != null) + return false; + } else if (!retrieverChain.equals(other.retrieverChain)) + return false; + return true; + } + + static class StatisticWithContext extends ExtractionFunctionWithContext { + + public StatisticWithContext(DataRetrieverChainDefinitionDTO retrieverChain, FunctionDTO extractionFunction) { + super(retrieverChain, extractionFunction); + addMatchingString(retrieverChain.getName()); + addMatchingString(extractionFunction.getDisplayName()); + } + + @Override + public String getDisplayString() { + return getExtractionFunction().getDisplayName(); + } + + } + + static class IdentityFunctionWithContext extends ExtractionFunctionWithContext { + + public IdentityFunctionWithContext(DataRetrieverChainDefinitionDTO retrieverChain, + FunctionDTO identityFunction) { + super(retrieverChain, identityFunction); + addMatchingString(retrieverChain.getName()); + } + + @Override + public String getDisplayString() { + return getRetrieverChain().getName(); + } + + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/SuggestBoxStatisticProvider.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/SuggestBoxStatisticProvider.java new file mode 100644 index 00000000000..d23a615cf45 --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/selection/statistic/SuggestBoxStatisticProvider.java @@ -0,0 +1,626 @@ +package com.sap.sse.datamining.ui.client.selection.statistic; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.i18n.client.LocaleInfo; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sse.common.Util; +import com.sap.sse.common.settings.SerializableSettings; +import com.sap.sse.common.settings.Settings; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; +import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; +import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; +import com.sap.sse.datamining.ui.client.AbstractDataMiningComponent; +import com.sap.sse.datamining.ui.client.AggregatorDefinitionChangedListener; +import com.sap.sse.datamining.ui.client.DataMiningServiceAsync; +import com.sap.sse.datamining.ui.client.DataMiningSettingsControl; +import com.sap.sse.datamining.ui.client.DataMiningSettingsInfo; +import com.sap.sse.datamining.ui.client.DataMiningSettingsInfoManager; +import com.sap.sse.datamining.ui.client.DataRetrieverChainDefinitionChangedListener; +import com.sap.sse.datamining.ui.client.ExtractionFunctionChangedListener; +import com.sap.sse.datamining.ui.client.StatisticChangedListener; +import com.sap.sse.datamining.ui.client.StatisticProvider; +import com.sap.sse.datamining.ui.client.selection.RetrieverLevelSettingsComponent; +import com.sap.sse.datamining.ui.client.selection.statistic.ExtractionFunctionWithContext.IdentityFunctionWithContext; +import com.sap.sse.datamining.ui.client.selection.statistic.ExtractionFunctionWithContext.StatisticWithContext; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.shared.components.Component; +import com.sap.sse.gwt.client.shared.components.CompositeSettings; +import com.sap.sse.gwt.client.shared.components.CompositeTabbedSettingsDialogComponent; +import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; +import com.sap.sse.gwt.client.shared.settings.ComponentContext; + +/** + * A {@link StatisticProvider} that contains all statistics registered in the server. Each statistic is paired with the + * corresponding {@link DataRetrieverChainDefinitionDTO}. This provides a more comfortable access to the available + * statistics, without the need to specify the domain to analyze. + * + * @author Lennart Hensler + */ +public class SuggestBoxStatisticProvider extends AbstractDataMiningComponent + implements StatisticProvider { + + private static final String StatisticProviderStyle = "statisticProvider"; + private static final String StatisticProviderLabelStyle = "statisticProviderLabel"; + private static final String StatisticProviderElementStyle = "statisticProviderElement"; + private static final String SuggestBoxContainerStyle = "statisticSuggestBoxContainer"; + + private final DataMiningServiceAsync dataMiningService; + private final ErrorReporter errorReporter; + private final Set retrieverChainListeners; + private final Set extractionFunctionListeners; + private final Set aggregatorDefinitionListeners; + private boolean isAwaitingReload; + private int awaitingRetrieverChainStatistics; + private int awaitingAggregators; + + private final DataMiningSettingsInfoManager settingsManager; + private final DataMiningSettingsControl settingsControl; + private final Map> settingsMap; + private final List> retrieverLevelSettingsComponents; + + private final FlowPanel mainPanel; + private final Label labelBetweenAggregatorAndStatistic; + + private final Map> collectedAggregators; + private final List availableAggregators; + private final AggregatorListBox aggregatorListBox; + + private FunctionDTO identityFunction; + private final List availableIdentityFunctions; + private final List availableStatistics; + private final ExtractionFunctionSuggestBox extractionFunctionSuggestBox; + + public SuggestBoxStatisticProvider(Component parent, ComponentContext componentContext, + DataMiningServiceAsync dataMiningService, ErrorReporter errorReporter, + DataMiningSettingsControl settingsControl, DataMiningSettingsInfoManager settingsManager) { + super(parent, componentContext); + this.dataMiningService = dataMiningService; + this.errorReporter = errorReporter; + retrieverChainListeners = new HashSet<>(); + extractionFunctionListeners = new HashSet<>(); + aggregatorDefinitionListeners = new HashSet<>(); + isAwaitingReload = false; + this.settingsManager = settingsManager; + this.settingsControl = settingsControl; + this.settingsControl.addSettingsComponent(this); + settingsMap = new HashMap<>(); + retrieverLevelSettingsComponents = new ArrayList<>(); + + collectedAggregators = new HashMap<>(); + availableAggregators = new ArrayList<>(); + aggregatorListBox = new AggregatorListBox("<" + getDataMiningStringMessages().any() + ">"); + aggregatorListBox.setValueChangeHandler(this::aggregatorSelectionChanged); + aggregatorListBox.addStyleName(StatisticProviderElementStyle); + aggregatorListBox.addStyleName("dataMiningListBox"); + aggregatorListBox.setEnabled(false); + + availableIdentityFunctions = new ArrayList<>(); + availableStatistics = new ArrayList<>(); + extractionFunctionSuggestBox = new ExtractionFunctionSuggestBox(extractionFunction -> { + AggregatorGroup aggregator = aggregatorListBox.getValue(); + return aggregator == null || aggregator.supportsFunction(extractionFunction); + }); + extractionFunctionSuggestBox.setValueChangeHandler(this::extractionFunctionSelectionChanged); + extractionFunctionSuggestBox.getValueBox().addFocusHandler(e -> { + extractionFunctionSuggestBox.getValueBox().selectAll(); + extractionFunctionSuggestBox.showSuggestionList(); + }); + extractionFunctionSuggestBox.getValueBox().addKeyUpHandler(e -> { + int keyCode = e.getNativeEvent().getKeyCode(); + if (keyCode == KeyCodes.KEY_ESCAPE) { + extractionFunctionSuggestBox.hideSuggestionList(); + extractionFunctionSuggestBox.setFocus(false); + } + }); + extractionFunctionSuggestBox.getElement().setPropertyString("placeholder", getDataMiningStringMessages().searchAvailableStatistics()); + extractionFunctionSuggestBox.setLimit(Integer.MAX_VALUE); + extractionFunctionSuggestBox.addStyleName(StatisticProviderElementStyle); + extractionFunctionSuggestBox.setWidth("100%"); + + SimplePanel suggestBoxContainer = new SimplePanel(extractionFunctionSuggestBox); + suggestBoxContainer.addStyleName(StatisticProviderElementStyle); + suggestBoxContainer.addStyleName(SuggestBoxContainerStyle); + + mainPanel = new FlowPanel(); + mainPanel.addStyleName(StatisticProviderStyle); + mainPanel.add(aggregatorListBox); + labelBetweenAggregatorAndStatistic = new Label(getDataMiningStringMessages().of()); + labelBetweenAggregatorAndStatistic.addStyleName(StatisticProviderLabelStyle); + labelBetweenAggregatorAndStatistic.addStyleName("emphasizedLabel"); + mainPanel.add(labelBetweenAggregatorAndStatistic); + mainPanel.add(suggestBoxContainer); + } + + @Override + public void awaitReloadComponents() { + isAwaitingReload = true; + } + + @Override + public boolean isAwaitingReload() { + return isAwaitingReload; + } + + @Override + public void reloadComponents() { + isAwaitingReload = false; + updateContent(); + } + + private void updateContent() { + final String localeName = LocaleInfo.getCurrentLocale().getLocaleName(); + dataMiningService.getDataRetrieverChainDefinitions(localeName, + new AsyncCallback>() { + @Override + public void onSuccess(ArrayList dataRetrieverChainDefinitions) { + settingsMap.clear(); + for (DataRetrieverChainDefinitionDTO retrieverChain : dataRetrieverChainDefinitions) { + if (retrieverChain.hasSettings()) { + settingsMap.put(retrieverChain, retrieverChain.getDefaultSettings()); + } + } + + updateIdentityFunctions(localeName, dataRetrieverChainDefinitions); + updateStatistics(localeName, dataRetrieverChainDefinitions); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error fetching the retriever chain definitions: " + caught.getMessage()); + } + }); + } + + private void updateIdentityFunctions(String localeName, Collection retrieverChains) { + availableIdentityFunctions.clear(); + if (retrieverChains.isEmpty()) { + return; + } + + dataMiningService.getIdentityFunction(localeName, new AsyncCallback() { + @Override + public void onSuccess(FunctionDTO identityFunction) { + SuggestBoxStatisticProvider.this.identityFunction = identityFunction; + for (DataRetrieverChainDefinitionDTO retrieverChain : retrieverChains) { + availableIdentityFunctions.add(new IdentityFunctionWithContext(retrieverChain, identityFunction)); + } + Collections.sort(availableIdentityFunctions); + } + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error fetching the identity function: " + caught.getMessage()); + } + }); + } + + private void updateStatistics(String localeName, Collection retrieverChains) { + availableStatistics.clear(); + if (retrieverChains.isEmpty()) { + extractionFunctionSuggestBox.setExtractionFunction(null); + extractionFunctionSuggestBox.setSelectableValues(Collections.emptySet()); + return; + } + + awaitingRetrieverChainStatistics = retrieverChains.size(); + awaitingAggregators = 0; + for (DataRetrieverChainDefinitionDTO retrieverChain : retrieverChains) { + dataMiningService.getStatisticsFor(retrieverChain, localeName, + new AsyncCallback>() { + @Override + public void onSuccess(HashSet statistics) { + collectStatistics(localeName, retrieverChain, statistics); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error fetching the statistics for the retriever chain '" + + retrieverChain + "': " + caught.getMessage()); + collectStatistics(localeName, retrieverChain, Collections.emptySet()); + } + }); + } + } + + private void collectStatistics(String localeName, DataRetrieverChainDefinitionDTO retrieverChain, + Iterable extractionFunctions) { + for (FunctionDTO extractionFunction : extractionFunctions) { + availableStatistics.add(new StatisticWithContext(retrieverChain, extractionFunction)); + if (!collectedAggregators.containsKey(extractionFunction.getReturnTypeName())) { + awaitingAggregators++; + dataMiningService.getAggregatorDefinitionsFor(extractionFunction, localeName, new AsyncCallback>() { + @Override + public void onSuccess(HashSet aggregators) { + collectAggregators(extractionFunction, aggregators); + } + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error fetching the aggregators for " + extractionFunction, caught.getMessage()); + collectAggregators(null, null); + } + }); + } + } + + awaitingRetrieverChainStatistics--; + if (awaitingRetrieverChainStatistics <= 0) { + extractionFunctionSuggestBox.setExtractionFunction(null); + extractionFunctionSuggestBox.setSelectableValues(availableStatistics); + extractionFunctionSuggestBox.setGroupingSuggestionsByRetrieverChain(true); + if (awaitingAggregators <= 0) { + collectAggregators(null, null); + } + } + } + + private void collectAggregators(FunctionDTO extractionFunction, Iterable aggregators) { + if (extractionFunction != null) { + String returnTypeName = extractionFunction.getReturnTypeName(); + Set aggregatorsForType = collectedAggregators.get(returnTypeName); + if (aggregatorsForType == null) { + aggregatorsForType = new HashSet<>(); + collectedAggregators.put(returnTypeName, aggregatorsForType); + } + Util.addAll(aggregators, aggregatorsForType); + } + + awaitingAggregators--; + if (awaitingAggregators <= 0) { + Map aggregatorGroups = new HashMap<>(); + for (String returnTypeName : collectedAggregators.keySet()) { + for (AggregationProcessorDefinitionDTO aggregator : collectedAggregators.get(returnTypeName)) { + String aggregatorKey = aggregator.getMessageKey(); + AggregatorGroup aggregatorGroup = aggregatorGroups.get(aggregatorKey); + if (aggregatorGroup == null) { + aggregatorGroup = new AggregatorGroup(aggregatorKey); + aggregatorGroups.put(aggregatorKey, aggregatorGroup); + } + + String extractedTypeName = aggregator.getExtractedTypeName(); + if (!aggregatorGroup.supportsType(extractedTypeName)) { + aggregatorGroup.setForType(extractedTypeName, aggregator); + } + if (!aggregatorGroup.supportsType(returnTypeName)) { + aggregatorGroup.setForType(returnTypeName, aggregator); + } + } + } + + availableAggregators.clear(); + availableAggregators.addAll(aggregatorGroups.values()); + Collections.sort(availableAggregators); + aggregatorListBox.setValue(null, true); + aggregatorListBox.setAcceptableValues(availableAggregators); + aggregatorListBox.setEnabled(!availableAggregators.isEmpty()); + } + } + + private void aggregatorSelectionChanged(AggregatorGroup oldAggregator, AggregatorGroup newAggregator) { + boolean currentAggregatorSupportsIdentityFunction = oldAggregator != null + && oldAggregator.supportsFunction(identityFunction); + boolean newAggregatorSupportsIdentityFunction = newAggregator != null + && newAggregator.supportsFunction(identityFunction); + + List selectableExtractionFunctions; + String labelBetweenAggregatorAndStatisticText = null; + if (newAggregatorSupportsIdentityFunction) { + selectableExtractionFunctions = availableIdentityFunctions; + labelBetweenAggregatorAndStatisticText = getDataMiningStringMessages().the(); + } else { + selectableExtractionFunctions = availableStatistics; + labelBetweenAggregatorAndStatisticText = getDataMiningStringMessages().of(); + } + + ExtractionFunctionWithContext currentExtractionFunction = extractionFunctionSuggestBox.getExtractionFunction(); + if (currentExtractionFunction != null && newAggregator != null && !newAggregator.supportsFunction(currentExtractionFunction)) { + // Current extraction function isn't supported by the new aggregator + if (newAggregatorSupportsIdentityFunction && !currentAggregatorSupportsIdentityFunction) { + // Switch to the corresponding identity function, if the identity functions aren't already the selectable functions + for (IdentityFunctionWithContext identityFunction : availableIdentityFunctions) { + if (identityFunction.getRetrieverChain().equals(currentExtractionFunction.getRetrieverChain())) { + extractionFunctionSuggestBox.setExtractionFunction(identityFunction); + break; + } + } + } else { + extractionFunctionSuggestBox.setExtractionFunction(null); + } + } + extractionFunctionSuggestBox.setSelectableValues(selectableExtractionFunctions); + extractionFunctionSuggestBox.setGroupingSuggestionsByRetrieverChain(!newAggregatorSupportsIdentityFunction); + labelBetweenAggregatorAndStatistic.setText(labelBetweenAggregatorAndStatisticText); + + notifyAggregatorDefinitionListeners(); + } + + private void extractionFunctionSelectionChanged(ExtractionFunctionWithContext oldExtractionFunction, + ExtractionFunctionWithContext newExtractionFunction) { + aggregatorListBox.updateItemStyles(newExtractionFunction); + AggregatorGroup aggregator = aggregatorListBox.getValue(); + if (aggregator != null && newExtractionFunction != null && !aggregator.supportsFunction(newExtractionFunction)) { + aggregatorListBox.setValue(null, true); + } + + DataRetrieverChainDefinitionDTO oldRetrieverChain = oldExtractionFunction == null ? null + : oldExtractionFunction.getRetrieverChain(); + DataRetrieverChainDefinitionDTO newRetrieverChain = newExtractionFunction == null ? null + : newExtractionFunction.getRetrieverChain(); + if (!Objects.equals(oldRetrieverChain, newRetrieverChain)) { + notifyRetrieverChainListeners(); + } + notifyExtractionFunctionListeners(); + } + + @Override + public HashMap getRetrieverSettings() { + DataRetrieverChainDefinitionDTO retrieverChain = getDataRetrieverChainDefinition(); + if (retrieverChain != null && settingsMap.containsKey(retrieverChain)) { + return settingsMap.get(retrieverChain); + } else { + return new HashMap<>(); + } + } + + @Override + public boolean hasSettings() { + DataRetrieverChainDefinitionDTO retrieverChain = getDataRetrieverChainDefinition(); + return retrieverChain != null && retrieverChain.hasSettings(); + } + + @Override + public SettingsDialogComponent getSettingsDialogComponent(CompositeSettings settings) { + retrieverLevelSettingsComponents.clear(); + retrieverLevelSettingsComponents.addAll(createSettingsComponentsFor(getDataRetrieverChainDefinition())); + return new CompositeTabbedSettingsDialogComponent(retrieverLevelSettingsComponents); + } + + private List> createSettingsComponentsFor(final DataRetrieverChainDefinitionDTO retrieverChain) { + List> settingsComponents = new ArrayList<>(); + for (Entry retrieverLevelSettings : settingsMap.get(retrieverChain) + .entrySet()) { + final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); + final Class settingsType = retrieverLevelSettings.getValue().getClass(); + DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); + settingsComponents.add(new RetrieverLevelSettingsComponent(this, getComponentContext(), retrieverLevel, + settingsInfo.getId(), settingsInfo.getLocalizedName()) { + @Override + public SettingsDialogComponent getSettingsDialogComponent( + SerializableSettings settings) { + return settingsManager.getSettingsInfo(settingsType) + .createSettingsDialogComponent(settingsMap.get(retrieverChain).get(retrieverLevel)); + } + + @Override + public void updateSettings(SerializableSettings newSettings) { + settingsMap.get(retrieverChain).put(retrieverLevel, newSettings); + } + }); + } + return settingsComponents; + } + + @Override + public void updateSettings(CompositeSettings newSettings) { + Map chainSettings = settingsMap + .get(getDataRetrieverChainDefinition()); + for (Entry settingsPerComponent : newSettings.getSettingsPerComponentId().entrySet()) { + RetrieverLevelSettingsComponent component = (RetrieverLevelSettingsComponent) findComponentById( + settingsPerComponent.getKey()); + SerializableSettings settings = (SerializableSettings) settingsPerComponent.getValue(); + chainSettings.put(component.getRetrieverLevel(), settings); + } + notifyRetrieverChainListeners(); + } + + private Component findComponentById(String componentId) { + for (Component component : retrieverLevelSettingsComponents) { + if (component.getId().equals(componentId)) { + return component; + } + } + return null; + } + + @Override + public CompositeSettings getSettings() { + Map settings = new HashMap<>(); + DataRetrieverChainDefinitionDTO retrieverChain = getDataRetrieverChainDefinition(); + if (retrieverChain != null) { + for (Entry retrieverLevelSettings : settingsMap + .get(retrieverChain).entrySet()) { + final DataRetrieverLevelDTO retrieverLevel = retrieverLevelSettings.getKey(); + final Class settingsType = retrieverLevelSettings.getValue().getClass(); + DataMiningSettingsInfo settingsInfo = settingsManager.getSettingsInfo(settingsType); + RetrieverLevelSettingsComponent c = new RetrieverLevelSettingsComponent(this, getComponentContext(), + retrieverLevel, settingsInfo.getId(), settingsInfo.getLocalizedName()) { + @Override + public SettingsDialogComponent getSettingsDialogComponent( + SerializableSettings settings) { + return null; + } + + @Override + public void updateSettings(SerializableSettings newSettings) { + } + }; + settings.put(c.getId(), c.hasSettings() ? c.getSettings() : null); + } + } + return new CompositeSettings(settings); + } + + @Override + public void applyQueryDefinition(StatisticQueryDefinitionDTO queryDefinition, Consumer> callback) { + Collection errorMessages = new ArrayList<>(); + + AggregationProcessorDefinitionDTO aggregator = queryDefinition.getAggregatorDefinition(); + AggregatorGroup aggregatorToSelect = null; + for (AggregatorGroup availableAggregator : availableAggregators) { + if (availableAggregator.getKey().equals(aggregator.getMessageKey()) && + availableAggregator.supportsType(aggregator.getExtractedTypeName())) { + aggregatorToSelect = availableAggregator; + break; + } + } + aggregatorListBox.setValue(aggregatorToSelect, true); + if (aggregatorToSelect == null) { + errorMessages.add(getDataMiningStringMessages().aggregatorNotAvailable(aggregator.getDisplayName())); + } + + DataRetrieverChainDefinitionDTO retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); + FunctionDTO extractionFunction = queryDefinition.getStatisticToCalculate(); + boolean isIdentityFunction = identityFunction != null && identityFunction.equals(extractionFunction); + ExtractionFunctionWithContext extractionFunctionToSelect = null; + if (isIdentityFunction) { + IdentityFunctionWithContext identityFunctionWrapper = new IdentityFunctionWithContext(retrieverChain, extractionFunction); + int index = availableIdentityFunctions.indexOf(identityFunctionWrapper); + if (index != -1) { + extractionFunctionToSelect = availableIdentityFunctions.get(index); + } + } else { + StatisticWithContext statisticWrapper = new StatisticWithContext(retrieverChain, extractionFunction); + int index = availableStatistics.indexOf(statisticWrapper); + if (index != -1) { + extractionFunctionToSelect = availableStatistics.get(index); + } + } + extractionFunctionSuggestBox.setExtractionFunction(extractionFunctionToSelect); + if (extractionFunctionToSelect != null) { + setSettings(retrieverChain, queryDefinition.getRetrieverSettings()); + } else { + errorMessages.add(getDataMiningStringMessages().statisticNotAvailable( + extractionFunction.getDisplayName())); + } + + callback.accept(errorMessages); + } + + private void setSettings(DataRetrieverChainDefinitionDTO retrieverChain, HashMap settings) { + HashMap newSettings = new HashMap<>(); + for (int level = 0; level < retrieverChain.getLevelAmount(); level++) { + DataRetrieverLevelDTO retrieverLevel = retrieverChain.getRetrieverLevel(level); + if (retrieverLevel.hasSettings()) { + SerializableSettings levelSettings = settings.get(retrieverLevel); + if (levelSettings == null) { + levelSettings = retrieverLevel.getDefaultSettings(); + } + newSettings.put(retrieverLevel, levelSettings); + } + } + settingsMap.put(retrieverChain, newSettings); + } + + @Override + public void addStatisticChangedListener(StatisticChangedListener listener) { + retrieverChainListeners.add(listener); + extractionFunctionListeners.add(listener); + aggregatorDefinitionListeners.add(listener); + } + + @Override + public void addDataRetrieverChainDefinitionChangedListener(DataRetrieverChainDefinitionChangedListener listener) { + retrieverChainListeners.add(listener); + } + + private void notifyRetrieverChainListeners() { + DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition = getDataRetrieverChainDefinition(); + for (DataRetrieverChainDefinitionChangedListener listener : retrieverChainListeners) { + listener.dataRetrieverChainDefinitionChanged(dataRetrieverChainDefinition); + } + } + + @Override + public void addExtractionFunctionChangedListener(ExtractionFunctionChangedListener listener) { + extractionFunctionListeners.add(listener); + } + + private void notifyExtractionFunctionListeners() { + FunctionDTO extractionFunction = getExtractionFunction(); + for (ExtractionFunctionChangedListener listener : extractionFunctionListeners) { + listener.extractionFunctionChanged(extractionFunction); + } + } + + @Override + public void addAggregatorDefinitionChangedListener(AggregatorDefinitionChangedListener listener) { + aggregatorDefinitionListeners.add(listener); + } + + private void notifyAggregatorDefinitionListeners() { + AggregationProcessorDefinitionDTO aggregatorDefinition = getAggregatorDefinition(); + for (AggregatorDefinitionChangedListener listener : aggregatorDefinitionListeners) { + listener.aggregatorDefinitionChanged(aggregatorDefinition); + } + } + + @Override + public DataRetrieverChainDefinitionDTO getDataRetrieverChainDefinition() { + ExtractionFunctionWithContext extractionFunction = extractionFunctionSuggestBox.getExtractionFunction(); + return extractionFunction == null ? null : extractionFunction.getRetrieverChain(); + } + + @Override + public FunctionDTO getExtractionFunction() { + ExtractionFunctionWithContext extractionFunction = extractionFunctionSuggestBox.getExtractionFunction(); + return extractionFunction == null ? null : extractionFunction.getExtractionFunction(); + } + + @Override + public AggregationProcessorDefinitionDTO getAggregatorDefinition() { + AggregatorGroup aggregator = aggregatorListBox.getValue(); + FunctionDTO extractionFunction = getExtractionFunction(); + if (aggregator == null || extractionFunction == null) { + return null; + } + return aggregator.getForType(extractionFunction.getReturnTypeName()); + } + + @Override + public String getLocalizedShortName() { + return getDataMiningStringMessages().statisticProvider(); + } + + @Override + public Widget getEntryWidget() { + return mainPanel; + } + + @Override + public boolean isVisible() { + return mainPanel.isVisible(); + } + + @Override + public void setVisible(boolean visibility) { + mainPanel.setVisible(visibility); + } + + @Override + public String getDependentCssClassName() { + return "globalStatisticsProvider"; + } + + @Override + public String getId() { + return "GlobalStatisticProvider"; + } + +} diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettings.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettings.java index 7fd8b5f1614..cb2b1fe3258 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettings.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettings.java @@ -3,19 +3,30 @@ package com.sap.sse.datamining.ui.client.settings; import com.sap.sse.common.settings.AbstractSettings; public class AdvancedDataMiningSettings extends AbstractSettings { + + public enum ChangeLossStrategy { + /** Ask the user for the strategy to use */ + ASK, + /** Display the query overwriting the current selection */ + DISCARD_CHANGES, + /** Keep the current selection. The new query won't be displayed. */ + KEEP_CHANGES + } private boolean developerOptions; + private ChangeLossStrategy changeLossStrategy; public AdvancedDataMiningSettings() { - this(false); + this(false, ChangeLossStrategy.ASK); } - public AdvancedDataMiningSettings(boolean developerOptions) { + public AdvancedDataMiningSettings(boolean developerOptions, ChangeLossStrategy changeLossStrategy) { this.developerOptions = developerOptions; + this.changeLossStrategy = changeLossStrategy; } public AdvancedDataMiningSettings(AdvancedDataMiningSettings settings) { - this(settings.developerOptions); + this(settings.developerOptions, settings.changeLossStrategy); } public boolean isDeveloperOptions() { @@ -26,4 +37,12 @@ public class AdvancedDataMiningSettings extends AbstractSettings { this.developerOptions = developerOptions; } + public ChangeLossStrategy getChangeLossStrategy() { + return changeLossStrategy; + } + + public void setChangeLossStrategy(ChangeLossStrategy changeLossStrategy) { + this.changeLossStrategy = changeLossStrategy; + } + } diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettingsDialogComponent.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettingsDialogComponent.java index 74b9d38ec6c..5d2f00e6187 100644 --- a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettingsDialogComponent.java +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/AdvancedDataMiningSettingsDialogComponent.java @@ -1,10 +1,19 @@ package com.sap.sse.datamining.ui.client.settings; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RadioButton; +import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sse.datamining.ui.client.StringMessages; +import com.sap.sse.datamining.ui.client.settings.AdvancedDataMiningSettings.ChangeLossStrategy; import com.sap.sse.gwt.client.dialog.DataEntryDialog; import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; @@ -15,6 +24,7 @@ public class AdvancedDataMiningSettingsDialogComponent implements SettingsDialog private StringMessages stringMessages; private CheckBox developerOptionsCheckBox; + private Map changeLossStrategyButtons; public AdvancedDataMiningSettingsDialogComponent(AdvancedDataMiningSettings initialSettings, StringMessages stringMessages) { @@ -24,18 +34,43 @@ public class AdvancedDataMiningSettingsDialogComponent implements SettingsDialog @Override public Widget getAdditionalWidget(DataEntryDialog dialog) { - FlowPanel additionalWidget = new FlowPanel(); + Grid grid = new Grid(2, 2); + grid.setCellPadding(5); developerOptionsCheckBox = dialog.createCheckbox(stringMessages.developerOptions()); developerOptionsCheckBox.setValue(initialSettings.isDeveloperOptions()); - additionalWidget.add(developerOptionsCheckBox); + grid.setWidget(0, 0, developerOptionsCheckBox); + + Label changeLossStrategyLabel = dialog.createLabel(stringMessages.changeLossStrategy()); + changeLossStrategyLabel.setTitle(stringMessages.changeLossStrageyTooltip()); + grid.setWidget(1, 0, changeLossStrategyLabel); + + changeLossStrategyButtons = new HashMap<>(); + VerticalPanel changeLossStrategyButtonsPanel = new VerticalPanel(); + for (ChangeLossStrategy strategy : ChangeLossStrategy.values()) { + String label = ChangeLossStrategyFormatter.format(strategy, stringMessages); + RadioButton button = dialog.createRadioButton("changeLossStrategy", label); + button.setTitle(ChangeLossStrategyFormatter.tooltipFor(strategy, stringMessages)); + changeLossStrategyButtons.put(strategy, button); + changeLossStrategyButtonsPanel.add(button); + } + changeLossStrategyButtons.get(initialSettings.getChangeLossStrategy()).setValue(true); + grid.setWidget(1, 1, changeLossStrategyButtonsPanel); - return additionalWidget; + return grid; } @Override public AdvancedDataMiningSettings getResult() { - return new AdvancedDataMiningSettings(developerOptionsCheckBox.getValue()); + ChangeLossStrategy changeLossStrategy = null; + for (Entry entry : changeLossStrategyButtons.entrySet()) { + if (entry.getValue().getValue()) { + changeLossStrategy = entry.getKey(); + break; + } + } + Objects.requireNonNull(changeLossStrategy, "Change loss strategy mustn't be null"); + return new AdvancedDataMiningSettings(developerOptionsCheckBox.getValue(), changeLossStrategy); } @Override diff --git a/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/ChangeLossStrategyFormatter.java b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/ChangeLossStrategyFormatter.java new file mode 100644 index 00000000000..ca2b1c6087c --- /dev/null +++ b/java/com.sap.sse.datamining.ui/src/main/java/com/sap/sse/datamining/ui/client/settings/ChangeLossStrategyFormatter.java @@ -0,0 +1,32 @@ +package com.sap.sse.datamining.ui.client.settings; + +import com.sap.sse.datamining.ui.client.StringMessages; +import com.sap.sse.datamining.ui.client.settings.AdvancedDataMiningSettings.ChangeLossStrategy; + +public class ChangeLossStrategyFormatter { + + public static String format(ChangeLossStrategy strategy, StringMessages stringMessages) { + switch (strategy) { + case ASK: + return stringMessages.askChangeLossStrategyName(); + case DISCARD_CHANGES: + return stringMessages.discardChangesChangeLossStrategyName(); + case KEEP_CHANGES: + return stringMessages.keepChangesChangeLossStrategyName(); + } + return strategy.toString(); + } + + public static String tooltipFor(ChangeLossStrategy strategy, StringMessages stringMessages) { + switch (strategy) { + case ASK: + return stringMessages.askChangeLossStrategyTooltip(); + case DISCARD_CHANGES: + return stringMessages.discardChangesChangeLossStrategyTooltip(); + case KEEP_CHANGES: + return stringMessages.keepChangesChangeLossStrategyTooltip(); + } + return null; + } + +} diff --git a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties index 00ef0cb6874..c5fa2845db6 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties +++ b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages.properties @@ -1,7 +1,7 @@ Average=Average PairAverage=Average Median=Median -PairCollecting=Identity +PairCollecting=Scatter Sum=Sum Maximum=Maximum Minimum=Minimum @@ -11,4 +11,5 @@ Meters=m Knots=kn Collecting=Collection ResultSignifier={0} ({1}) -SignifierInUnit={0} in {1} \ No newline at end of file +SignifierInUnit={0} in {1} +Identity=Identity \ No newline at end of file diff --git a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties index 24bf33e5faf..a501a8c9771 100644 --- a/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties +++ b/java/com.sap.sse.datamining/resources/stringmessages/StringMessages_de.properties @@ -1,6 +1,6 @@ Average=Durchschnitt PairAverage=Durchschnitt -PairCollecting=Identität +PairCollecting=Streuung Median=Median Sum=Summe Maximum=Maximum @@ -11,4 +11,5 @@ Meters=m Knots=kn Collecting=Sammlung ResultSignifier={0} ({1}) -SignifierInUnit={0} in {1} \ No newline at end of file +SignifierInUnit={0} in {1} +Identity=Identität \ No newline at end of file diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/DataMiningServer.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/DataMiningServer.java index 7bc335154e8..4a077db48da 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/DataMiningServer.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/DataMiningServer.java @@ -36,21 +36,16 @@ public interface DataMiningServer { Date getComponentsChangedTimepoint(); FunctionProvider getFunctionProvider(); - Iterable> getAllStatistics(); - Iterable> getFunctionsFor(Class sourceType); - Iterable> getStatisticsFor(Class sourceType); - Iterable> getDimensionsFor(Class sourceType); - Map, Iterable>> getDimensionsMappedByLevelFor(DataRetrieverChainDefinition dataRetrieverChainDefinition); - ReducedDimensions getReducedDimensionsMappedByLevelFor(DataRetrieverChainDefinition dataRetrieverChainDefinition); Function getFunctionForDTO(FunctionDTO functionDTO); DataRetrieverChainDefinitionProvider getDataRetrieverChainDefinitionProvider(); - Iterable> getDataRetrieverChainDefinitions(); - Iterable> getDataRetrieverChainDefinitionsBySourceType(Class dataSourceType); - Iterable> getDataRetrieverChainDefinitionsByDataType(Class retrievedDataType); - Iterable> getDataRetrieverChainDefinitions(Class dataSourceType, Class retrievedDataType); DataRetrieverChainDefinition getDataRetrieverChainDefinitionForDTO(DataRetrieverChainDefinitionDTO retrieverChainDTO); + AggregationProcessorDefinitionProvider getAggregationProcessorProvider(); + AggregationProcessorDefinition getAggregationProcessorDefinitionForDTO(AggregationProcessorDefinitionDTO aggregatorDefinitionDTO); + + QueryDefinitionDTOProvider getQueryDefinitionDTOProvider(); + Query> createDimensionValuesQuery(DataRetrieverChainDefinition dataRetrieverChainDefinition, DataRetrieverLevel retrieverLevel, Iterable> dimensions, Map, SerializableSettings> settings, Map, Map, Collection>> filterSelection, Locale locale); StatisticQueryDefinition getQueryDefinitionForDTO(StatisticQueryDefinitionDTO queryDefinitionDTO); @@ -58,13 +53,80 @@ public interface DataMiningServer { QueryResult runNewQueryAndAbortPreviousQueries(DataMiningSession session, Query query); int getNumberOfRunningQueries(); - AggregationProcessorDefinitionProvider getAggregationProcessorProvider(); - Iterable> getAggregationProcessorDefinitions(Class extractedType); - AggregationProcessorDefinition getAggregationProcessorDefinition(Class extractedType, String aggregationNameMessageKey); - AggregationProcessorDefinition getAggregationProcessorDefinitionForDTO(AggregationProcessorDefinitionDTO aggregatorDefinitionDTO); + //----------------------------------------------------------------------------------------------------------------- + // Component Accessors as default methods + //----------------------------------------------------------------------------------------------------------------- + + // Functions ------------------------------------------------------------------------------------------------------ - QueryDefinitionDTOProvider getQueryDefinitionDTOProvider(); - Iterable getPredefinedQueryIdentifiers(); - ModifiableStatisticQueryDefinitionDTO getPredefinedQueryDefinitionDTO(PredefinedQueryIdentifier identifier); + default Function getIdentityFunction() { + return getFunctionProvider().getIdentityFunction(); + } + + default Iterable> getAllStatistics() { + return getFunctionProvider().getAllStatistics(); + } + + default Iterable> getFunctionsFor(Class sourceType) { + return getFunctionProvider().getFunctionsFor(sourceType); + } + + default Iterable> getStatisticsFor(Class sourceType) { + return getFunctionProvider().getStatisticsFor(sourceType); + } + + default Iterable> getDimensionsFor(Class sourceType) { + return getFunctionProvider().getDimensionsFor(sourceType); + } + + default Map, Iterable>> getDimensionsMappedByLevelFor(DataRetrieverChainDefinition dataRetrieverChainDefinition) { + return getFunctionProvider().getDimensionsMappedByLevelFor(dataRetrieverChainDefinition); + } + + default ReducedDimensions getReducedDimensionsMappedByLevelFor(DataRetrieverChainDefinition dataRetrieverChainDefinition) { + return getFunctionProvider().getReducedDimensionsMappedByLevelFor(dataRetrieverChainDefinition); + } + + // Retriever Chains ----------------------------------------------------------------------------------------------- + + default Iterable> getDataRetrieverChainDefinitions() { + return getDataRetrieverChainDefinitionProvider().getAll(); + } + + default Iterable> getDataRetrieverChainDefinitionsBySourceType(Class dataSourceType) { + return getDataRetrieverChainDefinitionProvider().getBySourceType(dataSourceType); + } + + default Iterable> getDataRetrieverChainDefinitionsByDataType(Class retrievedDataType) { + return getDataRetrieverChainDefinitionProvider().getByDataType(retrievedDataType); + } + + default Iterable> getDataRetrieverChainDefinitions(Class dataSourceType, Class retrievedDataType) { + return getDataRetrieverChainDefinitionProvider().get(dataSourceType, retrievedDataType); + } + + // Aggregators ---------------------------------------------------------------------------------------------------- + + default Iterable> getAllAggregationProcessorDefinitions() { + return getAggregationProcessorProvider().getAll(); + } + + default Iterable> getAggregationProcessorDefinitions(Class extractedType) { + return getAggregationProcessorProvider().getByExtractedType(extractedType); + } + + default AggregationProcessorDefinition getAggregationProcessorDefinition(Class extractedType, String aggregationNameMessageKey) { + return getAggregationProcessorProvider().get(extractedType, aggregationNameMessageKey); + } + + // Predefined Queries --------------------------------------------------------------------------------------------- + + default Iterable getPredefinedQueryIdentifiers() { + return getQueryDefinitionDTOProvider().getIdentifiers(); + } + + default ModifiableStatisticQueryDefinitionDTO getPredefinedQueryDefinitionDTO(PredefinedQueryIdentifier identifier) { + return getQueryDefinitionDTOProvider().get(identifier); + } } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/AdditionalResultDataBuilder.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/AdditionalResultDataBuilder.java index 6a18852bf22..6d1414fee55 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/AdditionalResultDataBuilder.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/AdditionalResultDataBuilder.java @@ -17,6 +17,8 @@ public interface AdditionalResultDataBuilder { public void setRetrievedDataAmount(int retrievedDataAmount); public int getRetrievedDataAmount(); + + void setDataTypeMessageKey(String dataTypeMessageKey); /** * Sets the used {@link Function} for the statistic extraction. Also sets the amount of decimals to the values of diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/AggregationProcessorDefinitionProvider.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/AggregationProcessorDefinitionProvider.java index 8f4ae5d79ef..4caae62fba1 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/AggregationProcessorDefinitionProvider.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/AggregationProcessorDefinitionProvider.java @@ -5,8 +5,9 @@ import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; public interface AggregationProcessorDefinitionProvider { - public AggregationProcessorDefinition get(Class extractedType, String aggregationNameMessageKey); - public Iterable> getByExtractedType(Class extractedType); - public AggregationProcessorDefinition getForDTO(AggregationProcessorDefinitionDTO aggregatorDefinitionDTO, ClassLoader classLoader); + Iterable> getAll(); + AggregationProcessorDefinition get(Class extractedType, String aggregationNameMessageKey); + Iterable> getByExtractedType(Class extractedType); + AggregationProcessorDefinition getForDTO(AggregationProcessorDefinitionDTO aggregatorDefinitionDTO, ClassLoader classLoader); } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/FunctionProvider.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/FunctionProvider.java index de2e410c476..e2636e3a4a3 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/FunctionProvider.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/components/management/FunctionProvider.java @@ -7,10 +7,13 @@ import com.sap.sse.datamining.components.DataRetrieverChainDefinition; import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.DataRetrieverLevel; import com.sap.sse.datamining.impl.components.management.ReducedDimensions; +import com.sap.sse.datamining.impl.functions.IdentityFunction; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; public interface FunctionProvider { + public IdentityFunction getIdentityFunction(); + public Collection> getAllStatistics(); public Collection> getFunctionsFor(Class sourceType); diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/DataMiningDTOFactory.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/DataMiningDTOFactory.java index 0e2f36bd1a7..1bade7728ac 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/DataMiningDTOFactory.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/DataMiningDTOFactory.java @@ -2,9 +2,16 @@ package com.sap.sse.datamining.factories; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import com.sap.sse.common.settings.SerializableSettings; +import com.sap.sse.datamining.StatisticQueryDefinition; import com.sap.sse.datamining.annotations.Connector; import com.sap.sse.datamining.annotations.Dimension; import com.sap.sse.datamining.components.AggregationProcessorDefinition; @@ -12,16 +19,64 @@ import com.sap.sse.datamining.components.DataRetrieverChainDefinition; import com.sap.sse.datamining.data.QueryResult; import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.DataRetrieverLevel; +import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; import com.sap.sse.datamining.shared.impl.dto.LocalizedTypeDTO; +import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.QueryResultDTO; import com.sap.sse.i18n.ResourceBundleStringMessages; public class DataMiningDTOFactory { + public StatisticQueryDefinitionDTO createQueryDefinitionDTO(StatisticQueryDefinition queryDefinition, + ResourceBundleStringMessages stringMessages, Locale locale, String localeInfoName) { + FunctionDTO statisticToCalculate = createFunctionDTO(queryDefinition.getStatisticToCalculate(), stringMessages, + locale); + AggregationProcessorDefinitionDTO aggregatorDefinition = createAggregationProcessorDefinitionDTO( + queryDefinition.getAggregatorDefinition(), stringMessages, locale); + DataRetrieverChainDefinitionDTO dataRetrieverChainDefinition = createDataRetrieverChainDefinitionDTO( + queryDefinition.getDataRetrieverChainDefinition(), stringMessages, locale); + ModifiableStatisticQueryDefinitionDTO queryDefinitionDTO = new ModifiableStatisticQueryDefinitionDTO( + localeInfoName, statisticToCalculate, aggregatorDefinition, dataRetrieverChainDefinition); + + Map, SerializableSettings> retrieverSettings = queryDefinition.getRetrieverSettings(); + Map, Map, Collection>> filterSelection = queryDefinition + .getFilterSelection(); + DataRetrieverChainDefinition retrieverChain = queryDefinition.getDataRetrieverChainDefinition(); + List> retrieverLevels = retrieverChain.getDataRetrieverLevels(); + for (int level = 0; level < retrieverLevels.size(); level++) { + DataRetrieverLevel retrieverLevel = retrieverLevels.get(level); + DataRetrieverLevelDTO retrieverLevelDTO = dataRetrieverChainDefinition.getRetrieverLevel(level); + + SerializableSettings levelSettings = retrieverSettings.get(retrieverLevel); + if (levelSettings != null) { + queryDefinitionDTO.setRetrieverSettings(retrieverLevelDTO, levelSettings); + } + + HashMap> levelFilterSelection = new HashMap<>(); + if (filterSelection.containsKey(retrieverLevel)) { + for (Entry, Collection> dimensionFilterSelection : filterSelection.get(retrieverLevel) + .entrySet()) { + @SuppressWarnings("unchecked") + HashSet filterValues = new HashSet<>( + (Collection) dimensionFilterSelection.getValue()); + levelFilterSelection.put( + createFunctionDTO(dimensionFilterSelection.getKey(), stringMessages, locale), filterValues); + } + } + queryDefinitionDTO.setFilterSelectionFor(retrieverLevelDTO, levelFilterSelection); + } + + for (Function dimensionToGroupBy : queryDefinition.getDimensionsToGroupBy()) { + queryDefinitionDTO.appendDimensionToGroupBy(createFunctionDTO(dimensionToGroupBy, stringMessages, locale)); + } + + return queryDefinitionDTO; + } + /** * Creates the corresponding DTO for the given {@link Function}, without localization.
    * The display name of the resulting DTO is the {@link Function#getSimpleName() simple name} of the given function. diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java index 4873623673f..51d126d18e3 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/QueryFactory.java @@ -56,6 +56,7 @@ public class QueryFactory { Processor> groupingProcessor = processorFactory .createGroupingProcessor(dataTypeToRetrieve, extractionProcessor, getParameterProvidersFor( queryDefinition.getDimensionsToGroupBy(), stringMessages, locale)); + DataRetrieverChainBuilder chainBuilder = queryDefinition.getDataRetrieverChainDefinition().startBuilding(executor); Map, FilterCriterion> criteriaMappedByRetrieverLevel = createFilterCriteria( queryDefinition.getFilterSelection(), stringMessages, locale); diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningServerImpl.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningServerImpl.java index 762b9431a67..63832d85b8a 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningServerImpl.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/DataMiningServerImpl.java @@ -38,7 +38,6 @@ import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.DataRetrieverLevel; import com.sap.sse.datamining.impl.components.management.AbstractMemoryMonitorAction; import com.sap.sse.datamining.impl.components.management.QueryManagerMemoryMonitor; -import com.sap.sse.datamining.impl.components.management.ReducedDimensions; import com.sap.sse.datamining.impl.components.management.RuntimeMemoryInfoProvider; import com.sap.sse.datamining.impl.components.management.StrategyPerQueryTypeManager; import com.sap.sse.datamining.shared.DataMiningSession; @@ -48,7 +47,6 @@ import com.sap.sse.datamining.shared.impl.dto.AggregationProcessorDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverChainDefinitionDTO; import com.sap.sse.datamining.shared.impl.dto.DataRetrieverLevelDTO; import com.sap.sse.datamining.shared.impl.dto.FunctionDTO; -import com.sap.sse.datamining.shared.impl.dto.ModifiableStatisticQueryDefinitionDTO; import com.sap.sse.i18n.ResourceBundleStringMessages; import com.sap.sse.i18n.impl.CompoundResourceBundleStringMessages; import com.sap.sse.util.JoinedClassLoader; @@ -208,38 +206,7 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer { public FunctionProvider getFunctionProvider() { return functionRegistry; } - - @Override - public Iterable> getAllStatistics() { - return functionRegistry.getAllStatistics(); - } - - @Override - public Iterable> getFunctionsFor(Class sourceType) { - return functionRegistry.getFunctionsFor(sourceType); - } - - @Override - public Iterable> getStatisticsFor(Class sourceType) { - return functionRegistry.getStatisticsFor(sourceType); - } - - @Override - public Iterable> getDimensionsFor(Class sourceType) { - return functionRegistry.getDimensionsFor(sourceType); - } - - @Override - public Map, Iterable>> getDimensionsMappedByLevelFor(DataRetrieverChainDefinition dataRetrieverChainDefinition) { - return functionRegistry.getDimensionsMappedByLevelFor(dataRetrieverChainDefinition); - } - @Override - public ReducedDimensions getReducedDimensionsMappedByLevelFor( - DataRetrieverChainDefinition dataRetrieverChainDefinition) { - return functionRegistry.getReducedDimensionsMappedByLevelFor(dataRetrieverChainDefinition); - } - @Override public Function getFunctionForDTO(FunctionDTO functionDTO) { return functionRegistry.getFunctionForDTO(functionDTO, getJoinedClassLoader()); @@ -271,11 +238,6 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer { return dataRetrieverChainDefinitionRegistry; } - @Override - public Iterable> getDataRetrieverChainDefinitions() { - return dataRetrieverChainDefinitionRegistry.getAll(); - } - @Override public void registerDataRetrieverChainDefinition(DataRetrieverChainDefinition dataRetrieverChainDefinition) { boolean componentsChanged = dataRetrieverChainDefinitionRegistry.register(dataRetrieverChainDefinition); @@ -291,24 +253,6 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer { updateComponentsChangedTimepoint(); } } - - @Override - public Iterable> getDataRetrieverChainDefinitionsBySourceType( - Class dataSourceType) { - return dataRetrieverChainDefinitionRegistry.getBySourceType(dataSourceType); - } - - @Override - public Iterable> getDataRetrieverChainDefinitionsByDataType( - Class retrievedDataType) { - return dataRetrieverChainDefinitionRegistry.getByDataType(retrievedDataType); - } - - @Override - public Iterable> getDataRetrieverChainDefinitions( - Class dataSourceType, Class retrievedDataType) { - return dataRetrieverChainDefinitionRegistry.get(dataSourceType, retrievedDataType); - } @Override public DataRetrieverChainDefinition getDataRetrieverChainDefinitionForDTO(DataRetrieverChainDefinitionDTO retrieverChainDTO) { @@ -320,18 +264,6 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer { return aggregationProcessorDefinitionRegistry; } - @Override - public Iterable> getAggregationProcessorDefinitions( - Class extractedType) { - return aggregationProcessorDefinitionRegistry.getByExtractedType(extractedType); - } - - @Override - public AggregationProcessorDefinition getAggregationProcessorDefinition( - Class extractedType, String aggregationNameMessageKey) { - return aggregationProcessorDefinitionRegistry.get(extractedType, aggregationNameMessageKey); - } - @Override public AggregationProcessorDefinition getAggregationProcessorDefinitionForDTO(AggregationProcessorDefinitionDTO aggregatorDefinitionDTO) { return aggregationProcessorDefinitionRegistry.getForDTO(aggregatorDefinitionDTO, getJoinedClassLoader()); @@ -363,16 +295,6 @@ public class DataMiningServerImpl implements ModifiableDataMiningServer { return queryDefinitionRegistry; } - @Override - public Iterable getPredefinedQueryIdentifiers() { - return queryDefinitionRegistry.getIdentifiers(); - } - - @Override - public ModifiableStatisticQueryDefinitionDTO getPredefinedQueryDefinitionDTO(PredefinedQueryIdentifier identifier) { - return queryDefinitionRegistry.get(identifier); - } - @Override public QueryDefinitionDTORegistry getQueryDefinitionDTORegistry() { return queryDefinitionRegistry; diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/AbstractRetrievalProcessor.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/AbstractRetrievalProcessor.java index 7f8eaccc20a..ecae215882b 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/AbstractRetrievalProcessor.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/AbstractRetrievalProcessor.java @@ -19,6 +19,7 @@ import com.sap.sse.datamining.components.ProcessorInstruction; public abstract class AbstractRetrievalProcessor extends AbstractParallelProcessor { private final int retrievalLevel; + private final String retrievedDataTypeMessageKey; private final AtomicInteger retrievedDataAmount; /** @@ -26,9 +27,11 @@ public abstract class AbstractRetrievalProcessor extends * The position of this retriever in it's chain. 0 represents the first. */ public AbstractRetrievalProcessor(Class inputType, Class resultType, - ExecutorService executor, Collection> resultReceivers, int retrievalLevel) { + ExecutorService executor, Collection> resultReceivers, int retrievalLevel, + String retrievedDataTypeMessageKey) { super(inputType, resultType, executor, resultReceivers); this.retrievalLevel = retrievalLevel; + this.retrievedDataTypeMessageKey = retrievedDataTypeMessageKey; retrievedDataAmount = new AtomicInteger(); } @@ -53,6 +56,7 @@ public abstract class AbstractRetrievalProcessor extends @Override protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) { + additionalDataBuilder.setDataTypeMessageKey(retrievedDataTypeMessageKey); additionalDataBuilder.setRetrievedDataAmount(retrievedDataAmount.get()); } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/OverwritingResultDataBuilder.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/OverwritingResultDataBuilder.java index 39d35382caf..45a176adfe6 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/OverwritingResultDataBuilder.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/OverwritingResultDataBuilder.java @@ -4,6 +4,7 @@ import java.util.Locale; import com.sap.sse.datamining.components.AdditionalResultDataBuilder; import com.sap.sse.datamining.functions.Function; +import com.sap.sse.datamining.impl.functions.IdentityFunction; import com.sap.sse.datamining.shared.AdditionalResultData; import com.sap.sse.datamining.shared.impl.AdditionalResultDataImpl; import com.sap.sse.i18n.ResourceBundleStringMessages; @@ -16,8 +17,11 @@ import com.sap.sse.i18n.ResourceBundleStringMessages; * */ public class OverwritingResultDataBuilder implements AdditionalResultDataBuilder { + + private static final Function IdentityFunction = new IdentityFunction(); private int retrievedDataAmount; + private String dataTypeMessageKey; private Function extractionFunction; private String aggregationNameMessageKey; private int resultDecimals; @@ -40,8 +44,14 @@ public class OverwritingResultDataBuilder implements AdditionalResultDataBuilder if (extractionFunction == null || aggregationNameMessageKey == null) { return ""; } + + String extractedStatisticName; + if (extractionFunction.equals(IdentityFunction)) { + extractedStatisticName = stringMessages.get(locale, dataTypeMessageKey); + } else { + extractedStatisticName = extractionFunction.getLocalizedName(locale, stringMessages); + } - String extractedStatisticName = extractionFunction.getLocalizedName(locale, stringMessages); String aggregationName = stringMessages.get(locale, aggregationNameMessageKey); return stringMessages.get(locale, "ResultSignifier", extractedStatisticName, aggregationName); } @@ -55,6 +65,15 @@ public class OverwritingResultDataBuilder implements AdditionalResultDataBuilder public int getRetrievedDataAmount() { return retrievedDataAmount; } + + @Override + public void setDataTypeMessageKey(String dataTypeMessageKey) { + this.dataTypeMessageKey = dataTypeMessageKey; + } + + public String getDataTypeMessageKey() { + return dataTypeMessageKey; + } @Override public void setExtractionFunction(Function extractionFunction) { diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainBuilder.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainBuilder.java index 13e10002fca..b151e1b239e 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainBuilder.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainBuilder.java @@ -168,17 +168,19 @@ public class SimpleDataRetrieverChainBuilder implements DataRetr Class> retrieverType = (Class>)(Class) retrieverLevel.getRetrieverType(); Class settingsType = retrieverLevel.getSettingsType(); - return createRetriever(retrieverType, retrievedDataType, resultReceivers, filter, settingsType, retrieverLevelIndex); + String retrievedDataTypeMessageKey = retrieverLevel.getRetrievedDataTypeMessageKey(); + return createRetriever(retrieverType, retrievedDataType, resultReceivers, filter, settingsType, retrieverLevelIndex, retrievedDataTypeMessageKey); } private Processor createRetriever(Class> retrieverType, Class retrievedDataType, - Collection> resultReceivers, FilterCriterion filter, Class settingsType, int retrieverLevelIndex) { + Collection> resultReceivers, FilterCriterion filter, Class settingsType, int retrieverLevelIndex, + String retrievedDataTypeMessageKey) { Constructor> retrieverConstructor = null; try { if (settingsType == null) { - retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class); + retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class, String.class); } else { - retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class, settingsType, int.class); + retrieverConstructor = retrieverType.getConstructor(ExecutorService.class, Collection.class, settingsType, int.class, String.class); } } catch (NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException("Couldn't get an usable constructor from the given retrieverType '" @@ -190,11 +192,12 @@ public class SimpleDataRetrieverChainBuilder implements DataRetr SerializableSettings storedSettings = this.settings.get(retrieverLevelIndex); settings = storedSettings != null ? storedSettings : retrieverLevels.get(retrieverLevelIndex).getDefaultSettings(); } - return constructRetriever(retrieverConstructor, retrievedDataType, resultReceivers, filter, settings, settingsType, retrieverLevelIndex); + return constructRetriever(retrieverConstructor, retrievedDataType, resultReceivers, filter, settings, settingsType, retrieverLevelIndex, retrievedDataTypeMessageKey); } private Processor constructRetriever(Constructor> retrieverConstructor, Class retrievedDataType, - Collection> resultReceivers, FilterCriterion filter, SerializableSettings settings, Class settingsType, int retrieverLevelIndex) { + Collection> resultReceivers, FilterCriterion filter, SerializableSettings settings, Class settingsType, + int retrieverLevelIndex, String retrievedDataTypeMessageKey) { try { Collection> retrievalResultReceivers = resultReceivers; if (filter != null) { @@ -211,9 +214,9 @@ public class SimpleDataRetrieverChainBuilder implements DataRetr } if (settingsType == null) { - return retrieverConstructor.newInstance(executor, retrievalResultReceivers, retrieverLevelIndex); + return retrieverConstructor.newInstance(executor, retrievalResultReceivers, retrieverLevelIndex, retrievedDataTypeMessageKey); } else { - return retrieverConstructor.newInstance(executor, retrievalResultReceivers, settings, retrieverLevelIndex); + return retrieverConstructor.newInstance(executor, retrievalResultReceivers, settings, retrieverLevelIndex, retrievedDataTypeMessageKey); } } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainDefinition.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainDefinition.java index bde9ac60791..b383f8d8b57 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainDefinition.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/SimpleDataRetrieverChainDefinition.java @@ -122,9 +122,9 @@ public class SimpleDataRetrieverChainDefinition implem Class> retrieverType, Class settingsType) { try { if (settingsType == null) { - retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class); + retrieverType.getConstructor(ExecutorService.class, Collection.class, int.class, String.class); } else { - retrieverType.getConstructor(ExecutorService.class, Collection.class, settingsType, int.class); + retrieverType.getConstructor(ExecutorService.class, Collection.class, settingsType, int.class, String.class); } } catch (NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException("Couldn't get an usable constructor from the given nextRetrieverType '" diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairAverageAggregationProcessor.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairAverageAggregationProcessor.java index 69a35eebbf9..b3a484735ed 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairAverageAggregationProcessor.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/aggregators/ParallelGroupedNumberPairAverageAggregationProcessor.java @@ -25,7 +25,7 @@ public class ParallelGroupedNumberPairAverageAggregationProcessor private static final AggregationProcessorDefinition> DEFINITION = new SimpleAggregationProcessorDefinition<>(Pair.class, - _cc, "PairAverage", ParallelGroupedNumberPairAverageAggregationProcessor.class); + _cc, "Average", ParallelGroupedNumberPairAverageAggregationProcessor.class); public static AggregationProcessorDefinition> getDefinition() { return DEFINITION; diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/AggregationProcessorDefinitionManager.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/AggregationProcessorDefinitionManager.java index 11203b08f13..40a0825bb8e 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/AggregationProcessorDefinitionManager.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/AggregationProcessorDefinitionManager.java @@ -21,13 +21,25 @@ public class AggregationProcessorDefinitionManager implements AggregationProcess definitionsMappedByExtractedTypeAndMessageKey = new HashMap<>(); } + @Override + public Iterable> getAll() { + Collection> allDefinitions = new HashSet<>(); + for (Map> definitionsForType : definitionsMappedByExtractedTypeAndMessageKey.values()) { + allDefinitions.addAll(definitionsForType.values()); + } + return allDefinitions; + } + @SuppressWarnings("unchecked") @Override public AggregationProcessorDefinition get(Class extractedType, String aggregationNameMessageKey) { - if (definitionsMappedByExtractedTypeAndMessageKey.containsKey(extractedType)) { - return (AggregationProcessorDefinition) definitionsMappedByExtractedTypeAndMessageKey.get(extractedType).get(aggregationNameMessageKey); + AggregationProcessorDefinition definition = null; + Map> definitionsForType = definitionsMappedByExtractedTypeAndMessageKey.get(extractedType); + if (definitionsForType != null) { + definition = (AggregationProcessorDefinition) definitionsForType + .get(aggregationNameMessageKey); } - return null; + return definition; } @Override diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/FunctionManager.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/FunctionManager.java index eee01d9f783..cf529c96968 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/FunctionManager.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/management/FunctionManager.java @@ -20,10 +20,12 @@ import com.sap.sse.datamining.components.FilterCriterion; import com.sap.sse.datamining.components.management.FunctionProvider; import com.sap.sse.datamining.components.management.FunctionRegistry; import com.sap.sse.datamining.exceptions.MultipleDataMiningComponentsFoundForDTOException; +import com.sap.sse.datamining.factories.DataMiningDTOFactory; import com.sap.sse.datamining.factories.FunctionFactory; import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.DataRetrieverLevel; import com.sap.sse.datamining.impl.functions.ConcatenatingCompoundFunction; +import com.sap.sse.datamining.impl.functions.IdentityFunction; import com.sap.sse.datamining.impl.functions.MethodWrappingFunction; import com.sap.sse.datamining.impl.functions.criterias.MethodIsValidConnectorFilterCriterion; import com.sap.sse.datamining.impl.functions.criterias.MethodIsValidDimensionFilterCriterion; @@ -67,6 +69,9 @@ public class FunctionManager implements FunctionRegistry, FunctionProvider { private final FunctionFactory functionFactory; + protected final IdentityFunction identityFunction; + protected final FunctionDTO identityFunctionDTO; + protected final Map, Set>> statistics; protected final Map, Set>> dimensions; protected final Map, Set>> externalFunctions; @@ -76,6 +81,9 @@ public class FunctionManager implements FunctionRegistry, FunctionProvider { public FunctionManager() { functionFactory = new FunctionFactory(); + identityFunction = new IdentityFunction(); + identityFunctionDTO = new DataMiningDTOFactory().createFunctionDTO(identityFunction); + statistics = new HashMap<>(); dimensions = new HashMap<>(); externalFunctions = new HashMap<>(); @@ -209,6 +217,11 @@ public class FunctionManager implements FunctionRegistry, FunctionProvider { return functionsHaveBeenUnregistered; } + @Override + public IdentityFunction getIdentityFunction() { + return identityFunction; + } + @Override public Collection> getAllStatistics() { return asSet(statistics); @@ -361,6 +374,10 @@ public class FunctionManager implements FunctionRegistry, FunctionProvider { @Override public Function getFunctionForDTO(FunctionDTO functionDTO, ClassLoader classLoader) { + if (identityFunctionDTO.equals(functionDTO)) { + return identityFunction; + } + Function function = null; if (functionDTO != null) { try { diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/data/LocalizedCluster.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/data/LocalizedCluster.java index eb44417aecf..575843c20cd 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/data/LocalizedCluster.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/data/LocalizedCluster.java @@ -35,5 +35,10 @@ public class LocalizedCluster implements Clust public Cluster getInnerCluster() { return cluster; } + + @Override + public String toString() { + return messageKey + ": " + cluster.toString(); + } } diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/IdentityFunction.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/IdentityFunction.java new file mode 100644 index 00000000000..9ff0de18da1 --- /dev/null +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/functions/IdentityFunction.java @@ -0,0 +1,84 @@ +package com.sap.sse.datamining.impl.functions; + +import java.util.Collections; +import java.util.Locale; + +import com.sap.sse.datamining.functions.ParameterProvider; +import com.sap.sse.i18n.ResourceBundleStringMessages; + +public class IdentityFunction extends AbstractFunction { + + public IdentityFunction() { + super(false); + } + + @Override + public Class getDeclaringType() { + return Object.class; + } + + @Override + public Iterable> getParameters() { + return Collections.emptySet(); + } + + @Override + public boolean needsLocalizationParameters() { + return false; + } + + @Override + public Class getReturnType() { + return Object.class; + } + + @Override + public String getSimpleName() { + return "Identity"; + } + + @Override + public boolean isLocalizable() { + return true; + } + + @Override + public String getLocalizedName(Locale locale, ResourceBundleStringMessages stringMessages) { + return stringMessages.get(locale, "Identity"); + } + + @Override + public Object tryToInvoke(Object instance) { + return instance; + } + + @Override + public Object tryToInvoke(Object instance, ParameterProvider parameterProvider) { + return instance; + } + + @Override + public int getResultDecimals() { + return 0; + } + + @Override + public int getOrdinal() { + return Integer.MAX_VALUE; + } + + @Override + public int hashCode() { + return IdentityFunction.class.getCanonicalName().hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!IdentityFunction.class.equals(other.getClass())) { + return false; + } + // Ensure equality, if the classes have been loaded by different class loaders + return IdentityFunction.class.getCanonicalName().equals(other.getClass().getCanonicalName()); + } + +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java index 519f23db652..1851c160d8b 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractListSuggestOracle.java @@ -2,6 +2,7 @@ package com.sap.sse.gwt.client.suggestion; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import com.sap.sse.common.filter.AbstractListFilter; @@ -31,7 +32,11 @@ public abstract class AbstractListSuggestOracle extends AbstractSuggestOracle return suggestionMatchingFilter; } - public void setSelectableValues(Collection selectableValues) { + public Collection getSelectableValues() { + return Collections.unmodifiableList(suggestionObjectList); + } + + public void setSelectableValues(Collection selectableValues) { suggestionObjectList.clear(); suggestionObjectList.addAll(selectableValues); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractSuggestOracle.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractSuggestOracle.java index 5b737ee34f8..4d617d90fb2 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractSuggestOracle.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/suggestion/AbstractSuggestOracle.java @@ -20,7 +20,7 @@ public abstract class AbstractSuggestOracle extends SuggestOracle { } int count = 0; for (T match : suggestionObjects) { - suggestions.add(new SimpleSuggestion(match, normalizedQueryTokens)); + suggestions.add(createSuggestion(match, normalizedQueryTokens)); if (++count >= request.getLimit()) { break; } @@ -30,6 +30,10 @@ public abstract class AbstractSuggestOracle extends SuggestOracle { callback.onSuggestionsReady(request, response); } + protected SimpleSuggestion createSuggestion(T match, Iterable queryTokens) { + return new SimpleSuggestion(match, queryTokens); + } + protected abstract void getSuggestions(Request request, Callback callback, Iterable queryTokens); protected abstract String createSuggestionKeyString(T value); @@ -57,14 +61,14 @@ public abstract class AbstractSuggestOracle extends SuggestOracle { return true; } - class SimpleSuggestion implements Suggestion { + public class SimpleSuggestion implements Suggestion { private static final String STRONG_TAG_OPEN = "", STRONG_TAG_CLOSE = ""; private static final String ITALIC_TAG_OPEN = "", ITALIC_TAG_CLOSE = ""; private final T suggestObject; private final Iterable queryTokens; - private SimpleSuggestion(T suggestObject, Iterable queryTokens) { + public SimpleSuggestion(T suggestObject, Iterable queryTokens) { this.suggestObject = suggestObject; this.queryTokens = queryTokens; } diff --git a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java index 5978dc7c4a0..e31ea696019 100755 --- a/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java +++ b/mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/StartTimeFragment.java @@ -506,8 +506,8 @@ public class StartTimeFragment extends BaseFragment mTimePicker.setIs24HourView(true); int hours = time.get(Calendar.HOUR_OF_DAY); int minutes = time.get(Calendar.MINUTE); - if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) != MODE_TIME_PANEL && mRaceId == null - && mStartTimeOffset == null) { + if (getArguments() != null && getArguments().getInt(START_MODE, START_MODE_PRESETUP) != MODE_TIME_PANEL + && getArguments().getSerializable(MainScheduleFragment.START_TIME) == null) { // In 10 minutes from now, but always a 5-minute-mark. time.add(Calendar.MINUTE, 10); hours = time.get(Calendar.HOUR_OF_DAY); @@ -515,6 +515,7 @@ public class StartTimeFragment extends BaseFragment minutes = (int) (Math.ceil((minutes / 5.0)) * 5.0); if (minutes >= 60) { hours++; + minutes = 0; } } mTimePicker.setCurrentHour(hours); @@ -623,7 +624,7 @@ public class StartTimeFragment extends BaseFragment break; case R.id.set_start_time_relative: - changeFragment(MillisecondsTimePoint.now(), new MillisecondsDurationImpl(mTimeOffset.getValue() * 60 * 1000), identifier); + changeFragment(mStartTime, new MillisecondsDurationImpl(mTimeOffset.getValue() * 60 * 1000), identifier); break; case R.id.sync_to_minute: @@ -772,9 +773,12 @@ public class StartTimeFragment extends BaseFragment viewId = R.id.race_content; } args.putAll(getArguments()); - args.putSerializable(MainScheduleFragment.START_TIME, startTime); - args.putSerializable(MainScheduleFragment.START_TIME_DIFF, startTimeDiff); - args.putSerializable(MainScheduleFragment.DEPENDENT_RACE, identifier); + if (startTimeDiff == null) { + args.putSerializable(MainScheduleFragment.START_TIME, startTime); + } else { + args.putSerializable(MainScheduleFragment.START_TIME_DIFF, startTimeDiff); + args.putSerializable(MainScheduleFragment.DEPENDENT_RACE, identifier); + } } fragment.setArguments(args); transaction.replace(viewId, fragment); diff --git a/wiki/howto/development/selenium-ui-tests.md b/wiki/howto/development/selenium-ui-tests.md index bc6d58f4b92..e59bf62c576 100644 --- a/wiki/howto/development/selenium-ui-tests.md +++ b/wiki/howto/development/selenium-ui-tests.md @@ -107,6 +107,26 @@ Since a full Maven build needs some time, you can also execute the tests in the For a more practical example of how to write page objects and test you should take a look at the [[tutorial|wiki/howto/misc/ui-tests-tutorial]]. +## Running Selenium tests on IE11 + +You need IEDriverServer.exe to successfully run selenium tests using IE 11. The download is listed on the official Selenium download page: https://www.seleniumhq.org/download/ + +In local-test-environment.xml you need to set the property "webdriver.ie.driver" to point to the downloaded and unzipped IEDriverServer.exe, not the ie.exe! + +With IE 10/11, only the 32Bit version works by the time of writing. + +Some further configuration steps may be required depending on the Windows version: +https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver#required-configuration + +Be aware that Selenium tests tend to fail on IE11 if the browser window does not keep the focus. This means you must not touch the mouse or keyboard in order to get consistent test results. You may lock your machine while the tests run but ensure that standby mode isn't triggered after a short period. + +In case you see test cases fail with a java.net.BindException with message "Address already in use: connect" you can tune Windows' TCP settings via registry (as admin). In "Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters" set the following values: + +* "MaxUserPort" to decimal value 65534 (create a DWORD if it does not exist) +* "TcpTimedWaitDelay" to decimal value 30 (create a DWORD if it does not exist) + +You need to reboot the system after changing the values above. + ## Updating Selenium While our build environment is stable regarding to the used browser version, this may not be the case in your development environment, where you have the newest browser version installed probably. The short release cycles of the browsers often bring changes in the implementation, which are incompatible with Selenium. Therefore you have to update the used Selenium version by performing the following steps to be able to run the tests local. diff --git a/wiki/info/landscape/development-environment.md b/wiki/info/landscape/development-environment.md index 33393c125b6..8912e8a0c85 100644 --- a/wiki/info/landscape/development-environment.md +++ b/wiki/info/landscape/development-environment.md @@ -87,8 +87,4 @@ We use the Highcharts library to present graphs to the user. These graphs are us * The GWT Highcharts Wrapper – The source code can be found in our project and it’s slightly modified to match our scenario * The actual Highcharts Library -* The jQuery Library - -To support polar diagrams we use version 3.5.0 of the wrapper. This version is tested with version 2.3.3 of the Highcharts library. We tried using 2.3.5 but that led to problems when resizing the RaceBoard. - -Highcharts uses the jQuery library. We do know that version 1.4.2 does NOT work with the versions of Highcharts mentioned above. Multiple yAxis are not initialized correctly. We know use version 1.5.2 of this library. \ No newline at end of file +* The jQuery Library \ No newline at end of file diff --git a/wiki/projects/cloud-orchestrator.md b/wiki/projects/cloud-orchestrator.md index 0fdf6024670..41a8ef25481 100644 --- a/wiki/projects/cloud-orchestrator.md +++ b/wiki/projects/cloud-orchestrator.md @@ -192,7 +192,7 @@ A user wants to create a new event. But instead of creating it on an existing se The steps are: -- Launch a new instance from a prepared AMI (either the AMI is regularly updated with the latest packages and kernel patches, or a "yum update" needs to be run and then the instance rebooted) +- Launch a new instance from a prepared AMI (either the AMI is regularly updated with the latest packages and kernel patches, or a "yum update" needs to be run and then the instance rebooted). The user provide hints as to the sizing of the instance in terms of CPU and memory. Ideally, these sizing parameters would be given in domain concepts, such as number of tracked competitors expected, or number of competitors in largest leaderboard in the event. - The latest (or a specified) release is installed to */home/sailing/servers/server* - The */home/sailing/servers/server/env.sh* file is adjusted to reflect the server name, MongoDB settings, as well as the ports to be used (for a dedicated server probably the defaults at 8888 for the HTTP server, 14888 for the OSGi console telnet port, and 2010 for the Expedition UDP connector) - The Java process is launched @@ -203,24 +203,99 @@ The steps are: - The Apache *httpd* server is launched - Two target groups *S-ded-<servername>* and *S-ded-<servername>-master* are created, and the new instance is added to both of them - Two new ALB rules for *<servername>.sapsailing.com* and *<servername>-master.sapsailing.com* are created, forwarding their requests to the respective target group from the previous step +- If requested, a remote server reference is added to www.sapsailing.com that points to the public URL of the replication cluster -asdf +Monitoring for the target groups shall be established and wired to the auto-scaling procedures which will launch more replicas or terminate replicas as needed. See also [Observe and Automatically Scale a Replication Cluster](#observe-and-automatically-scale-a-replication-cluster). ### Create a New "Club Set-Up" in a Multi-Instance +This is a variant of [Create a New Event on a Dedicated Replication Cluster](#create-a-new-event-on-a-dedicated-replication-cluster). Likewise, the user will provide a technical server name, such as "LYC" for "Lübecker Yacht-Club." A point of contact may be provided, in the form of a name and an e-mail address, telling whom to contact for questions about the instance. + +The difference compared to a dedicated replication cluster set-up is that a multi-instance host needs to be identified that has resources available to run the new Java process. This requires enough disk space, as well as enough available fast swap space and a CPU usage that is not saturated. When such a host has been identified, no *yum* activity or anything related to the AMI will be conducted. Otherwise, a new multi-instance host needs to be launched from an AMI, and the same *yum* activities as above apply. + +A port combination for Jetty/Telnet/Expedition UDP is identified based on any already mapped Java processes on that host. The port combination together with the MongoDB and RabbitMQ settings are stored in an *env.sh* file under */home/sailing/servers/<servername>* and the process can be launched as usual. + +The Apache configuration entry needs to be added, pointing to the respective Jetty port, usually with a "*Plain*" or "*Home*" macro because such club servers usually don't serve a single event. As in the scenario above, the Apache *httpd* server process will then need to load the new configuration. + +The load balancer handling varies slightly compared to the dedicated replication cluster set-up. The health checks need to be set to probe the Java instance using HTTP at the registered Jetty port, not the HTTPS set-up through the Apache server because the Apache server on a multi-instance can handle several Java processes. + +Using a separate *-master* URL seems advisable because when migrating the Java process to a larger set-up later, all devices have already been bound to the *-master* URL, and no re-configuration will be required. + ### Migrate from a Multi-Instance to a Dedicated Replication Cluster +When the orchestrator figures that an event is approaching on a Java server hosted in a multi-instance set-up and the event seems too large to be handled successfully by such set-up, migration to a dedicated replication cluster is a good way to handle the load. This migration should ideally happen when there is no write-load applied to the instance. + +As a first step, the *-master* target group is emptied. This will avoid any write requests such as those coming from the Race Manager app or the Sail InSight app reaching a master process that is deprecated and will be stopped anytime soon. Then, the [Create a New Event on a Dedicated Replication Cluster](#create-a-new-event-on-a-dedicated-replication-cluster) scenario is executed, except for the step of creating a new event and new load balancer rules and target groups. Just by configuring the MongoDB database name, all existing data held so far by the Java process hosted in the multi-instance set-up will be loaded into a dedicated master instance. + +The new dedicated master server can be added to the public-facing target group for the server name as well as to the *-master* target group. The health checks of both, public and *-master* target group need to be changed to the default health check rules for dedicated replication clusters (using the default HTTPS port). When healthy, the old master server will be removed from the public-facing target group. The Java process on the multi-instance host can be shut down. Its resources such as the directory on the multi-host and the port "reservations" that this entailed can be freed. The Apache httpd configuration entry on the multi-instance host shall be removed, and the Apache configuration shall be reloaded. + ### Migrate from a Dedicated Replication Cluster into a Multi-Instance +This is the reverse case to [Migrate from a Multi-Instance to a Dedicated Replication Cluster](#migrate-from-a-multi-instance-to-a-dedicated-replication-cluster). In detail: + +- Start out with removing the existing master server from the *-master* target group +- [Create a New "Club Set-Up" in a Multi-Instance](#create-a-new-club-set-up-in-a-multi-instance), except that the database already exists, and the data will be loaded from that existing database, and the target groups and load balancer rules do not have to be created +- add the new multi-instance Java server to the two target groups +- change the health check rules for both target groups so they use HTTP for the multi-instance Java server's HTTPS port +- when the new Java server process is considered healthy by both target groups, remove the dedicated master and all replicas from all target groups +- terminate all instances of dedicated replication cluster + ### Observe and Automatically Scale a Replication Cluster +According to our experience, in most cases scalability limitations are caused by bandwidth bottlenecks. Less frequently, CPU limitations cause performance degradations when several large leaderboards need to be re-calculated at high frequency. This can also be caused by several dedicated requests for specific, non-live time points. There are different approaches to handling these two different kinds of bottlenecks. The former requires more bandwidth in the form or more instances to be made available to the load balancer. The latter requires more CPU resources and optionally the use of [sharding](#sharding). + +When bandwidth limitations are observed, adding replicas is the simple remedy. The replicas are launched with the exact same release as the master to which they shall belong. Note that replication can also be used for master server processes running on a multi-instance host. The master server may or may not be part of the public-facing target group. If it is low on network resources it may be a good idea to not include it in the public-facing target group. The new replica will be added to the public-facing target group. A solid health check will avoid that it receives any application domain-oriented requests unless the replication process has completed the initial load phase successfully. + +When a master server process is CPU bound, this will usually be because of abundant leaderboard (re-)calculation requests hitting that server. The quickest remedy is to add replicas to the public-facing target group and to remove the master server from that group. Those replicas can have more CPU resources than the master. Their memory should not be less than that of the master. + +If replicas are bound by their CPU power, simply adding more replicas will usually not help. Instead, [sharding](#sharding) may provide a solution (see also [Configure Sharding in a Replication Cluster](#configure-sharding-in-a-replication-cluster)). It allows spreading leaderboard (re-)calculation load across several target groups, avoiding a situation where all replicas have to (re-)calculate all leaderboards all the time. Target groups will then receive only a subset of the leaderboard (re-)calculation requests, based on the leaderboard names. + +When the CPU and/or network load drop under a given threshold for a given period of time, replicas may be removed from the public-facing target group and can then be terminated, once the *draining* status is over. During termination the replica will automatically save its logs. + ### Archiving +When an event is over, a dedicated replication cluster is no longer required, and the scaling requirements change from high-CPU, high ingestion rates to read-only with decreasing, rather sporadic access. The archive server(s) are geared towards this load profile. Furthermore, with our current data mining architecture, archived races are amenable to holistic analysis. + +The archive server, as described in [Archive Servers](#archive-servers), comes as a production and a fail-over instance. Archiving is done using the Master Data Import (MDI) on the production archive server, using the dedicated replication cluster's master as source. An API shall be provided that lets the importing server discover the restore records needed to load the races. The importing archive server shall then restore the tracked races, before a comparison with the exporting source server can be carried out. + +After the comparison was successful, the remote server reference from the archive server to the dedicated replication cluster can be removed, avoiding event duplication on the archive server. An Apache macro for the event imported has to be added to the central web server. Then, the ALB rules for the *-master* and public-facing sub-domain of the dedicated replication cluster can be removed, the instances terminated and the target groups deleted. The default "catch-all" rule will delegate requests for the event to the archive server where the Apache rule re-writes it accordingly. + ### Archive Server Upgrade and Switch +Upon administrator request a release upgrade can be performed for the archive server. Based on the principles discussed in [Archive Servers](#archive-servers), the fail-over server will receive the new release and will have its Java process re-started. Restoring the many races can take several hours. When done, a comparison with the production archive server is carried out. Only when no differences are found and a few spot checks show reasonable results the switch is performed. + +Today, these spot checks are done "manually" by looking at a few sample races and a few leaderboards. These steps will need automation. + +The switch is currently performed by adjusting the rule in *sapsailing.com:/etc/httpd/conf.d/000-macros.conf* that tells the internal IP address of the production archive server, followed by a *service httpd reload* command. + ### Automatic Archive Server Fail-Over +When the production archive server becomes "unhealthy" a fast switch to the fail-over archive server is required. The reasons for an unhealthy production archive server may vary. Recently, for example, we experienced an unexpected AWS-enforced and not announced reboot of the EC2 instance. Other cases may involve regressions in a release to which the archive server was upgraded. + +We may use the ALB architecture and separate web servers for the two archive servers, sharing their configuration rules, to implement such an automatic fail-over. With the ALB rules implementing a precedence order, the default rule could forward to the target group containing the web server for the fail-over archive server, whereas a last-but-one rule would catch **.sapsailing.com* and would forward to the target group containing the production archive server's web server. This way, when the production archive server becomes unhealthy, the default rule will apply and will forward events to the fail-over archive server. + +A severe alert shall be triggered when the production archive server becomes unavailable. + ### Configure Sharding in a Replication Cluster +Sharding works by the GWT RPC requests targeting a specific leaderboard being identified by a specific URL path suffix that mentions the leaderboard to which the request is specific. This way, an ALB rule can be established that matches the specific leaderboard suffix and forwards to a dedicated target group. A replication cluster then will have more than the two typical target groups (*-master* and public-facing). The rule forwarding to the public-facing target group will act as the default after all leaderboard-specific routing rules. + +The decision which and how many target groups to create shall be made based on monitoring the leaderboard re-calculation times. If the times exceed a certain threshold, and the general decision to use sharding is made by the orchestrator, the orchestrator should start by creating a target group for the leaderboard that is most expensive to calculate, considering the re-calculation frequency over some current time range such as a few minutes, as well as the duration required per re-calculation. + +Depending on the number of replicas already available, the orchestrator may choose to partition the existing replica set such that after a target group and ALB rule for a specific leaderboard has been established, the replicas added to that target group will be removed from the public-facing target group. The problem with this set-up could be that then those replicas cannot serve as default target in case other replicas become unhealthy. + +Better would be a complete partitioning based on all the leaderboard requests observed over a recent time range. The public-facing target group then would only serve as a default rule for requests not specific to any leaderboard, and in case target groups for specific leaderboards have no healthy targets anymore. + ### Amazon Machine Image (AMI) Upgrades +The Linux installation that our images are based upon receives regular updates for all sorts of packages. Most package updates can be obtained after an instance was booted, simply by running the command *yum update*. However, kernel updates require the instance to be re-booted and this is not something we would like to have to do each time an instance needs to be started. + +Instead, the AMI used to launch such instances should regularly and automatically be maintained, and there shall be a test procedure in place for the updated AMI before it is made the new default. A few older revisions may be kept as fallback, in case the tests don't catch a problem with an upgrade. + +Part of the upgrade process needs to be an adjustable, parameterizable boot-up script that can understand whether a re-boot is performed as part of an image upgrade. In this case, certain actions need to be skipped, such as launching the Apache server or the Java process or patching any files based on the user details, assuming the instance were to become part of an ALB's target group. + +### Add Disk Space to MongoDB + +When monitoring shows that the disk volumes holding the MongoDB contents crosses a certain threshold (such as 90%) then more disk space needs to be provided automatically. [https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modify-volume.html](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-modify-volume.html) explains the process with AWS and Linux tools. + +Currently, the instance used to run the MongoDB processes is of type m1.large which is an "old" instance type that does not support in-place volume size changes without detaching the volume. Therefore, as a first step towards this goal the MongoDB/RabbitMQ instance should be migrated to a new instance type with similar resources, such as *m4.large*. Then, growing the file-system "in-flight" should not be a problem according to the documentation... \ No newline at end of file