[VIVO-1249] Add capability maps for research areas (#35)

* Initial capability map implementation

* Add autocomplete

* Make headers update the info panel

* Ajax retrieval of person details - reduce memory requirements and startup time

* Remove old referer info

* Handle missing labels.

* Fix refresh button; add menu entry for new installations
This commit is contained in:
grahamtriggs 2016-06-20 17:01:51 +01:00 committed by GitHub
parent 1443ff6685
commit ee7b974ac5
21 changed files with 3126 additions and 36 deletions

View file

@ -9,6 +9,8 @@
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean id="capability_map"
class="edu.cornell.mannlib.vitro.webapp.visualization.capabilitymap.CapabilityMapRequestHandler" />
<bean id="person_pub_count"
class="edu.cornell.mannlib.vitro.webapp.visualization.personpubcount.PersonPublicationCountRequestHandler" />
@ -47,6 +49,10 @@
class="edu.cornell.mannlib.vitro.webapp.controller.visualization.VisualizationInjector">
<property name="visualizations">
<map>
<entry key="capabilitymap">
<ref bean="capability_map"></ref>
</entry>
<entry key="person_pub_count">
<ref bean="person_pub_count"></ref>
</entry>

View file

@ -0,0 +1,355 @@
body {
background-position-y:60px;
}
.text {
margin: 7px;
}
.alert {
color: red;
font-size: large;
}
#queryform button, #queryform input[type=submit] {
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
-o-border-radius: 4px;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
-o-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-webkit-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-moz-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
-o-text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.3);
background-color: #8BAB2E;
// background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#446791), to(#164682));
// background-image: -moz-linear-gradient(top, #446791, #164682);
width: auto;
padding: 9px 18px 8px;
// margin: 10px 10px 0 0;
color: #fff;
border: 0;
vertical-align: middle
}
#content {
width: 1200px;
max-width: 1200px;
}
/* Contains all the graphics. */
#container {
/*width: 1200px;*/width:940px;
height: auto;
/* margin:0 auto; */
/*margin-left:-130px;*/
position:relative;
/*box-shadow:0px 0px 20px -6px #000000;*/
margin-top:15px;
}
#left-container,
#right-container,
#center-container {
height:600px;
position:absolute;
top:0;
}
ul#log_printout {
display: block;
height: 330px;
overflow: scroll
}
#left-container, #right-container {
width: 300px;
color:#686c70;
text-align: left;
/*overflow: auto;*/
background-color:#fff;
background-repeat:no-repeat;
/*border-bottom:1px solid #ddd;*/
}
.result_body {
overflow:auto;
height:570px;
}
#left-container {
left:0;
}
#right-container {
right:0;
border-left:1px solid #ddd;
}
#right-container h4{
text-indent:8px;
}
#center-container {
width: 900px;
background-color:#FFFFFF;
color:#ccc;
}
.result_section {
/* width: 100%;*/
font-size: 12px;
list-style:none;
margin:7px;
}
#log {
position: absolute;
top: 10px;
right:10px;
font-size:1.0em;
}
#infovis {
width:100%;
height:600px;
/*margin:auto;*/
overflow:hidden;
}
span.close {
color:#FF5555;
cursor:pointer;
font-weight:bold;
margin-left:3px;
}
span.name {
cursor: pointer;
}
/*TOOLTIPS*/
.tip {
color: #111;
width: 139px;
background-color: white;
border:1px solid #ccc;
-moz-box-shadow:#555 2px 2px 8px;
-webkit-box-shadow:#555 2px 2px 8px;
-o-box-shadow:#555 2px 2px 8px;
box-shadow:#555 2px 2px 8px;
opacity:0.9;
filter:alpha(opacity=90);
font-size:10px;
font-family:Verdana, Geneva, Arial, Helvetica, sans-serif;
padding:7px;
text-align: left;
width:auto;
max-width:500px;
}
/* LIST OF EXPERTS */
.field {
font-style: italic;
margin: .4ex 1ex;
}
#graphDetails {
margin-bottom:0px;
margin-top:5px;
}
input[disabled] {
opacity:0.5;
box-shadow:none;
}
#resetButton {
background-color: #933;
background-image: -webkit-gradient(linear,0 0,0 100%,from(#B55),to(#933));
background-image: -moz-linear-gradient(center top , #B55, #933);
}
#helptext {
width:630px;
/*padding:5px;*/
}
#center-container {
display:none;
}
.bar {
height:15px;
display:block;
background-color:#EEE;
border:1px solid #CCC;
float:left;
margin-bottom:1px;
}
.barchart {
line-height:18px;
padding-top:5px;
}
h2 {
font-size:23px;
margin-top:0px;
padding-top:0px;
line-height:1.3em;
margin-bottom:20px;
}
.result_section h2 {
font-size:16px;
padding:10px 0px;
margin:0px;
}
.result_section ul li {
margin-top:3px;
}
li input[type=checkbox] {
vertical-align:middle;
}
.orange-square, .blue-circle {
display:block;
float:left;
height:15px;
width:15px;
margin-right:3px;
}
.orange-square {
background-color:#ffa500;
}
.blue-circle {
background-color:#23A4FF;
border-radius:7px;
}
ul.visible {
margin-bottom:1.3em;
margin-left:20px;
list-style-type:disc;
}
#cutofflabel {
display:inline-block;
width:0px;
overflow:visible;
position:relative;
left:10px;
top:2px;
foont-size: 14px;
}
#queryCutoff {
background:transparent;
text-align:right;
padding-left:40px;
}
#cutofflabel img {
vertical-align:middle;
position:relative;
top:-2px;
}
.progress, .progress div {
height:10px;
margin-bottom:-10px;
}
.progress {
position:absolute;
top:0px;
left:0px;
width:100%;
}
.progress div {
background-color:#9999FF;
width:0%;
white-space:nowrap;
color:#FFF;
font-size:10px;
line-height:10px;
}
hr {
background:#efefef;
}
.tabs {
width: auto;
margin-bottom: 15px;
border: 0;
z-index: 10;
}
.tabs ul.titles li {
background: transparent;
/*padding-right: 1px;*/
line-height: normal;
display:inline;
}
.tabs ul {
padding-left:0px;
}
.tabs ul.titles {
list-style: none;
height:30px;
color: #5e6363;
font-size: 13px;
}
.tabs ul.titles li a {
text-decoration: none;
background-color: #eeeeee;
outline: none;
display:block;
float:left;
// width:100px;
width:150px;
height:30px;
line-height:30px;
text-align:center;
color: #5e6363;
}
.tabs ul.titles li a:hover {
background-color: #dddddd;
color: #002b44;
}
.tabs ul.titles li.activeTab a,
.tabs ul.titles li.activeTab a:hover {
background-color: #fff;
}
.smalllogo {
margin-left:-5px;
vertical-align:middle;
}
.advanced label, input {
display:inline;
}
.advanced input[type=text] {
width:50px;
}
.advanced {
display:none;
}
.person_details {
margin-top:5px;
}
.person_details p {
margin-bottom:0px;
}
.person_details ul {
list-style:disc;
margin-left:20px;
}
.person_details li {
margin:0px;
margin-bottom:3px;
}
.ack {
padding-top:20px;
/*text-align:center;*/
color:#999;
margin:0px 20px;
/*font-size:8pt;*/
}
footer {
margin:0px 20px;
width:auto;
}
footer p.copyright {
margin-left:0px;
padding-left:0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,663 @@
/*!
* jQuery Color Animations v@VERSION
* https://github.com/jquery/jquery-color
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* Date: @DATE
*/
(function( jQuery, undefined ) {
var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
// plusequals test for += 100 -= 100
rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
// a set of RE's that can match strings and generate color tuples.
stringParsers = [{
re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ],
execResult[ 3 ],
execResult[ 4 ]
];
}
}, {
re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
parse: function( execResult ) {
return [
execResult[ 1 ] * 2.55,
execResult[ 2 ] * 2.55,
execResult[ 3 ] * 2.55,
execResult[ 4 ]
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ], 16 )
];
}
}, {
// this regex ignores A-F because it's compared against an already lowercased string
re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
parse: function( execResult ) {
return [
parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
];
}
}, {
re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
space: "hsla",
parse: function( execResult ) {
return [
execResult[ 1 ],
execResult[ 2 ] / 100,
execResult[ 3 ] / 100,
execResult[ 4 ]
];
}
}],
// jQuery.Color( )
color = jQuery.Color = function( color, green, blue, alpha ) {
return new jQuery.Color.fn.parse( color, green, blue, alpha );
},
spaces = {
rgba: {
props: {
red: {
idx: 0,
type: "byte"
},
green: {
idx: 1,
type: "byte"
},
blue: {
idx: 2,
type: "byte"
}
}
},
hsla: {
props: {
hue: {
idx: 0,
type: "degrees"
},
saturation: {
idx: 1,
type: "percent"
},
lightness: {
idx: 2,
type: "percent"
}
}
}
},
propTypes = {
"byte": {
floor: true,
max: 255
},
"percent": {
max: 1
},
"degrees": {
mod: 360,
floor: true
}
},
support = color.support = {},
// element for support tests
supportElem = jQuery( "<p>" )[ 0 ],
// colors = jQuery.Color.names
colors,
// local aliases of functions called often
each = jQuery.each;
// determine rgba support immediately
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
// define cache name and alpha properties
// for rgba and hsla spaces
each( spaces, function( spaceName, space ) {
space.cache = "_" + spaceName;
space.props.alpha = {
idx: 3,
type: "percent",
def: 1
};
});
function clamp( value, prop, allowEmpty ) {
var type = propTypes[ prop.type ] || {};
if ( value == null ) {
return (allowEmpty || !prop.def) ? null : prop.def;
}
// ~~ is an short way of doing floor for positive numbers
value = type.floor ? ~~value : parseFloat( value );
// IE will pass in empty strings as value for alpha,
// which will hit this case
if ( isNaN( value ) ) {
return prop.def;
}
if ( type.mod ) {
// we add mod before modding to make sure that negatives values
// get converted properly: -10 -> 350
return (value + type.mod) % type.mod;
}
// for now all property types without mod have min and max
return 0 > value ? 0 : type.max < value ? type.max : value;
}
function stringParse( string ) {
var inst = color(),
rgba = inst._rgba = [];
string = string.toLowerCase();
each( stringParsers, function( i, parser ) {
var parsed,
match = parser.re.exec( string ),
values = match && parser.parse( match ),
spaceName = parser.space || "rgba";
if ( values ) {
parsed = inst[ spaceName ]( values );
// if this was an rgba parse the assignment might happen twice
// oh well....
inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
rgba = inst._rgba = parsed._rgba;
// exit each( stringParsers ) here because we matched
return false;
}
});
// Found a stringParser that handled it
if ( rgba.length ) {
// if this came from a parsed string, force "transparent" when alpha is 0
// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
if ( rgba.join() === "0,0,0,0" ) {
jQuery.extend( rgba, colors.transparent );
}
return inst;
}
// named colors
return colors[ string ];
}
color.fn = jQuery.extend( color.prototype, {
parse: function( red, green, blue, alpha ) {
if ( red === undefined ) {
this._rgba = [ null, null, null, null ];
return this;
}
if ( red.jquery || red.nodeType ) {
red = jQuery( red ).css( green );
green = undefined;
}
var inst = this,
type = jQuery.type( red ),
rgba = this._rgba = [];
// more than 1 argument specified - assume ( red, green, blue, alpha )
if ( green !== undefined ) {
red = [ red, green, blue, alpha ];
type = "array";
}
if ( type === "string" ) {
return this.parse( stringParse( red ) || colors._default );
}
if ( type === "array" ) {
each( spaces.rgba.props, function( key, prop ) {
rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
});
return this;
}
if ( type === "object" ) {
if ( red instanceof color ) {
each( spaces, function( spaceName, space ) {
if ( red[ space.cache ] ) {
inst[ space.cache ] = red[ space.cache ].slice();
}
});
} else {
each( spaces, function( spaceName, space ) {
var cache = space.cache;
each( space.props, function( key, prop ) {
// if the cache doesn't exist, and we know how to convert
if ( !inst[ cache ] && space.to ) {
// if the value was null, we don't need to copy it
// if the key was alpha, we don't need to copy it either
if ( key === "alpha" || red[ key ] == null ) {
return;
}
inst[ cache ] = space.to( inst._rgba );
}
// this is the only case where we allow nulls for ALL properties.
// call clamp with alwaysAllowEmpty
inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
});
// everything defined but alpha?
if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
// use the default of 1
inst[ cache ][ 3 ] = 1;
if ( space.from ) {
inst._rgba = space.from( inst[ cache ] );
}
}
});
}
return this;
}
},
is: function( compare ) {
var is = color( compare ),
same = true,
inst = this;
each( spaces, function( _, space ) {
var localCache,
isCache = is[ space.cache ];
if (isCache) {
localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
each( space.props, function( _, prop ) {
if ( isCache[ prop.idx ] != null ) {
same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
return same;
}
});
}
return same;
});
return same;
},
_space: function() {
var used = [],
inst = this;
each( spaces, function( spaceName, space ) {
if ( inst[ space.cache ] ) {
used.push( spaceName );
}
});
return used.pop();
},
transition: function( other, distance ) {
var end = color( other ),
spaceName = end._space(),
space = spaces[ spaceName ],
startColor = this.alpha() === 0 ? color( "transparent" ) : this,
start = startColor[ space.cache ] || space.to( startColor._rgba ),
result = start.slice();
end = end[ space.cache ];
each( space.props, function( key, prop ) {
var index = prop.idx,
startValue = start[ index ],
endValue = end[ index ],
type = propTypes[ prop.type ] || {};
// if null, don't override start value
if ( endValue === null ) {
return;
}
// if null - use end
if ( startValue === null ) {
result[ index ] = endValue;
} else {
if ( type.mod ) {
if ( endValue - startValue > type.mod / 2 ) {
startValue += type.mod;
} else if ( startValue - endValue > type.mod / 2 ) {
startValue -= type.mod;
}
}
result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
}
});
return this[ spaceName ]( result );
},
blend: function( opaque ) {
// if we are already opaque - return ourself
if ( this._rgba[ 3 ] === 1 ) {
return this;
}
var rgb = this._rgba.slice(),
a = rgb.pop(),
blend = color( opaque )._rgba;
return color( jQuery.map( rgb, function( v, i ) {
return ( 1 - a ) * blend[ i ] + a * v;
}));
},
toRgbaString: function() {
var prefix = "rgba(",
rgba = jQuery.map( this._rgba, function( v, i ) {
return v == null ? ( i > 2 ? 1 : 0 ) : v;
});
if ( rgba[ 3 ] === 1 ) {
rgba.pop();
prefix = "rgb(";
}
return prefix + rgba.join() + ")";
},
toHslaString: function() {
var prefix = "hsla(",
hsla = jQuery.map( this.hsla(), function( v, i ) {
if ( v == null ) {
v = i > 2 ? 1 : 0;
}
// catch 1 and 2
if ( i && i < 3 ) {
v = Math.round( v * 100 ) + "%";
}
return v;
});
if ( hsla[ 3 ] === 1 ) {
hsla.pop();
prefix = "hsl(";
}
return prefix + hsla.join() + ")";
},
toHexString: function( includeAlpha ) {
var rgba = this._rgba.slice(),
alpha = rgba.pop();
if ( includeAlpha ) {
rgba.push( ~~( alpha * 255 ) );
}
return "#" + jQuery.map( rgba, function( v ) {
// default to 0 when nulls exist
v = ( v || 0 ).toString( 16 );
return v.length === 1 ? "0" + v : v;
}).join("");
},
toString: function() {
return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
}
});
color.fn.parse.prototype = color.fn;
// hsla conversions adapted from:
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
function hue2rgb( p, q, h ) {
h = ( h + 1 ) % 1;
if ( h * 6 < 1 ) {
return p + (q - p) * h * 6;
}
if ( h * 2 < 1) {
return q;
}
if ( h * 3 < 2 ) {
return p + (q - p) * ((2/3) - h) * 6;
}
return p;
}
spaces.hsla.to = function ( rgba ) {
if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
return [ null, null, null, rgba[ 3 ] ];
}
var r = rgba[ 0 ] / 255,
g = rgba[ 1 ] / 255,
b = rgba[ 2 ] / 255,
a = rgba[ 3 ],
max = Math.max( r, g, b ),
min = Math.min( r, g, b ),
diff = max - min,
add = max + min,
l = add * 0.5,
h, s;
if ( min === max ) {
h = 0;
} else if ( r === max ) {
h = ( 60 * ( g - b ) / diff ) + 360;
} else if ( g === max ) {
h = ( 60 * ( b - r ) / diff ) + 120;
} else {
h = ( 60 * ( r - g ) / diff ) + 240;
}
// chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
if ( diff === 0 ) {
s = 0;
} else if ( l <= 0.5 ) {
s = diff / add;
} else {
s = diff / ( 2 - add );
}
return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
};
spaces.hsla.from = function ( hsla ) {
if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
return [ null, null, null, hsla[ 3 ] ];
}
var h = hsla[ 0 ] / 360,
s = hsla[ 1 ],
l = hsla[ 2 ],
a = hsla[ 3 ],
q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
p = 2 * l - q;
return [
Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
Math.round( hue2rgb( p, q, h ) * 255 ),
Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
a
];
};
each( spaces, function( spaceName, space ) {
var props = space.props,
cache = space.cache,
to = space.to,
from = space.from;
// makes rgba() and hsla()
color.fn[ spaceName ] = function( value ) {
// generate a cache for this space if it doesn't exist
if ( to && !this[ cache ] ) {
this[ cache ] = to( this._rgba );
}
if ( value === undefined ) {
return this[ cache ].slice();
}
var ret,
type = jQuery.type( value ),
arr = ( type === "array" || type === "object" ) ? value : arguments,
local = this[ cache ].slice();
each( props, function( key, prop ) {
var val = arr[ type === "object" ? key : prop.idx ];
if ( val == null ) {
val = local[ prop.idx ];
}
local[ prop.idx ] = clamp( val, prop );
});
if ( from ) {
ret = color( from( local ) );
ret[ cache ] = local;
return ret;
} else {
return color( local );
}
};
// makes red() green() blue() alpha() hue() saturation() lightness()
each( props, function( key, prop ) {
// alpha is included in more than one space
if ( color.fn[ key ] ) {
return;
}
color.fn[ key ] = function( value ) {
var vtype = jQuery.type( value ),
fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
local = this[ fn ](),
cur = local[ prop.idx ],
match;
if ( vtype === "undefined" ) {
return cur;
}
if ( vtype === "function" ) {
value = value.call( this, cur );
vtype = jQuery.type( value );
}
if ( value == null && prop.empty ) {
return this;
}
if ( vtype === "string" ) {
match = rplusequals.exec( value );
if ( match ) {
value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
}
}
local[ prop.idx ] = value;
return this[ fn ]( local );
};
});
});
// add cssHook and .fx.step function for each named hook.
// accept a space separated string of properties
color.hook = function( hook ) {
var hooks = hook.split( " " );
each( hooks, function( i, hook ) {
jQuery.cssHooks[ hook ] = {
set: function( elem, value ) {
var parsed, curElem,
backgroundColor = "";
if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
value = color( parsed || value );
if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
curElem = hook === "backgroundColor" ? elem.parentNode : elem;
while (
(backgroundColor === "" || backgroundColor === "transparent") &&
curElem && curElem.style
) {
try {
backgroundColor = jQuery.css( curElem, "backgroundColor" );
curElem = curElem.parentNode;
} catch ( e ) {
}
}
value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
backgroundColor :
"_default" );
}
value = value.toRgbaString();
}
try {
elem.style[ hook ] = value;
} catch( e ) {
// wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
}
}
};
jQuery.fx.step[ hook ] = function( fx ) {
if ( !fx.colorInit ) {
fx.start = color( fx.elem, hook );
fx.end = color( fx.end );
fx.colorInit = true;
}
jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
};
});
};
color.hook( stepHooks );
jQuery.cssHooks.borderColor = {
expand: function( value ) {
var expanded = {};
each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
expanded[ "border" + part + "Color" ] = value;
});
return expanded;
}
};
// Basic color names only.
// Usage of any of the other color names requires adding yourself or including
// jquery.color.svg-names.js.
colors = jQuery.Color.names = {
// 4.1. Basic color keywords
aqua: "#00ffff",
black: "#000000",
blue: "#0000ff",
fuchsia: "#ff00ff",
gray: "#808080",
green: "#008000",
lime: "#00ff00",
maroon: "#800000",
navy: "#000080",
olive: "#808000",
purple: "#800080",
red: "#ff0000",
silver: "#c0c0c0",
teal: "#008080",
white: "#ffffff",
yellow: "#ffff00",
// 4.2.3. "transparent" color keyword
transparent: [ null, null, null, 0 ],
_default: "#ffffff"
};
})( jQuery );

