提交 cf218bbc 编写于 作者: K kohsuke

In lower resolution systems, create smaller test trend chart.



git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@9000 71c3de6d-444a-0410-be80-ed276b4c234a
上级 b3f8f536
pom.iml
target
.svn
pom.ipr
pom.iml
pom.iws
build
ref
*.iws
work
target
war
target
.project
.classpath
hudson.iml
hudson-core.iml
*.iws
.project
hudson.ipr
build
war
.settings
ref
.classpath
hudson-core.iml
target
.svn
......@@ -26,6 +26,7 @@ import hudson.tasks.Publisher;
import hudson.security.SecurityRealm;
import hudson.security.AuthorizationStrategy;
import hudson.security.Permission;
import hudson.util.Area;
import org.apache.commons.jexl.parser.ASTSizeFunction;
import org.apache.commons.jexl.util.Introspector;
import org.apache.commons.jelly.JellyContext;
......@@ -61,6 +62,7 @@ import java.util.TreeMap;
import java.util.logging.LogRecord;
import java.util.logging.SimpleFormatter;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
/**
* Utility functions used in views.
......@@ -181,6 +183,17 @@ public class Functions {
return new RunUrl( (Run) f.getObject(), head, base, rest);
}
/**
* If we know the user's screen resolution, return it. Otherwise null.
* @since 1.213
*/
public static Area getScreenResolution() {
Cookie res = Functions.getCookie(Stapler.getCurrentRequest(),"screenResolution");
if(res!=null)
return Area.parse(res.getValue());
return null;
}
/**
* URL decomposed for easier computation of relevant URLs.
*
......
......@@ -10,6 +10,7 @@ import hudson.util.ColorPalette;
import hudson.util.DataSetBuilder;
import hudson.util.ShiftedCategoryAxis;
import hudson.util.StackedAreaRenderer2;
import hudson.util.Area;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
......@@ -164,7 +165,7 @@ public abstract class AbstractTestResultAction<T extends AbstractTestResultActio
if(req.checkIfModified(owner.getTimestamp(),rsp))
return;
ChartUtil.generateGraph(req,rsp,createChart(req,buildDataSet(req)),500,200);
ChartUtil.generateGraph(req,rsp,createChart(req,buildDataSet(req)),calcDefaultSize());
}
/**
......@@ -173,9 +174,23 @@ public abstract class AbstractTestResultAction<T extends AbstractTestResultActio
public void doGraphMap( StaplerRequest req, StaplerResponse rsp) throws IOException {
if(req.checkIfModified(owner.getTimestamp(),rsp))
return;
ChartUtil.generateClickableMap(req,rsp,createChart(req,buildDataSet(req)),500,200);
ChartUtil.generateClickableMap(req,rsp,createChart(req,buildDataSet(req)),calcDefaultSize());
}
/**
* Determines the default size of the trend graph.
*
* This is default because the query parameter can choose arbitrary size.
* If the screen resolution is too low, use a smaller size.
*/
private Area calcDefaultSize() {
Area res = Functions.getScreenResolution();
if(res!=null && res.width<=800)
return new Area(250,100);
else
return new Area(500,200);
}
private CategoryDataset buildDataSet(StaplerRequest req) {
boolean failureOnly = Boolean.valueOf(req.getParameter("failureOnly"));
......
package hudson.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Represents a two dimensional area.
*
* @author Kohsuke Kawaguchi
* @since 1.213
*/
public final class Area {
public final int width;
public final int height;
public Area(int width, int height) {
this.width = width;
this.height = height;
}
/**
* Opposite of {@link #toString()}.
*/
public static Area parse(String s) {
Matcher m = PATTERN.matcher(s);
if(m.matches())
return new Area(Integer.parseInt(m.group(1)),Integer.parseInt(m.group(2)));
return null;
}
public int area() {
return width*height;
}
public String toString() {
return width+"x"+height;
}
private static final Pattern PATTERN = Pattern.compile("(\\d+)x(\\d+)");
}
......@@ -58,6 +58,17 @@ public class ChartUtil {
*/
public static boolean awtProblem = false;
/**
* Generates the graph in PNG format and sends that to the response.
*
* @param defaultSize
* The size of the picture to be generated. These values can be overridden
* by the query paramter 'width' and 'height' in the request.
*/
public static void generateGraph(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, Area defaultSize) throws IOException {
generateGraph(req,rsp,chart,defaultSize.width, defaultSize.height);
}
/**
* Generates the graph in PNG format and sends that to the response.
*
......@@ -83,6 +94,13 @@ public class ChartUtil {
}
}
/**
* Generates the clickable map info and sends that to the response.
*/
public static void generateClickableMap(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, Area defaultSize) throws IOException {
generateClickableMap(req,rsp,chart,defaultSize.width,defaultSize.height);
}
/**
* Generates the clickable map info and sends that to the response.
*/
......
......@@ -2,7 +2,7 @@
<st:header name="X-Hudson" value="${servletContext.getAttribute('version')}" />
<j:new var="h" className="hudson.Functions" /><!-- instead of JSP functions -->
<l:layout title="${h.ifThenElse(app==it,'%Dashboard',it.viewName)}">
<st:include page="sidepanel.jelly" />
<st:include page="sidepanel.jelly" />
<l:main-panel>
<div id="view-message">
<t:editableDescription adminOnly="true"/>
......@@ -28,5 +28,12 @@
</j:otherwise>
</j:choose>
</l:main-panel>
<l:header>
<!-- for screen resolution detection -->
<l:yui module="cookie" suffix="-beta" />
<script>
YAHOO.util.Cookie.set("screenResolution", screen.width+"x"+screen.height);
</script>
</l:header>
</l:layout>
</st:compress>
/*
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.5.1
*/
/**
* Utilities for cookie management
* @namespace YAHOO.util
* @module cookie
* @beta
*/
YAHOO.namespace("util");
/**
* Cookie utility.
* @class Cookie
* @static
*/
YAHOO.util.Cookie = {
//-------------------------------------------------------------------------
// Private Methods
//-------------------------------------------------------------------------
/**
* Creates a cookie string that can be assigned into document.cookie.
* @param {String} name The name of the cookie.
* @param {String} value The value of the cookie.
* @param {encodeValue} encodeValue True to encode the value, false to leave as-is.
* @param {Object} options (Optional) Options for the cookie.
* @return {String} The formatted cookie string.
* @method _createCookieString
* @private
* @static
*/
_createCookieString : function (name /*:String*/, value /*:Variant*/, encodeValue /*:Boolean*/, options /*:Object*/) /*:String*/ {
//shortcut
var lang = YAHOO.lang;
var text /*:String*/ = encodeURIComponent(name) + "=" + (encodeValue ? encodeURIComponent(value) : value);
if (lang.isObject(options)){
//expiration date
if (options.expires instanceof Date){
text += "; expires=" + options.expires.toGMTString();
}
//path
if (lang.isString(options.path) && options.path != ""){
text += "; path=" + options.path;
}
//domain
if (lang.isString(options.domain) && options.domain != ""){
text += "; domain=" + options.domain;
}
//secure
if (options.secure === true){
text += "; secure";
}
}
return text;
},
/**
* Formats a cookie value for an object containing multiple values.
* @param {Object} hash An object of key-value pairs to create a string for.
* @return {String} A string suitable for use as a cookie value.
* @method _createCookieHash
* @private
* @static
*/
_createCookieHashString : function (hash /*:Object*/) /*:String*/ {
//shortcuts
var lang = YAHOO.lang;
if (!lang.isObject(hash)){
throw new TypeError("Cookie._createCookieHashString(): Argument must be an object.");
}
var text /*:Array*/ = new Array();
for (var key in hash){
if (lang.hasOwnProperty(hash, key) && !lang.isFunction(hash[key]) && !lang.isUndefined(hash[key])){
text.push(encodeURIComponent(key) + "=" + encodeURIComponent(String(hash[key])));
}
}
return text.join("&");
},
/**
* Parses a cookie hash string into an object.
* @param {String} text The cookie hash string to parse. The string should already be URL-decoded.
* @return {Object} An object containing entries for each cookie value.
* @method _parseCookieHash
* @private
* @static
*/
_parseCookieHash : function (text /*:String*/) /*:Object*/ {
var hashParts /*:Array*/ = text.split("&");
var hashPart /*:Array*/ = null;
var hash /*:Object*/ = new Object();
for (var i=0, len=hashParts.length; i < len; i++){
hashPart = hashParts[i].split("=");
hash[decodeURIComponent(hashPart[0])] = decodeURIComponent(hashPart[1]);
}
return hash;
},
/**
* Parses a cookie string into an object representing all accessible cookies.
* @param {String} text The cookie string to parse.
* @param {Boolean} decode (Optional) Indicates if the cookie values should be decoded or not. Default is true.
* @return {Object} An object containing entries for each accessible cookie.
* @method _parseCookieString
* @private
* @static
*/
_parseCookieString : function (text /*:String*/, decode /*:Boolean*/) /*:Object*/ {
var cookies /*:Object*/ = new Object();
if (YAHOO.lang.isString(text) && text.length > 0) {
var decodeValue = (decode === false ? function(s){return s;} : decodeURIComponent);
if (/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(text)){
var cookieParts /*:Array*/ = text.split(/;\s/g);
var cookieName /*:String*/ = null;
var cookieValue /*:String*/ = null;
var cookieNameValue /*:Array*/ = null;
for (var i=0, len=cookieParts.length; i < len; i++){
//check for normally-formatted cookie (name-value)
cookieNameValue = cookieParts[i].match(/([^=]+)=/i);
if (cookieNameValue instanceof Array){
cookieName = decodeURIComponent(cookieNameValue[1]);
cookieValue = decodeValue(cookieParts[i].substring(cookieName.length+1));
} else {
//means the cookie does not have an "=", so treat it as a boolean flag
cookieName = decodeURIComponent(cookieParts[i]);
cookieValue = cookieName;
}
cookies[cookieName] = cookieValue;
}
}
}
return cookies;
},
//-------------------------------------------------------------------------
// Public Methods
//-------------------------------------------------------------------------
/**
* Returns the cookie value for the given name.
* @param {String} name The name of the cookie to retrieve.
* @param {Function} converter (Optional) A function to run on the value before returning
* it. The function is not used if the cookie doesn't exist.
* @return {Variant} If no converter is specified, returns a string or null if
* the cookie doesn't exist. If the converter is specified, returns the value
* returned from the converter or null if the cookie doesn't exist.
* @method get
* @static
*/
get : function (name /*:String*/, converter /*:Function*/) /*:Variant*/{
var lang = YAHOO.lang;
var cookies /*:Object*/ = this._parseCookieString(document.cookie);
if (!lang.isString(name) || name === ""){
throw new TypeError("Cookie.get(): Cookie name must be a non-empty string.");
}
if (lang.isUndefined(cookies[name])) {
return null;
}
if (!lang.isFunction(converter)){
return cookies[name];
} else {
return converter(cookies[name]);
}
},
/**
* Returns the value of a subcookie.
* @param {String} name The name of the cookie to retrieve.
* @param {String} subName The name of the subcookie to retrieve.
* @param {Function} converter (Optional) A function to run on the value before returning
* it. The function is not used if the cookie doesn't exist.
* @return {Variant} If the cookie doesn't exist, null is returned. If the subcookie
* doesn't exist, null if also returned. If no converter is specified and the
* subcookie exists, a string is returned. If a converter is specified and the
* subcookie exists, the value returned from the converter is returned.
* @method getSub
* @static
*/
getSub : function (name /*:String*/, subName /*:String*/, converter /*:Function*/) /*:Variant*/ {
var lang = YAHOO.lang;
var hash /*:Variant*/ = this.getSubs(name);
if (hash !== null) {
if (!lang.isString(subName) || subName === ""){
throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string.");
}
if (lang.isUndefined(hash[subName])){
return null;
}
if (!lang.isFunction(converter)){
return hash[subName];
} else {
return converter(hash[subName]);
}
} else {
return null;
}
},
/**
* Returns an object containing name-value pairs stored in the cookie with the given name.
* @param {String} name The name of the cookie to retrieve.
* @return {Object} An object of name-value pairs if the cookie with the given name
* exists, null if it does not.
* @method getHash
* @static
*/
getSubs : function (name /*:String*/) /*:Object*/ {
//check cookie name
if (!YAHOO.lang.isString(name) || name === ""){
throw new TypeError("Cookie.getSubs(): Cookie name must be a non-empty string.");
}
var cookies = this._parseCookieString(document.cookie, false);
if (YAHOO.lang.isString(cookies[name])){
return this._parseCookieHash(cookies[name]);
}
return null;
},
/**
* Removes a cookie from the machine by setting its expiration date to
* sometime in the past.
* @param {String} name The name of the cookie to remove.
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string),
* and secure (true/false). The expires option will be overwritten
* by the method.
* @return {String} The created cookie string.
* @method remove
* @static
*/
remove : function (name /*:String*/, options /*:Object*/) /*:String*/ {
//check cookie name
if (!YAHOO.lang.isString(name) || name === ""){
throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string.");
}
//set options
options = options || {};
options.expires = new Date(0);
//set cookie
return this.set(name, "", options);
},
/**
* Sets a cookie with a given name and value.
* @param {String} name The name of the cookie to set.
* @param {Variant} value The value to set for the cookie.
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string), expires (a Date object),
* and secure (true/false).
* @return {String} The created cookie string.
* @method set
* @static
*/
set : function (name /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
var lang = YAHOO.lang;
if (!lang.isString(name)){
throw new TypeError("Cookie.set(): Cookie name must be a string.");
}
if (lang.isUndefined(value)){
throw new TypeError("Cookie.set(): Value cannot be undefined.");
}
var text /*:String*/ = this._createCookieString(name, value, true, options);
document.cookie = text;
return text;
},
/**
* Sets a sub cookie with a given name to a particular value.
* @param {String} name The name of the cookie to set.
* @param {String} subName The name of the subcookie to set.
* @param {Variant} value The value to set.
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string), expires (a Date object),
* and secure (true/false).
* @return {String} The created cookie string.
* @method setSub
* @static
*/
setSub : function (name /*:String*/, subName /*:String*/, value /*:Variant*/, options /*:Object*/) /*:String*/ {
var lang = YAHOO.lang;
if (!lang.isString(name) || name === ""){
throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string.");
}
if (!lang.isString(subName) || subName === ""){
throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string.");
}
if (lang.isUndefined(value)){
throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined.");
}
var hash /*:Object*/ = this.getSubs(name);
if (!lang.isObject(hash)){
hash = new Object();
}
hash[subName] = value;
return this.setSubs(name, hash, options);
},
/**
* Sets a cookie with a given name to contain a hash of name-value pairs.
* @param {String} name The name of the cookie to set.
* @param {Object} value An object containing name-value pairs.
* @param {Object} options (Optional) An object containing one or more
* cookie options: path (a string), domain (a string), expires (a Date object),
* and secure (true/false).
* @return {String} The created cookie string.
* @method setSubs
* @static
*/
setSubs : function (name /*:String*/, value /*:Object*/, options /*:Object*/) /*:String*/ {
var lang = YAHOO.lang;
if (!lang.isString(name)){
throw new TypeError("Cookie.setSubs(): Cookie name must be a string.");
}
if (!lang.isObject(value)){
throw new TypeError("Cookie.setSubs(): Cookie value must be an object.");
}
var text /*:String*/ = this._createCookieString(name, this._createCookieHashString(value), false, options);
document.cookie = text;
return text;
}
};
YAHOO.register("cookie", YAHOO.util.Cookie, {version: "2.5.1", build: "984"});
/*
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.net/yui/license.txt
version: 2.5.1
*/
YAHOO.namespace("util");YAHOO.util.Cookie={_createCookieString:function(B,D,C,A){var F=YAHOO.lang;var E=encodeURIComponent(B)+"="+(C?encodeURIComponent(D):D);if(F.isObject(A)){if(A.expires instanceof Date){E+="; expires="+A.expires.toGMTString();}if(F.isString(A.path)&&A.path!=""){E+="; path="+A.path;}if(F.isString(A.domain)&&A.domain!=""){E+="; domain="+A.domain;}if(A.secure===true){E+="; secure";}}return E;},_createCookieHashString:function(B){var D=YAHOO.lang;if(!D.isObject(B)){throw new TypeError("Cookie._createCookieHashString(): Argument must be an object.");}var C=new Array();for(var A in B){if(D.hasOwnProperty(B,A)&&!D.isFunction(B[A])&&!D.isUndefined(B[A])){C.push(encodeURIComponent(A)+"="+encodeURIComponent(String(B[A])));}}return C.join("&");},_parseCookieHash:function(E){var D=E.split("&");var F=null;var C=new Object();for(var B=0,A=D.length;B<A;B++){F=D[B].split("=");C[decodeURIComponent(F[0])]=decodeURIComponent(F[1]);}return C;},_parseCookieString:function(I,A){var J=new Object();if(YAHOO.lang.isString(I)&&I.length>0){var B=(A===false?function(K){return K;}:decodeURIComponent);if(/[^=]+=[^=;]?(?:; [^=]+=[^=]?)?/.test(I)){var G=I.split(/;\s/g);var H=null;var C=null;var E=null;for(var D=0,F=G.length;D<F;D++){E=G[D].match(/([^=]+)=/i);if(E instanceof Array){H=decodeURIComponent(E[1]);C=B(G[D].substring(H.length+1));}else{H=decodeURIComponent(G[D]);C=H;}J[H]=C;}}}return J;},get:function(A,B){var D=YAHOO.lang;var C=this._parseCookieString(document.cookie);if(!D.isString(A)||A===""){throw new TypeError("Cookie.get(): Cookie name must be a non-empty string.");}if(D.isUndefined(C[A])){return null;}if(!D.isFunction(B)){return C[A];}else{return B(C[A]);}},getSub:function(A,C,B){var E=YAHOO.lang;var D=this.getSubs(A);if(D!==null){if(!E.isString(C)||C===""){throw new TypeError("Cookie.getSub(): Subcookie name must be a non-empty string.");}if(E.isUndefined(D[C])){return null;}if(!E.isFunction(B)){return D[C];}else{return B(D[C]);}}else{return null;}},getSubs:function(A){if(!YAHOO.lang.isString(A)||A===""){throw new TypeError("Cookie.getSubs(): Cookie name must be a non-empty string.");}var B=this._parseCookieString(document.cookie,false);if(YAHOO.lang.isString(B[A])){return this._parseCookieHash(B[A]);}return null;},remove:function(B,A){if(!YAHOO.lang.isString(B)||B===""){throw new TypeError("Cookie.remove(): Cookie name must be a non-empty string.");}A=A||{};A.expires=new Date(0);return this.set(B,"",A);},set:function(B,C,A){var E=YAHOO.lang;if(!E.isString(B)){throw new TypeError("Cookie.set(): Cookie name must be a string.");}if(E.isUndefined(C)){throw new TypeError("Cookie.set(): Value cannot be undefined.");}var D=this._createCookieString(B,C,true,A);document.cookie=D;return D;},setSub:function(B,D,C,A){var F=YAHOO.lang;if(!F.isString(B)||B===""){throw new TypeError("Cookie.setSub(): Cookie name must be a non-empty string.");}if(!F.isString(D)||D===""){throw new TypeError("Cookie.setSub(): Subcookie name must be a non-empty string.");}if(F.isUndefined(C)){throw new TypeError("Cookie.setSub(): Subcookie value cannot be undefined.");}var E=this.getSubs(B);if(!F.isObject(E)){E=new Object();}E[D]=C;return this.setSubs(B,E,A);},setSubs:function(B,C,A){var E=YAHOO.lang;if(!E.isString(B)){throw new TypeError("Cookie.setSubs(): Cookie name must be a string.");}if(!E.isObject(C)){throw new TypeError("Cookie.setSubs(): Cookie value must be an object.");}var D=this._createCookieString(B,this._createCookieHashString(C),false,A);document.cookie=D;return D;}};YAHOO.register("cookie",YAHOO.util.Cookie,{version:"2.5.1",build:"984"});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册