View file

@ -0,0 +1,82 @@
// jsr_class.js
//
// JSONscriptRequest -- a simple class for making HTTP requests
// using dynamically generated script tags and JSON
//
// Author: Jason Levitt
// Date: December 7th, 2005
//
// A SECURITY WARNING FROM DOUGLAS CROCKFORD:
// "The dynamic <script> tag hack suffers from a problem. It allows a page
// to access data from any server in the web, which is really useful.
// Unfortunately, the data is returned in the form of a script. That script
// can deliver the data, but it runs with the same authority as scripts on
// the base page, so it is able to steal cookies or misuse the authorization
// of the user with the server. A rogue script can do destructive things to
// the relationship between the user and the base server."
//
// So, be extremely cautious in your use of this script.
//
//
// Sample Usage:
//
// <script type="text/javascript" src="jsr_class.js"></script>
//
// function callbackfunc(jsonData) {
// alert('Latitude = ' + jsonData.ResultSet.Result[0].Latitude +
// ' Longitude = ' + jsonData.ResultSet.Result[0].Longitude);
// aObj.removeScriptTag();
// }
//
// request = 'http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&
// output=json&callback=callbackfunc&location=78704';
// aObj = new JSONscriptRequest(request);
// aObj.buildScriptTag();
// aObj.addScriptTag();
//
//
// Constructor -- pass a REST request URL to the constructor
//
function JSONscriptRequest(fullUrl) {
// REST request path
this.fullUrl = fullUrl;
// Keep IE from caching requests
this.noCacheIE = '&noCacheIE=' + (new Date()).getTime();
// Get the DOM location to put the script tag
this.headLoc = document.getElementsByTagName("head").item(0);
// Generate a unique script tag id
this.scriptId = 'JscriptId' + JSONscriptRequest.scriptCounter++;
}
// Static script ID counter
JSONscriptRequest.scriptCounter = 1;
// buildScriptTag method
//
JSONscriptRequest.prototype.buildScriptTag = function () {
// Create the script tag
this.scriptObj = document.createElement("script");
// Add script object attributes
this.scriptObj.setAttribute("type", "text/javascript");
this.scriptObj.setAttribute("charset", "utf-8");
this.scriptObj.setAttribute("src", this.fullUrl + this.noCacheIE);
this.scriptObj.setAttribute("id", this.scriptId);
};
// removeScriptTag method
//
JSONscriptRequest.prototype.removeScriptTag = function () {
// Destroy the script tag
this.headLoc.removeChild(this.scriptObj);
};
// addScriptTag method
//
JSONscriptRequest.prototype.addScriptTag = function () {
// Create the script tag
this.headLoc.appendChild(this.scriptObj);
};

View file

@ -0,0 +1,167 @@
${scripts.add(
'<script type="text/javascript" src="${urls.base}/js/d3.min.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/jquery.color.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/jsr_class.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/visualization/capabilitymap/graph_new.js"></script>'
)}
${stylesheets.add(
'<link rel="stylesheet" href="${urls.base}/js/jquery-ui/css/smoothness/jquery-ui-1.8.9.custom.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/templates/freemarker/edit/forms/css/autocomplete.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/personlevel/page.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/visualization.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/capabilitymap/graph.css" />'
)}
<script language="JavaScript" type="text/javascript">
var contextPath = "${urls.base}";
$(document).ready(function() {
var loadedConcepts = $.ajax({
url: contextPath + "/visualizationAjax?vis=capabilitymap&data=concepts",
type: "GET",
async: false,
success: function(result) {
return result;
}
});
var conceptArray = $.parseJSON(loadedConcepts.responseText);
$("#query").autocomplete({
source: conceptArray
});
});
</script>
<div class="main" id="main-content" role="main">
<div class="col-8">
<h2>Capability Map</h2>
<p>Build a &lsquo;first pass&rsquo; capability map by typing in a<!-- set of--> search term<!--s--> that could be said to represent a broad research capability.</p>
</div>
<div id="queryform">
<p>
<span>
<input name="query" id="query" size="34" value="" onfocus="" accesskey="q" onblur="" type="text" onkeydown="queryKeyDown(event);">
<label id="cutofflabel" for="queryCutoff">Cutoff:</label>
<input id="queryCutoff" name="queryCutoff" type="text" title="Cutoff" size="4" value="10">
<input type="submit" value="Search" id="add" type="button" onclick="addKwd();">
<input value="Search and Expand" type="submit" id="sExpand" onclick="expandLastQuery = 1; addKwd();">
<input value="Reset" id="resetButton" type="submit" onclick="reset()" disabled>
<!-- a style="display:inline-block; float:right; line-height:32px; height:32px; cursor:pointer" onclick="showhideadvanced(this)">Show advanced</a -->
</span>
</p>
</div>
<hr style="clear:both;">
<div id="container">
<div id="helptext">
<p>
Welcome to the Capability Mapping tool.
This tool visualises how researchers relate to other
researchers via search terms.
</p>
<h3>Getting Started</h3>
<p>
Enter a research area into the search field above and press Search.
The resulting diagram displays the search term, rendered in orange,
connected to the blue group of researchers that are active in that area.
Enter another search term to see how researchers from both searches relate.
Keep adding search terms to build a capability map.
</p>
<p>
Tip: you can expand a broad search term into smaller concepts
by clicking &lsquo;search and expand&rsquo;.
</p>
<h3>Interacting with the visualisation</h3>
<p>
By clicking on any node in the visualisation,
additional information can be viewed in the
Info tab on the right-hand side.
For groups of people, the participants in the group
and their information can be viewed,
and individual researchers can be removed from the graph.
Selecting a search term will display all attached groups.
Under each group full information for each person is retrieved,
and the number of matching grants and publications
for each researcher within the mapped capabilities is shown.
Clicking on a researcher's name will lead to the original search
results.
</p>
<h4>Visual cues</h4>
<p>
To make the visualisation easier to read,
search terms and groups are scaled according
to the number of results returned.
Groups are also given different shades
according to the number of connected search terms.
The darker the shade, the more search terms a group is connected to.
</p>
<h3>Advanced features</h3>
<h4>Changing the cutoff value</h4>
<p>
The amount of researchers retrieved for each search term
for is limited by the cutoff value in the search form
(10 by default).
Increasing this cutoff will increase the likelihood
of an intersection between different search terms.
This will also increase the complexity of the graph,
however, and may make it difficult to identify patterns.
</p>
</div>
<div id="center-container">
<div id="log"></div>
<div id="infovis"></div>
<div class="progress"><div id="progressbar"></div></div>
</div>
<div id="right-container">
<div class="tabs">
<ul class="titles">
<li><a href="#demo">Search terms</a></li>
<li><a href="#logg">Info</a></li>
<!-- li><a href="#extractData">Data</a></li -->
</ul>
<div class="result_body">
<div class="result_section" id="demo">
<h2>Current search terms</h2>
<ul id="log_printout">
<li>This panel displays a list of the search terms currently
on the graph. Search for something to begin.</li>
</ul>
<p style="position:absolute; bottom:10px">
<img src="${urls.base}/images/visualization/capabilitymap/key.png" alt="Key">
</p>
</div>
<div class="result_section" id="logg">
<div id="inner-details">
<p>
This panel displays information about individual
search terms and groups. Click on a group to display
its information.
</p>
</div>
</div>
<!-- div class="result_section" id="extractData">
<h2>Extract Data</h2>
<p>
Import:
<button disabled>SVG</button>
<button onclick="importGraphDetails();">JSON</button>
<br>
Export:
<button onclick="generateGraphSVG();">SVG</button>
<button onclick="download(g.export(), 'json')">JSON</button>
<button onclick="download(g.toDOT(), 'gv')">DOT</button>
<button onclick="generateGraphPersonList();">MRW</button>
</p>
<textarea id="graphDetails" style="width:99%; height:450px; border:1px solid #EEE; padding:0px;"></textarea>
</div -->
</div>
</div>
</div>
</div>
</div>